I have a parent component which can have multiple instances of the same child component. The tricky part here is that an instance of the child component is available at init and based on certain logic within child component I am using a output event emitter to regenerate another child component from the parent component. However, when saving I just see the first instance of the child component which I had put in my html template and not the dynamically generated one. I did see other similar questions but none seem to had the same problem. What am I missing here?
Any help would be greatly appreciated.
UPDATE - start
Added a plunker to demonstrate the issue if someone wants to play around and find the cause
https://plnkr.co/edit/FW49ztzsg5uyXF7DtHDY?p=preview
UPDATE - end
Parent Component
import { Component, OnInit, ViewContainerRef, ViewChild, ComponentFactoryResolver, ViewChildren, QueryList } from '@angular/core';
import { ChildComponent } from './child/child.component';
@Component( {
selector: 'parent-cmp',
entryComponents: [ChildComponent],
template: `
<child-cmp (onSomeSelectionChange)="onSomeSelectionChange($event)"></child-cmp>
<div #childCompSection></div>
<button (click)="save()">Save</button>
`
} )
export class ParentComponent implements OnInit {
@ViewChild( 'childCompSection', { read: ViewContainerRef } ) childCompSection: any;
currentComponentHolder: any;
@ViewChildren( ChildComponent ) childComponents: QueryList<ChildComponent>;
constructor( private resolver: ComponentFactoryResolver ) { }
ngOnInit() {
// do some stuff to load initial view, nothing done using childComponents
}
onSomeSelectionChange( someValue ) {
if ( someValue !== 3 ) {
let componentFactory = this.resolver.resolveComponentFactory( ChildComponent );
this.currentComponentHolder = this.childCompSection.createComponent( componentFactory );
this.currentComponentHolder.instance.onSomeSelectionChange.subscribe( data => this.onSomeSelectionChange( data ) );
}
}
// Need all child components (which includes dynamically generated components) here
save() {
console.log(this.childComponents); // This just prints the child-cmp object that is already added not the ones that are dynamically added via ComponentFactoryResolver
}
}
Child Component
import { Component, Output, EventEmitter } from '@angular/core';
import { ChildComponent } from './child/child.component';
@Component( {
selector: 'child-cmp',
template: `
<div>
<select [(ngModel)]="selectedValue" (ngModelChange)="showNumberField($event)">
<option value="0">Select...</option>
<option value="1">Add...</option>
<option value="2">Subtract...</option>
<option selected value="3">End</option>
</select>
<div *ngIf="showField">
<label>Number:</label><input type="number" [(ngModel)]="numValue">
</div>
</div>
`
} )
export class ChildComponent {
@Output() onSomeSelectionChange = new EventEmitter<Lookup>();
selectedValue: number;
numValue: number;
showField: boolean = false;
showNumberField(value) {
if(value !== 3) {
showField = true;
}
this.onSomeSelectionChange.emit( value ); // This does work fine and another instance of the child component is generated from parent component but QueryList in parent component does not get updated
}
}
I came across the same issue and found that you can subscribe to the query list changes which will fire if you add or remove an element. Hopefully will help someone else down the line...
@ViewChildren(MyComponent) myComp: QueryList<MyComponent>;
ngAfterViewInit() {
this.myComp.changes
.subscribe((queryChanges) => {
// Do something
});
}
Got a confirmation from the Angular chat forum that @ViewChildren/@ViewChild can only read elements which are already available in the view. Since, dynamic components are not available during view init, I had to maintain a list of all such dynamically generated components to keep track of them.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With