I want to create a bunch of generic components (angular 1.5) with multiple optional bindings that would be used inside multiple applications.
I am afraid it will create a lot of unnecessary watchers for an application that doesn't use most of the optional bindings.
Example:
Component declaration:
let dateRangeComponent = {
bindings: {
label: '@',
name1: '@',
name2: '@',
model1: '>?',
model2: '>?',
extra1: '>?'
},
template: `<div ng-if="$ctrl.model1>stuff</div>
<div ng-if="$ctrl.model2>stuff</div>
<div ng-if="$ctrl.extra1>stuff</div>`
};
Component use example:
<date-rage-component label="Pretty Date" name1="Start" name2="end"/>
My question is if it is possible to automatically unwatch all the stuff related to the unused optional bindings, knowing they are undefined at compile time.
For instance, imagine I want to use a component in my application where it doesn't need any of the optional Binding, angular would create a lot of unnecessary watchers to keep the ng-if updated when we know they will always be false.
Am I doing an early performance optimization when not needed or misunderstanding any concept?
I thought of creating a custom wrapper directive to take advantage of the lazy transclude compilation in angular 1.5
Something like this (pseudo-code, not tested):
<optional-binding-once ng-if="::attrs.model1">
<div ng-if="attrs.model1">
stuff
</div>
</optional-binding-once>
In this way I think the code inside optional-binding-once would only be compiled if ng-if is true, thus reducing one watcher if a binding is not defined.
Well, I guess there isn't a trivial solution to reduce the number of watchers inside a component when optional bindings are not filled.
I ran some tests through the $digest phase of angular, to check if the increased number of this kind of watchers is really a problem.
Here are my results:
The tests were against a worst-case scenario having 888 components with 4 optional bindings.
Chrome - Without optional bindings ( 888 component, total watchers 889)
Chrome - With optional bindings ( 888 component, 4 optional bindings, total watchers 4441)
Safari - Without optional bindings ( 888 component, total watchers 889)
Safari - With optional bindings ( 888 component, 4 optional bindings, total watchers 4441)
Conclusions:
In a worst-case scenario, the $digest time will be increased by 1ms. I don't think this rise will be a bottleneck for my application performance. This kind of watchers will fail in the first $digest condition ( value = get(current)) !== (last = watch.last) && etc ...), thus having a small impact in the processing time, because they never change or get the angular context dirty!
I would utilise the fact that the template
property can be a function (tElem, tAttrs) { ... }
(docs) that returns a string to modify the template based on the attributes present.
The way I would do this is to use jQuery and some custom elements to indicate which parts of the template are conditional.
Here is a quick sample template function:
function template($element, $attrs) {
var fullTemplate = $('<div><if-attr name="a"><div ng-if="$ctrl.a"></div></if-attr></div>');
fullTemplate.find('if-attr').each(function() {
if (attrs.hasOwnProperty($(this).attr('name'))) {
$(this).replaceWith(this.innerHTML);
} else {
$(this).remove();
}
});
return fullTemplate[0].outerHTML;
}
template(null, {a: '1'})
=> "<div><div ng-if="$ctrl.a"></div></div>"
template(null, {b: '1'})
=> "<div></div>"
This breaks down if you wanted to fetch the template from a URL (and it isn't prepopulated in the $templateCache
) but that doesn't appear to be your situation.
The documentation states that if template
is a function, it is injected with $element
and $attrs
. That means that if you are minifying your code, make sure you use a minification-safe method of specifying the function parameter names.
e.g.
template: ['$element', '$attrs', function ($elem, $attrs) {
// ...
}],
or
function templateFn($elem, $attrs) {
// ...
}
templateFn['$inject'] = ['$element', '$attrs'];
template: templateFn,
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With