Generic Angular Components - Optional Bindings

Viewed 5299

I want to create a bunch of generic component (angular 1.5) with multiple optional binding 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 the most of 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 with 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 though in creating a costum 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.

(EDIT) Some Conclusions after some tests and research

Well I guess there isn't a trivial solution to reduce the number of watchers inside a component when there are optional bindings not filled.

I runned some tests through the $digest phase of angular, to check if the increased number of this kind of watchers is really problem.

Here are my results:

The tests where against a worst case scenario having 888 components with 4 optional bindings.

Chrome - Without optional bindings ( 888 component, total watchers 889)

  • Total Watchers: 889
  • Last Digest Cycle time: 0.9950000000026193
  • Average time for the last 1004 digest cycles: 1.0544920318724353 ms
  • starting dom loading (400ms)

Chrome - With optional bindings ( 888 component, 4 optional bindings, total watchers 4441)

  • Total Watchers:4441
  • Last Digest Cycle time: 1.1549999999988358
  • Average time for the last 1001 digest cycles: 1.6851748251747816 ms
  • starting dom loading (600ms)

Safari - Without optional bindings ( 888 component, total watchers 889)

  • Total Watchers: 889
  • Last Digest Cycle time: 1.0849999999991269
  • Average time for the last 530 digest cycles: 1.211632075471664 ms

Safari - With optional bindings ( 888 component, 4 optional bindings, total watchers 4441)

  • Total Watchers: 4441
  • Last Digest Cycle time: 1.7450000000026193
  • Average time for the last 588 digest cycles: 2.1167176870748237 ms

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!

5 Answers

A good way is the answer of @GregL, use a different template with different attributes, but you can use too a ng-attr-[nameCustomAttr]="value", for the optional bindings, see my related answer, and in this way angular use a type of bindingOnce and check if the attribute has value and add or not the attribute depending on the value.

For that, the attributes need to stay on the template of the directive/component.

Note: angularjs create a watch only for variables which appear in the UI.

Well, good luck, I hope it helps you in something.

Related