I need to create components dynamically based on a predetermined type, and be able to set and get data from them. Up until now I'm able to create and show the componentes, but I don't know how to set or get data from them.
This is how i'm adding and displaying the componentes:
app.component.ts:
export class AppComponent {
components: any[] = [];
constructor(private componentFactoryResolver: ComponentFactoryResolver) { }
addField(type: string) {
let component: any;
switch (type) {
case 'string':
// how to set data in the created component??
component = StringComponent;
break;
//...
}
const childComponent = this.componentFactoryResolver.resolveComponentFactory(component);
this.components.push(childComponent);
}
getComponentData() {
this.components.forEach(component => {
// how to access the data from each component??
});
}
}
app.component.html:
<div class="col-12" cdkDropList (cdkDropListDropped)="drop($event)">
<div cdkDrag *ngFor="let cmp of components">
<div class="row">
<div class="col-10">
<app-string *ngIf="cmp.selector == 'app-string'"></app-string>
<!-- (...) -->
</div>
<div class="col-2 field-btn pt-4">
<button type="button" mdbBtn color="danger" mdbWavesEffect>
<mdb-icon fas icon="times"></mdb-icon>
</button>
<button cdkDragHandle type="button" class="move" mdbBtn color="info" mdbWavesEffect>
<mdb-icon fas icon="arrows-alt"></mdb-icon>
</button>
</div>
</div>
</div>
</div>
string.component.ts:
export class StringComponent implements OnInit {
item = new Item();
constructor() { }
ngOnInit() {
}
}
string.component.html:
<div class="form-row mb-3">
<div class="form-group col-12 col-md-6">
<label for="name">Field Name</label>
<input mdbInput type="text" [(ngModel)]="item.value" class="form-control" id="name" placeholder="Field Name" (keyup)="onInputValueChange($event.target.value)">
</div>
<div class="form-group col-12 col-md-3">
<label for="identifier">Identifier</label>
<input mdbInput type="text" [(ngModel)]="item.id" class="form-control" id="identifier" placeholder="Autogenerated ID" disabled>
</div>
<div class="form-group col-12 col-md-3">
<label for="type">Type</label>
<input mdbInput type="text" class="form-control" id="type" placeholder="STRING" disabled>
</div>
</div>
Dynamically added components listing result:
I'm not using ng-template to add the components because i what to be able to drag and drop them within a cdkDropList container in the parent component.
What i would like to achieve is set the value for the property 'item.type' before adding the component to the list, and be able to get each components 'item' property after i fill their inputs.
What is the best approach for setting and getting the components information?
UPDATE: Thanks to @kari's solution i've managed to solve my issue. Here is the result:
I made an example how you can pass and retrieve data from your dynamic components with @Input()
and @Output()
: https://stackblitz.com/edit/angular-z6wssp
I think it works the way you wanted.
I basically suggest that you push both the component and data to your component array:
compAndData.component = childComponent;
compAndData.data = data;
this.components.push(compAndData);
Then you pass the data to the @Input()
variable in your component and retrieve the changes thru the EventEmitter
and Output()
:
<app-string [data]="cmp.data" (changedData)="onCompDataChanged($event)" *ngIf="cmp.component.selector === 'app-string'"></app-string>
Try the example: change the field names, click the GetComponentData button and check the retrieved data in the console.