In angular 1 we could do one time binding in this way: {{ ::myFunction() }}
.
In angular 2 this is throwing:
EXCEPTION: Template parse er
ChangeDetectionStrategy.CheckOnce
is the solution for this problem.
Some info here:
http://www.syntaxsuccess.com/viewarticle/change-detection-in-angular-2.0
https://angular.io/docs/ts/latest/api/core/index/ChangeDetectionStrategy-enum.html
Best Practice Most of the Time: ngOnInit()
In Angular 2+ we have ngOnInit()
which will generally only run one time, exactly when the component is initialized. This is the simplest and usually best solution to the issue of one-time binding.
Binding to a function can lead to dozens of unneeded calls to that function and slow down your app.
Instead of {{ ::myFunction() }}
, create a property on the component and set its value in ngOnInit()
:
export class MyComponent implements OnInit {
myValue: any;
constructor() { }
ngOnInit() {
this.myValue = /* CALL FUNCTIONS OR CALCULATE VALUE HERE */
}
}
And then in the template, simply use:
{{ myValue }}
Your calculation will run just once.
Since one time read/bind isnt possible by default in angular, I think writing a public getter function will solve the issue.
For example
public getValue():number {
return mynumber ? mynumber : 25; // if mynumber is not undefined the mynumber else return 25
}
//In html template
<input type="range" min="getValue()" max="100">
This will work perfectly if the getter function is able to reply back before the template rendering takes place. So the initialization of mynumber would be great if done in ngOnInit() function
I found solution for one time binding in angular 2 here: https://github.com/angular/angular/issues/14033
I created this directive:
import { Directive, TemplateRef, ViewContainerRef, NgZone } from "@angular/core";
@Directive({
selector: '[oneTime]',
})
export class OneTimeDirective {
constructor(template: TemplateRef<any>, container: ViewContainerRef, zone: NgZone) {
zone.runOutsideAngular(() => {
const view = container.createEmbeddedView(template);
setTimeout(() => view.detach());
})
}
}
And used it:
<some-selector *oneTime [somePropertyToOneTimeBinding]="someValueToOneTimeBinding"></some-selector>
For example:
<iframe *oneTime [src]="myUrl"></iframe>
Currently, you cannot do one time binding with Angular 2. However, you can know when your binding changes and reset your inputs.
Angular 2 provides OnChanges lifecycle hook for the same. You need to implement OnChanges interface to get the changes.
See the code example below where, I am storing the data-bound property in a private variable when OnInit is called.
export class Footer implements OnInit, OnChanges {
@Input() public name: string;
private privname: string;
constructor() { }
ngOnInit() {
this.privname = this.name;
}
ngOnChanges(changes: { [key: string]: SimpleChange }): void {
if (!changes["name"].isFirstChange()) {
this.name = this.privname;
}
}
}
Later when other changes occur, I am setting the value to its old value on subsequent changes.
This mechanism works like One-time binding.
Alternate solutions: You could also use a setter function to capture the changes.
ChangeDetectionStrategy.CheckOnce is the solution for this problem.
This has been updated to OnPush
see also comment in code:
export declare enum ChangeDetectionStrategy {
/**
* `OnPush` means that the change detector's mode will be set to `CheckOnce` during hydration.
*/
OnPush = 0,
/**
* `Default` means that the change detector's mode will be set to `CheckAlways` during hydration.
*/
Default = 1,
}