1

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 {


  }

}
2
  • maybe check stackoverflow.com/a/42951200/1987977 Commented May 23, 2018 at 18:08
  • I get the rough just of what their doing (I'm new to angular2) but I don't fully grasp how to relate it to my pipe Commented May 23, 2018 at 18:38

2 Answers 2

1

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.

Sign up to request clarification or add additional context in comments.

7 Comments

I'm pulling in my list of possible components by placing them in a ts file using export for each then importing them into my pipe file with import * as views from '../../views/views'; This returns an an array with key value pairs already. Couldn't I just keep using that?
FYI, I'm not saying you're wrong. Just asking if using views from the import above is acceptable since it's already an array and views[value] returns the same thing as what you did above.
I think the keys in views will be the minimized version in production. Just run console.log(views) from a production build to see if I'm right.
I see the concern. Listing out each component in the pipe file can be a bit heavy when others start working in this system. The views.ts file in the views directory is used to provide a single point of export. How would you suggest addressing that?
@dcp3450 how do you assign the string values to item.viewType? Do you have control over what those values can be?
|
0

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.

2 Comments

Won't this break in production when the JavaScript is minimized and function names are shorten.
i'm going to go with ¯_(ツ)_/¯

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.