What is the defined lifecycle of property binding in Angular 2? For example I have following element in my template:
<input type="radio" name="{{choice.question_id}}" value="{{choice.id}}"
[checked]="isSelected()"
(change)="select()"
required>
When are exactly those properties and event callbacks bound? As far as I know, there is some cycle which automatically refresh the bindings. Where I can find better explanation about that cycle?
My goal above is to make radiobutton selected by default if isSelected(). Thus polling the isSelected() after initial rendering is redundant and inefficient. How can I restrict that [checked]="isSelected() just to moment when element is first added to DOM?
Bindings are evaluated at every change detection cycle.
Change detection is run after some async execution happened. Angulars zone patches most async APIs like addEventHandler, removeEventHandler, setTimeout, ... After such events are processed Angular runs change detection and checks all expressions bound to inputs ([], {{}}).
Such events happen very frequently and thus bound expressions are evaluated very frequently. Therefore it's important to make these expressions efficient. This is one of the reasons the Angular team discourages binding to functions and to rather assign the result to a property and bind to that property instead because compairsion of properties is quite efficient, or even better, bind to observables and promises (using the | async pipe) that actively notify about changes.
You can't define at what point a binding is evaluated. It is evaluated every time change detection runs. You can control though when change detection runs on your component or its child components by setting ChangeDetectionStrategy.OnPush instead of ChangeDetectionStrategy.CheckAlways (default) and invoke change detection "manually".
In devMode default, change detection also aways runs twice to check if the first change detection turn itself didn't cause any changes in the model which is considered a bug. This needs to be taken into account if you're wondering why a bound method is called so frequently. If in devMode divide the count by 2 to get the effective number as it would happen in prodMode.
Related
I was working on a component that has a button which toggles a boolean. This boolean is supposed to determine if a child component in the HTML need to re-render or not, since I want the ngOnInit function in the child to be re-run.
The situation is described in the app component here: https://codesandbox.io/s/angular-qxtm8
The app.component is the parent and second.component is the child.
I have tried three different solutions. They are onTestClickOne, onTestClickTwo, and onTestClickThree in app.component.ts. onTestClickOne and onTestClickTwo successfully re-triggers the ngOnInit in the child component. We can see the console log in it is printed on the console whenever I click the corresponding buttons. However, onTestClickThree didn't work.
I'm not 100% sure why onTestClickThree didn't work, and onTestClickTwo did.
My guesses are the following:
onTestClickTwo works because the change detection in Angular is run after the event handler has been executed. So, it will detect the boolean has been set to true. After that, the event loop will get the callback of the setTimeout and put it into the stack. Angular will execute change detection after finishing the callback.
onTestClickThree didn't work because, by the time Angular runs change detection, the boolean is already true. Angular doesn't know that it has been changed.
Let's tackle the main issue there, which is your design : why would you re-render the component to trigger ngOnInit again ?
Sure, in the case of your example, that's no big deal. But what happens for a fully coded component, making http calls, having children and all ? That will cause some severe performance issues.
Instead of re-rendering the component, you should use a function to do that.
If the event (that is initially supposed to re-render the component) comes from the child, then use an #Output. If it comes from the parent, use a #ViewChild reference.
As you can see it works well, without any detection issue.
There is possibly a solution out there for this but i can't seem to find it or if i found it already i can't understand it.
I have a AngularJS app and there is the following setup:
in the view:
<button ng-click="data=data+1"></button>
<div>{{getValue("something")}}</div>
<div>{{getAnotherValue("anotherThing")}}</div>
In the controller:
$scope.getValue=function(param){
return param+$scope.otherValues+$scope.data;
}
$scope.getAnotherValue=function(param){
return param+$scope.evenOtherValues+$scope.getValue("someOtherParam");
}
When i click the button, the "getValue()" function is recalculated automatically but not the "getAnotherValue()". Is there a way to recalculate everything what is effected by the data change, even in this nested (or even more nested) situation?
Are u sure getAnotherValue() is not updating? I set up a fiddle myself and it DOES updates.
In fact angular will ensure that the data bind to UI(DOM) to be correctly presented by adding $watch (watcher keeping track of the expression) to them.
Whenever there is something changed, there will be a digest cycle in which angular will go through all the watchers and check whether the data watched has changed (by recalculating it if it is a function).
For any watcher if the value has changed, angular will repeat the check process again, until all watchers reporting there is no change in the value they are watching.
If we write the process in steps:
1.start a digest cycle
2.ask the watchers one by one if there is any change in value
3.if any watcher reports there is a change, go back to step 1
4.if no change is reported, finish digest cycle and update UI.
However there could be exception that there are too complicated and nested watchers or any watcher loops, which will cause angular to reach the maximum number of digest cycles (default is 10). Angular will break the digest process and an exception will be thrown under this circumstance.
After digging into the code a bit I see that invalidate() on a control will increase a counter which seems to mark the control as invalidated.
This seems to lead to a rerender.
So if you have a control that you want rerendered, is it better practice to use invalidate() or rerender()?
How does a rerender actually get triggered? (other than by explicitly invoking it of course)
Both are marked as "protected", meaning you should not call any of them unless you are really deep into developing custom controls.
A control gets invalidated when (for example) a property is changed. In that case you usually want to re-render the control, that's why setters call invalidate by default.
When you overwrite your invalidate method in your custom control, you can analyze the source of the invalidation and then decide whether you really want to rerender etc. See for example the unified.Shell which decides what to do on invalidate based on the source:
https://sapui5.netweaver.ondemand.com/sdk/resources/sap/ui/unified/Shell-dbg.js line 1539ff
Again: Protected means that it should not be called from the outside (and that it should not be necessary, except for debugging).
Is it better practice to use invalidate() or rerender()?
As an application developer
Neither invalidate() nor rerender() should be used as they're not public APIs.
As a control developer
Usually, the framework already manages rerendering of the controls automatically if one of the control settings (properties, aggregations, or associations) changes. But if it's still required to trigger the rerendering explicitly, the API invalidate() should be favored over rerender() because:
rerender()
renders the control synchronously (blocking the main/UI thread)
works only when the control has been rendered before (no initial rendering possible)
does not combine multiple state changes into a single rerendering
causes additional layout trashing
has become deprecated since 1.70.
invalidate(), on the other hand, just adds the control to a collection of to-be-rendered controls so that it can be rendered together with them in one process asynchronously (currently in the next browser task using setTimeout(fn, 0)).
I encountered such thing in my work recently. I am fixing an animation issue but the bad thing is I have to start the animation after rendering. If that is all I can handle it, but the worse thing is the control rendering twice. When the first rendering happen, the animation start to do, but the second rendering comes immediately and the DOM was restructed, then the animation is terminated and the effect is like no animation executed. After investigate the code, I found there is a place invoke render() method which will execute render immediately. After change it to invoke invalidated() method, it looks the animation works well. It looks multiple invalidate() method invocation only cause executing render once.
Say I have a variable "stateName," (or rather a className in my exact case) and every time the value of stateName changes, which might be for any of myriad reasons, I want the program to respond in a uniform way.
I am thinking that I want to create an event "stateChanged" and an eventListener that listens for this change and executes a function accordingly?
I don't really know how to make custom events in javascript though, and I also don't know if this is a good approach. Any suggestions?
use a setter function so you never manipulate it directly, and then you can generate a custom event for it. If it has to trigger even when "something else" changes the value, you need to use something more powerful like mutation observing
I'm playing a bit with Backbone.js and Backbone.Marionette and I would like to know what's the difference between trigger and triggerMethod.
In particular, is there any rule of thumb to decide when use the former or the latter?
In my mind events, for example, are useful to communicate between a DOM element and its view.
triggerMethod is used in Marionette to update in cascade different components, e.g. a layout calls the show method to its children (children respond to onShow). So, for me its the same as calling a direct method on it. Is this true?
What about trigger?
Thanks you in advance.
There isn't a huge difference, and it simply depends on what you want to do...
trigger will trigger an event
triggerMethod will trigger an event AND call a corresponding method according to naming convention (see https://marionettejs.com/docs/v2.1.0/marionette.functions.html#marionettetriggermethod)
Obviously, if you only want to trigger an event, you'd use trigger. But using trigger you also create a "home made" triggerMethod implementation: trigger the event, then have a listener that will call the function you want.
So what about triggerMethod ? As mentioned above, it will trigger an event and call a method. So if your only objective is to call the method in the first place, there isn't necessarily a need for using triggerMethod.
So why would one use triggerMethod at all? Because it gives you "hooks" to add functionality with event listeners. In my book on Marionette, for example, there is a triggerMethod call in https://github.com/davidsulc/marionette-gentle-introduction/blob/master/assets/js/apps/contacts/edit/edit_controller.js#L24 to display error messages on a form. The same could be achieved by simply calling
view.onFormDataInvalid(contact.validationError);
But as mentioned above, triggerMethod give us a "hook" for later use. For example, let's say I want to add logging of user errors: I can simply add a listener to my view:
initialize: function(){
this.on("form:data:invalid", function(validationError){
// log error here
}
}
This additonal functionality can be added without impacting the rest of the code, because we've used triggerMethod instead of a direct method call. In addition, it will be easier to test later (small tests, with single point of failure):
test that "form:data:invalid" event is triggered when a user enters incorrect info
test that when "form:data:invalid" event is triggered, error gets logged
etc.
trigger(name)
Part of Backbone.js
Triggers event using the name passed in
triggerMethod(name)
Part of Marionnete.js
Does everything trigger(name) does
Also calls methods using a predefined naming convention.
eg. triggerMethod('foo') will call onFoo()
eg. triggerMethod('foo:bar') will call onFooBar()