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?
In FiltersFactory you can access fields of the dynamic component using
For this you need a property
option
inOptionsFilter
, 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