Reputation: 11187
a function is passing the name of my component via a string but I need it to be the actual component class:
Current: "MyComponent" Need: MyComponent
I need to "convert" it so It's passing correctly. I have an ngFor
that spits out values and I'm trying to use piping to convert it:
<div id="grid">
<gridster [options]="options">
<gridster-item [item]="item" *ngFor="let item of dashboard;let i = index;">
<div class="grid-header drag-handle">
<span class="handle-icon"><i class="material-icons">open_with</i></span>
<span class="close-icon" (click)="removePanel(item)"><i class="material-icons">close</i></span>
</div>
<div class="grid-content">
<ndc-dynamic [ndcDynamicComponent]="item.viewType | dynamicComponent"></ndc-dynamic>
</div>
</gridster-item>
</gridster>
</div>
Above it's item.viewType
that's a strong coming from my item array. I'm passing the value to a dynamicComponent
custom pipe. I have my components imported into my dynamic-component.pipe.ts
. I just need to change the string to the specified viewType
and return the typed
value:
import { Pipe, PipeTransform } from '@angular/core';
//pulling in all the possible components
import * from '../../views';
@Pipe({
name: 'dynamicComponent'
})
export class DynamicComponentPipe implements PipeTransform {
transform(value: string): any {
}
}
Upvotes: 1
Views: 2001
Reputation: 54741
You'll need to manually create a mapping between string values and the components. Components are actually named functions which can be minimized to shorter variable names when compiling for production.
import ComponentA from '../views/ComponentA';
import ComponentB from '../views/ComponentA';
const componentMap = {
'ComponentA': ComponentA,
'ComponentB': ComponentB
};
@Pipe({name: 'dynamicComponent'})
export class DynamicComponentPipe implements PipeTransform {
transform(value: string): any {
if(componentMap[value]) {
return componentMap[value];
}
throw new Error(`${componentMap} component was not found`);
}
}
UPDATED:
The problem with using the name of a component at runtime is that it can be minimized into a smaller variable name for production. Therefore, doing something like views['MyComponent']
won't work later when MyComponent
is renamed to something like a12
.
An alternative approach is to use the component's selector string value to select the component. Each component in Angular has to be a unique selector string. So this is a safe value to use as a key.
You can access (at least in Angular 5) the component's metadata via the __annotations__
property. This property is an array that contains a the metadata.
So you could try something like:
import * as views from '../views';
@Pipe({name: 'dynamicComponent'})
export class DynamicComponentPipe implements PipeTransform {
transform(value: string): any {
const view = views.filter((component)=>component['__annotations__'][0]['selector'] === value));
if(view) {
return view;
}
throw new Error(`A component with selector "${value}" was not found`);
}
}
Furthermore, you could drop the need for a views
file by accessing the ngModule
directly and iterating all components to find a matching selector. Modules will have the same __annotations__
property.
Upvotes: 1
Reputation: 11187
I did this pretty simple way:
I created a views.ts
with all of my views exported at the root of my views directory. Then in directory that my pipe is being used I imported the views
using:
import * as views from '../../views/views'
Then in my transform
method I return views[value];
where value
is the string being pulled in from my template.
Upvotes: 0