Search code examples
angularangular-dynamic-components

Angular Dynamic Components: How to Set and Get Data


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:

components listing

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:

animated gif with final result


Solution

  • 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.