What is the alternative of ng-init=\"myText=\'Hello World!\'\"
in Angular 2 to add in the template, not in the component
You can use a directive
@Directive({
selector: 'ngInit',
exportAs: 'ngInit'
})
export class NgInit {
@Input() values: any = {};
@Input() ngInit;
ngOnInit() {
if(this.ngInit) { this.ngInit(); }
}
}
you can use it to pass a function to be called like
<div [ngInit]="doSomething"
or to make values available
<div ngInit [values]="{a: 'a', b: 'b'}" #ngInit="ngInit">
<button (click)="clickHandler(ngInit.values.a)">click me</button>
</div>
ngInit
addes the directive[values]="{a: 'a', b: 'b'}"
sets some initial values#ngInit="ngInit"
creates a reference for later usengInit.values.a
reads the a
value from the created reference.See also Converting Angular 1 to Angular 2 ngInit function
Another approach is by using the @Output decorator and EventEmitter:
import {Directive, OnInit, Output, EventEmitter} from '@angular/core';
@Directive({
selector: '[ngInit]'
})
export class NgInitDirective implements OnInit {
@Output()
ngInit: EventEmitter<any> = new EventEmitter();
ngOnInit() {
this.ngInit.emit();
}
}
And then use it like:
<div *ngIf="condition" (ngInit)="initialize()"> ... </div>
Demo
While I agree that initialization should go into the ngOnInit life-cycle hook, it should also be noted that you can use the constructor of the component to initialize class members. In your simple example, you could even use the member declaration to set the variable, e.g.:
@Component({ template: '<div>{{myText}}</div>' })
export class MyComponent {
myText = 'Hello World!';
}
Little Update! In the latest versions of Angular this will not work:
@Directive({
selector: 'ngInit',
exportAs: 'ngInit'
})
you should use '[]':
@Directive({
selector: '[ngInit]',
exportAs: 'ngInit'
})
A possible improvement over Günter's answer:
@Directive({
selector: 'ngInit',
exportAs: 'ngInit'
})
export class NgInit {
@Input() ngInit: () => any;
ngOnInit() {
if(typeof this.ngInit === 'function') {
this.ngInit();
} else {
// preventing re-evaluation (described below)
throw 'something';
}
}
}
And then use higher-order functions for passing in data, like so:
// component.ts
myInitFunction(info) {
// returns another function
return () => console.log(info);
}
If you use a higher-order function like this, you also don't need to worry about what this
is inside of myInitFunction
since an arrow function is really what is passed.
Use the directive like so:
// component.html
<another-component #ref></another-component>
<div [ngInit]="myInitFunction(ref)"></div>
If you were to try and create a directive that doesn't pass in a function as the input in the manner described here, you run the risk of infinite loops. For example, you'd get that if you whole directive was simply evaluating the expression you gave it.
This is what would happen if your myInitFunction
method didn't return another function (and your HTML was the same as above). You'd console out, return undefined, and then change detection would re-evaluate it, consoling out over and over.
You do not always need a custom directive for this. If you're okay with your function being called more than once, you can simple do:
<input #input [attr.init]="resizeInput(input)"/>
The word "init" there is completely arbitrary. The downside is yourInitFunction will get called on every digest cycle.
Note, if you return anything from your function this will add an attribute called "init" to your element with the returned value. If you return undefined
, it will not add the attribute.
This is normally a non-issue, just keep it in mind.