0

I have an angular 1.x app which works with a dynamic layout. The app receives a layout json with the screen's metadata and simply, generates compiled components on the fly and creates a screen.

The relevant "important code" looks like this:

const element = this.$compile(`<${this.control.type} control="control" data="data"></${this.control.type}>`)(scope);
this.$element.replaceWith(element);

Now, I'm trying to migrate this to Angular 5 and I've understood that Angular 5 dropped the DOM manipulation and the $compile functionality.

I've searched all around and found solutions that know how to render dynamic html (e.g. {{1+1}}) and other deprecated stuff (prior to Angular 5), but couldn't find a fit for rendering dynamic made components (and handling their inner bindings).

Is there any way I can accomplish this kind of functionality

YakovL
  • 7,557
  • 12
  • 62
  • 102
Amir Popovich
  • 29,350
  • 9
  • 53
  • 99
  • what you're trying to achieve is still possible in Angular, read this article [Here is what you need to know about dynamic components in Angular](https://blog.angularindepth.com/here-is-what-you-need-to-know-about-dynamic-components-in-angular-ac1e96167f9e) and [this answer](https://stackoverflow.com/a/47472523/2545680) to get an idea of what you will have to go through – Max Koretskyi Nov 30 '17 at 18:08
  • however, I wouldn't advice going that path. I had the same strategy in AngluarJS, but once we migrated to Angular we started keeping all references to the components in a service and when needed we now insert them manually. The article I linked shows how to manually manipulate the components. – Max Koretskyi Nov 30 '17 at 18:10
  • @AngularInDepth.com - I was looking for something a bit different (compiling an existing component with bindings on the fly). My main problem was to convert the component's string name into a type. This link has a very similar use case: http://blog.angular.cool/2016/11/dynamic-components-with.html Currently, that's the path I took. I've created an extra string-type mapping object and created the dynamic components on the fly with their injectable bindings. – Amir Popovich Nov 30 '17 at 20:22

1 Answers1

1

This is the way to create a component by your self like angularJS: (Don't forget to destroy it! call destroy() on the componentRef to do this)

Parent Component:

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

constructor(private componentFactoryResolver: ComponentFactoryResolver) { }
public createComponent(): void {
          const componentFactory = this.componentFactoryResolver.resolveComponentFactory(MyComponent);
          const componentRef = this.componentHolder.createComponent(componentFactory);
}

Parent Component HTML:

<button (click)="createComponent()">Add New MyComponent below</button>
<div #componentHolder ></div>

Add the MyComponent in the NgModule:

...
  imports: [
    BrowserModule,
    FormsModule,
    RouterModule.forRoot(appRoutes),
  ],
  entryComponents: [
    MyComponent
  ],...
G.Vitelli
  • 1,229
  • 9
  • 18
  • `this.componentFactoryResolver.resolveComponentFactory(MyComponent);` - This line is a problem for me. As you can see in my example, I'm trying to resolve a control's type from a string. Thanks for the reply. – Amir Popovich Nov 30 '17 at 17:01
  • You need to pass a Type Object to the resolveComponentFactory, what you can do is to create a Factory / map that resolves the string into the Type like 'MyComponent' => MyComponent otherwise is no other option to do this, if you look to the angular router in the routerConfig you need to give the class Type as well. – G.Vitelli Nov 30 '17 at 17:17
  • This is my workaround for now (plus adding those components as entryComponents). I still have one issue with injecting @Input()'s to the dynamic component - do you know how to achieve that? – Amir Popovich Nov 30 '17 at 17:19
  • [Look this post about string to type conversion](https://stackoverflow.com/questions/35356988/how-to-convert-a-string-value-to-type-in-angular-2) – G.Vitelli Nov 30 '17 at 17:20
  • You can get the instance of the component via componentRef.instance and then you can call on the instance the name of the attribute like (< MyComponent>componentRef.instance).name = 'peter' – G.Vitelli Nov 30 '17 at 17:23
  • I have build a tab implementation and that worked in angular 4.3 and 5, try it again it must work! – G.Vitelli Nov 30 '17 at 17:29
  • 1
    Found my bug, I've created the component on the ngViewAfterInit and not on ngOnInit and that's why nothing got rendered. Here's a pretty nice link that creates something similar to what I tried to create: http://blog.angular.cool/2016/11/dynamic-components-with.html +1 - Thanks for your help!! I still think this is a little work around and I was looking for something more complete - so additional answers will be more than accepted :) – Amir Popovich Nov 30 '17 at 17:46