As the code provided bellow. I tried to select a dynamic element generated by ngIf but failed.
I used two ways in total.
If you need the element only in the moment that someone interacts with it (e.g. clicks on it) or with a sibling or child element of it you can pass it's reference with the event.
Template:
<div class="test" *ngIf="expr">
<a #b id="button1" (click)="onButton1(b)">button1</a>
</div>
Code:
onButton1(button: HTMLButtonElement) {
}
If you need the element without interaction with it you might also look at the ViewChildren
query instead of ViewChild
.
You can set it up with
@ViewChildren('button') buttons: QueryList<ElementRef>;
You can then subscribe to changes of elements that match a selector through this.buttons.changes.subscribe(...)
. If the element get's created or deleted you will get notified through the subscription. However my first way involves far less boilerplate code.
Alternativly you can also only access the QueryList
synchronously in moments where you are sure that the element exists (through some preconditions). In your example you should be able to retrieve the button with
let button: ElementRef = this.buttons.first;
in these cases.
You can't get the element when the *ngIf="expr"
expression is false because then the element doesn't exist.
The value is not yet set in ngOnInit()
, only when ngAfterViewInit()
is called.
Plunker example
@Component({
selector: 'my-app',
template: `
<div class="test" *ngIf="prop">
<a #button id="button1">button1</a>
</div>
<div class="test" *ngIf="!boolean">
<a id="button2">button2</a>
</div>`
,
})
export class App {
@ViewChild('button') button: ElementRef;
prop:boolean = true;
ngAfterViewInit() {
console.log(this.button);
}
}
Plunker example with ViewChildren
@Component({
selector: 'my-app',
template: `
<button (click)="prop = !prop">toggle</button>
<div class="test" *ngIf="prop">
<a #button id="button1">button1</a>
</div>
<div class="test" *ngIf="!boolean">
<a #button id="button2">button2</a>
</div>`
,
})
export class App {
@ViewChildren('button') button: QueryList<ElementRef>;
prop:boolean = true;
ngAfterViewInit() {
console.log(this.button.toArray());
this.button.changes.subscribe(val => {
console.log(this.button.toArray());
});
}
}
Is not exactly the best but I deal with a similar situation using [ngClass] instead *ngIf. Just create a class with "display: none" and hide the elements when needed. The elements selected with @ViewChild can be accessed without problem. For now you can use this little "hack" while search for a better solution or more elegant one. ex.
.hide-element{
display: none;
}
<div class="test" [ngClass]="expr === 'yourTest' ? 'hide-element' : null">
<a #b id="button1" (click)="onButton1(b)">button1</a>
</div>
if you are handling some async return you can just use async pipe
<div class="test" [ngClass]="(expr | async) === 'yourTest' ? 'hide-element' : null">
<a #b id="button1" (click)="onButton1(b)">button1</a>
</div>
Hope it helps.
<div class="test" *ngIf="boolean">
<a #button id="button1">button1</a>
</div>
@ViewChild('button') button: elementRef;
console.log(this.button);
the ID = 'button1', not 'button'?
var target = document.getElementById('Button1');