Angular2: get data from component, created via ComponentFactoryResolver

1k Views Asked by At

I have a grid component which I want to extend by adding filters to each column. In fact I have different filter types for each column, like simple text input, option filter, date range filter and etc. So, currently I want to create a filter factory. I want just to path a filter type and get required filter component. I was following this guide and that is what I have so far.

Factory component:

import {
    Component, Input, Inject, ViewContainerRef, ViewChild, ReflectiveInjector, ComponentFactoryResolver
} from '@angular/core';
import mod = require('../../env-bootstrap');
import {OptionsFilter} from "./options/options.filter";
import {BlankFilter} from "./blank/blank.filter";

@Component(mod.check({
    selector: 'filters-factory',
    moduleId: module.id,
    entryComponents: [OptionsFilter, BlankFilter],
    template: '<div #dynamicComponentContainer></div>'
}))

export class FiltersFactory {
    constructor(@Inject(ComponentFactoryResolver) private resolver: ComponentFactoryResolver) {}
    currentComponent = null;

    @ViewChild('dynamicComponentContainer', {read: ViewContainerRef}) dynamicComponentContainer: ViewContainerRef;

    @Input() set componentData(data: {componentName: any, inputs: any }) {
        if (! data) {
            return;
        }

        switch (data.componentName) {
            case 'name':
                component = OptionsFilter;
                break;
            default:
                component = BlankFilter;
        }

        // Inputs need to be in the following format to be resolved properly
        let inputProviders = Object.keys(data.inputs).map((inputName) => {
            return {provide: inputName, useValue: data.inputs[inputName]};
        });
        let resolvedInputs = ReflectiveInjector.resolve(inputProviders);

        // We create an injector out of the data we want to pass down and this components injector
        let injector = ReflectiveInjector.fromResolvedProviders(resolvedInputs, this.dynamicComponentContainer.parentInjector);

        // We create a factory out of the component we want to create
        let factory = this.resolver.resolveComponentFactory(component);

        // We create the component using the factory and the injector
        let component = factory.create(injector);

        // We insert the component into the dom container
        this.dynamicComponentContainer.insert(component.hostView);

        // Destroy the previously created component
        if (this.currentComponent) {
            this.currentComponent.destroy();
        }

        this.currentComponent = component;
    }
}

And my OptionsFilter component:

import {Component, Injector, Inject, Output, EventEmitter } from '@angular/core';
import mod = require('../../../env-bootstrap');

@Component(mod.check({
    selector: 'options-filter',
    moduleId: module.id,
    styleUrls: 'share/filters/options/options.css',
    templateUrl: 'share/filters/options/options.html'
}))

export class OptionsFilter {
    showNum = 0;
    constructor(@Inject(Injector) private injector: Injector) {
        this.showNum = this.injector.get('showNum');
    }

    @Output() filterValue = new EventEmitter();

    private onChange(option) {
        this.filterValue.emit(option);
    }
}

Here is how I'm adding a factory component to my grid

<td *ngFor="let columnName of columnNames">
    <filters-factory [componentData]="{ componentName: columnName ,inputs: { showNum: 9} }"></filters-factory>
</td>

And my FilterOptions template

<select (change)="onChange($event)">
    <option disabled>Options</option>
    <option>33</option>
    <option>33</option>
</select>

So the main thing for me now is to path option value from html select tag from the OptionsFilter to factory component. Is it possible?

1

There are 1 best solutions below

6
On BEST ANSWER

In FiltersFactory you can access fields of the dynamic component using

console.log(this.currentComponent.instance.option);

For this you need a property option in OptionsFilter, currently you only have an @Output().

You can't use bindings with dynamically added components, therefore @Input() and @Output() is meaningless.

You can also always use a shared service to communicate with dynamically added components, like explained in https://angular.io/docs/ts/latest/cookbook/component-communication.html#!#bidirectional-service