AngularJS Events vs Watches - angularjs

What happens inside AngularJS when a event is fired with the Angular internal event system?
From a performance point of view, should I tend to use watches or events to update parts in my application?

When a JS event is fired, it is typically handled in the same way that all JS events are handled - there is nothing special or unique about this.
However, angular will wrap the handler inside of an $apply block so that after it executes the function, it can trigger a digest cycle:
$scope.$apply(function(){
$element.on('click',function(e){
...
});
})
A digest cycle iterates over all your scope variables, compares each one with the previous value to determine if anything has changed, and if it has, then their corresponding $watch handlers are called to update the view.
Since you are using angular, set up $watch expressions when you want to detect that a model on your scope has changed, and then do any DOM manipulations from inside your $watch handler. If you are concerned about performance, make sure your $watch function is optimized (i.e. avoid full jquery, avoid expensive query selectors, minimize DOM manipulation etc.)
To answer your question, you should use $watches to monitor changes to your models, and $watch functions to update your view.

Related

Ng-if and custom directives

I've been creating custom directives that involve initialization of data on load. Since this initialization depends on the state of scope objects from the parent bound to the isolate scope within my directives, I've been enclosing the directives in ng-if statements, so they are only rendered in the DOM, and therefore initialized, once the appropriate parent scope objects are created.
Ng-if seems preferable because the directives have various API calls bound to $watches that I would prefer to not trigger until the data is valid.
For example, I have a directive that provides an interface for editing a scheduled event. The parent page allows users to select an event for editing, or to create a new event. Creating a new event is done in a way that allows the user to select a variety of values to pre-populate the event object with (start dates, event type, whether the event is tied to another object in the database, etc.).
The ng-if wrapping the directive gets set to true when the user makes the selections necessary for working with the event, and the event (either an existing event, for edits, or a framework for an event created by a custom service, for adds) is bound to the isolate scope of the directive.
This prevents the $watches from triggering until the valid event is bound to the directive, and also allows me to initialize some variables local to the directive for validation and data manipulation of the event.
The problem I'm running in to is that enclosing the directive in an ng-if isolates the isolate scope.
For example, a start time for the event is something that may, or may not, be specified by the user when they create the new event from the controller. If they don't, I want to calculate a default start time based upon other variables within the directive. If they do, then I want to use that value.
The way I'm doing this is to bind a scope variable from the controller to the isolate scope of the directive:
scope: {
editEvent: '=',
overrideTime: '='
}
This is passed as an attribute of my directive:
<add-edit-event ng-if="viewEdit" event="editEvent" override-time="overrideTime">
</add-edit-slot>
Once the directive creates the new event (or cancels out), I want to reset that overrideTime in the parent scope to null.
To do that from within my directive, however, I can't rely on the two-way binding to work without traversing up past the ng-if scope, thus:
scope.$parent.$parent.overrideTime = null;
I keep running into situations like this, where the fact that the directive is enclosed in an ng-if causes complications with the scope, which makes me feel like there's a problem with my general approach.
Is there a better way of handling conditional initialization and loading of custom directives, or is enclosing them in ng-if statements okay, so long as I deal with the interposing ng-if's scope appropriately?

need two directives per view with 2-way binding to fire watch only once

I've written a pagination control directive that is bound to the view controller on pageNumber and pageSize. When either value updates, the view controller makes the appropriate, paged search and updates the data table. The directive is inserted twice, above and below the data table.
Plunker (you'll want your console open)
The trouble I'm having is that the events fire twice, once for each directive. I tried modifying it to 1-way bind pageSize and pass it as an option to setPage(), but then the two directives don't stay in sync with each other or the view controller (if it updates pageSize).
Normally, I'd use an isolated scope, but I think that won't work here. Can I make this work like I want?
Rather than having two directives implement one $watch each, move it up in the the parent controller, where it will fire only per value change. Then, if you need to propagate some value to both directives, do that with another isolate scope variable.

Why does $digest run all the watches that have been registered on the scope?

I was wondering why $digest runs all the watches that have been registered on the scope, if you had multiple inputs with data coming from the scope in a view, why does AngularJS have to dirty check all the watches of the scope when for instance just one input field is bound to a label?
Angular is not clairvoyant; it doesn't know what your watchers do except by checking their value. If something may have changed that could potentially mean that displayed data needs to be updated, Angular will check all your watchers. If something never needs to be checked, it shouldn't have a watcher.
Of course it's possible that you have something that only needs to be bound once and won't change after that, which means it doesn't have to be checked constantly. I'm not sure if there's a standard solution for that, but here is a project on Github that provides one-time binding for Angular.

How can I "integration test" my AngularJS $destroy event handler?

I've written code in my controllers to clean up resources on a $destroy event. It's straightforward to write unit tests to verify the operation of my cleanup method, but it's reasonable to wonder this: When will my app ever get a $destroy event? What can I do as a user in my browser to make my app get a $destroy event?
From the docs
Removes the current scope (and all of its children) from the parent
scope. Removal implies that calls to $digest() will no longer
propagate to the current scope and its children. Removal also implies
that the current scope is eligible for garbage collection.
The $destroy() is usually used by directives such as ngRepeat for
managing the unrolling of the loop.
Just before a scope is destroyed, a $destroy event is broadcasted on
this scope. Application code can register a $destroy event handler
that will give it a chance to perform any necessary cleanup.
Note that, in AngularJS, there is also a $destroy jQuery event, which
can be used to clean up DOM bindings before an element is removed from
the DOM.
http://docs.angularjs.org/api/ng/type/$rootScope.Scope
It appears there are a couple of things related to $destroy. My basic understanding of this is that any time a scope is no longer needed navigating away from some view and now the controller/scope used for that view is no longer needed or some DOM element is removed $destroy is called. The places I've seen this actually in use are for cleaning up timers or listeners so that the memory can be garbage collected.

AngularJS - Do I need to call $apply if no model is changed?

Greetings Overflowers,
If I am changing an HTML native attribute (say a division's class) inside an event handler, do I need to wrap it with a call to $apply? In other words, are these native attributes watchable?
EXAMPLE:
I am doing a custom directive inside which I am modifying the element's classes for styling on certain events like mouse enter, but no modification to the scope (or model).
I am wondering if I need to surround this logic with a call to $apply just in case there is a $watch on these element's class attribute? Are these $watches possible using angularjs? My custom directive will be used by other programmers.
Kind regards
Any thing that Angular doesn't know about should be inside an $apply if you want bindings to be updated etc...
So no, this native attributes are not watchable if you manually change the DOM and it's not part of a user event or Angular's service event (like $http)

Resources