dynamic-directive
Version:
Dynamic directives for AngularJS
240 lines (205 loc) • 7.52 kB
JavaScript
;
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
(function (angular) {
var uniqueId = 0;
var DEFAULT_PRIORITY = 0;
var DynamicDirective = function DynamicDirective(injectionFunction, name, options) {
_classCallCheck(this, DynamicDirective);
if (!name) {
throw new Error('DynamicInjection: name argument should be a string');
}
this.name = name;
if (injectionFunction === true) {
injectionFunction = function () {
return true;
};
} else if (!injectionFunction || !angular.isFunction(injectionFunction)) {
throw new Error('DynamicInjection: injectionFunction argument should be a function');
}
this.injectionFunction = injectionFunction;
options = options || {};
this.attributes = options.attributes || [];
this.scope = options.scope || undefined;
this.priority = !isNaN(parseInt(options.priority, 10)) ? parseInt(options.priority, 10) : DEFAULT_PRIORITY;
this._id = ++uniqueId;
};
angular.module('op.dynamicDirective', []).provider('dynamicDirectiveService', (function () {
var injections = {};
function _dynamicDirectivesSortByName(a, b, onEquality) {
if (b.name < a.name) {
return -1;
} else if (b.name > a.name) {
return 1;
} else {
return onEquality(a, b);
}
}
function _dynamicDirectivesSortByid(a, b) {
if (b._id < a._id) {
return -1;
} else if (b._id > a._id) {
return 1;
} else {
return 0;
}
}
function _dynamicDirectivesSort(a, b) {
var prio = b.priority - a.priority;
if (prio !== 0) {
return prio;
}
if (b.name < a.name) {
return -1;
} else if (b.name > a.name) {
return 1;
} else {
return _dynamicDirectivesSortByName(a, b, _dynamicDirectivesSortByid);
}
}
function _ensureInjectionsArray(anchorName) {
injections[anchorName] = injections[anchorName] || [];
}
function getInjections(anchorName, scope) {
_ensureInjectionsArray(anchorName);
var ia = injections[anchorName];
return ia.filter(function (da) {
return da.injectionFunction(scope);
});
}
function _addInjection(anchorName, da) {
_ensureInjectionsArray(anchorName);
injections[anchorName].push(da);
}
function _resetInjections(anchorName) {
injections[anchorName] = [];
}
function orderInjections(injections) {
injections.sort(_dynamicDirectivesSort);
return injections;
}
function resetAllInjections() {
injections = {};
}
return {
addInjection: _addInjection,
resetAllInjections: resetAllInjections,
DynamicDirective: DynamicDirective,
$get: ['$rootScope', function ($rootScope) {
return {
DynamicDirective: DynamicDirective,
getInjections: getInjections,
sort: orderInjections,
addInjection: function addInjection(anchorName, da) {
_addInjection(anchorName, da);
$rootScope.$broadcast('dynamicDirectiveInjectionUpdated', anchorName, da);
},
resetInjections: function resetInjections(anchorName) {
_resetInjections(anchorName);
$rootScope.$broadcast('dynamicDirectiveInjectionUpdated', anchorName);
}
};
}]
};
})()).value('DynamicDirective', DynamicDirective).directive('dynamicDirective', ['$compile', 'dynamicDirectiveService', function ($compile, dynamicDirectiveService) {
var DYNAMIC_DIRECTIVE_ID = 'dynamic-directive-id';
function orderDirectives(element, dynamicDirectives) {
var orderedIds = dynamicDirectiveService.sort(dynamicDirectives).map(function (d) {
return d._id;
});
var domIds = element.children('[' + DYNAMIC_DIRECTIVE_ID + ']').map(function (index, e) {
return parseInt(angular.element(e).attr(DYNAMIC_DIRECTIVE_ID), 10);
}).toArray();
// 99% of the time
if (orderedIds.join(',') === domIds.join(',')) {
return;
}
for (var i = 0, len = orderedIds.length - 2; i <= len; i++) {
var current = orderedIds[i],
next = orderedIds[i + 1],
$current = element.children('[' + DYNAMIC_DIRECTIVE_ID + '=' + current + ']');
if ($current.next().attr(DYNAMIC_DIRECTIVE_ID) !== next) {
element.children('[' + DYNAMIC_DIRECTIVE_ID + '=' + next + ']').insertAfter($current);
}
}
}
function hideElement(element) {
element.data('__dd_original_display', element.attr('display'));
element.attr('display', 'none');
}
function showElement(element) {
var originalDisplay = element.data('__dd_original_display');
if (!originalDisplay || originalDisplay === 'none') {
originalDisplay = 'block;';
}
element.attr('display', originalDisplay);
}
function link(scope, element, attrs) {
function appendDirective(dynamicDirective) {
var template = angular.element(buildHtmlFromInjectionData(dynamicDirective));
var newElt = $compile(template)(dynamicDirective.scope || scope);
element.append(newElt);
}
function buildHtmlFromInjectionData(dynamicDirective) {
var attributes = {};
attributes[DYNAMIC_DIRECTIVE_ID] = dynamicDirective._id;
dynamicDirective.attributes.forEach(function (attribute) {
return attributes[attribute.name] = attribute.value;
});
var e = angular.element('<' + dynamicDirective.name + '/>');
e.attr(attributes);
return e;
}
function fixVisibility() {
if (element.children().length) {
showElement(element);
} else {
hideElement(element);
}
}
var anchorName = attrs.dynamicDirective;
hideElement(element);
var dynamicDirectives = dynamicDirectiveService.sort(dynamicDirectiveService.getInjections(anchorName, scope));
dynamicDirectives.forEach(appendDirective);
fixVisibility();
scope.$on('dynamicDirectiveInjectionUpdated', function (evt, name) {
if (name !== anchorName) {
return;
}
var dynamicDirectives = dynamicDirectiveService.sort(dynamicDirectiveService.getInjections(anchorName, scope));
var dIds = {},
currentIds = {};
dynamicDirectives.forEach(function (d) {
return dIds[d._id] = d;
});
element.children().each(function (index, elt) {
var $e = angular.element(elt),
directiveId = $e.attr(DYNAMIC_DIRECTIVE_ID);
if (!directiveId) {
return;
}
if (!dIds[directiveId]) {
$e.remove();
return;
}
currentIds[directiveId] = true;
});
var dIdsCount = Object.keys(dIds).length,
currentIdsCount = Object.keys(currentIds).length;
if (dIdsCount !== currentIdsCount) {
Object.keys(dIds).forEach(function (id) {
if (!currentIds[id]) {
appendDirective(dIds[id]);
}
});
}
orderDirectives(element, dynamicDirectives);
fixVisibility();
});
}
return {
restrict: 'A',
link: link
};
}]);
})(angular);
//# sourceMappingURL=dynamic-directive.js.map