I'm new with angular directive so I had created a directive as follows:
import { Directive, ElementRef, Input, Output } from '@angular/core';
@Directive({
selector: "[bonusCard]"
})
export class BonusCard {
@Input() bonus: string;
@Input() amount: string;
@Input() isSeleted: string;
constructor(private el: ElementRef){
el.nativeElement.innerHTML = this.getTemplate()
}
getTemplate(){
return ''+
'<div>'+
'<div class="bonus">'+this.bonus+'</div>'+
'<div class="amount">'+this.amount+'</div>'+
'<div class="radio '+(this.isSeleted?'is_seleted':'')+'"></div>'+
'</div>' +
'';
}
}
I'm using this directive in template as follows:
<div class="column col-3" *ngFor="let b of bonusJson;let i = index" bonusCard [bonus]="b.bonus" [amount]="b.amount" [isSeleted]="i==activeBonus"></div>
where variable are defined as below:
bonusJson = [{
bonus:1500,
amount:2000
},{
bonus:1000,
amount:1000
},{
bonus:500,
amount:500
},{
bonus:0,
amount:100
}];
activeBonus = 0;
But view is not rendering correctly it is showing undefind
in view as below.
Don't use the value of @Input()
properties inside the constructor. Use ngOninit()
instead.
You are executing your code in the constructor, but the constructor is executed before that the Input
s are bound.
When you bootstrap your application, Angular will create a tree of components using the new
keywoard for each of them and their constructor is thus executed. But in this phase change detection cycle isn't active yet, the tree hasn't still been attached to DOM and most of application data hasn't still been fetched and parsed.
The constructor isn't the ideal place to put complex initialization logic, as you can read in this guide, written by a developer of the Angular team. All what you will need to do in the constructor is just basic initialization and dependency injection result.
Things change in the ngOnInit()
hook method. When Angular calls this method, instead, it has already attached each component to the DOM, injected all required dependencies and processed input bindings. You can safely use your Input()
property with no fear.
Coming back to your question, you call getTemplate()
it looks for this.bonus
, this.amount
and this.isSelected
but the view of the parent component isn't still parsed and the input proprierites aren't still bound, so you receive undefined
instead.
To avoid this problem, move your code from the constructor to the ngOnInit hook.
export class BonusCard implements OnInit {
@Input() bonus: string;
@Input() amount: string;
@Input() isSeleted: string;
constructor(private el: ElementRef) {}
ngOnInit() {
this.el.nativeElement.innerHTML = this.getTemplate()
}
I want to provide three additional advises to improve your code:
===
instead of ==
, if you are sure you are comparing two variables of the same type, like in this case.So:
[isSeleted]="i===activeBonus"
and
return `<div>
<div class="bonus"> ${this.bonus} </div>
<div class="amount"> ${this.amount} </div>
<div class="radio ${this.isSeleted?'is_seleted':''}"></div>
</div>`;