Wednesday, 26 December 2018

How to dynamically create a component in Angular?

Background

In the last few posts, we have been seeing how to write applications in Angular. If you wish to revisit some of them take a look at the Related Links section at the bottom of this post. In this post, I will show you how to dynamically create components on Angular. I am assuming you already have the boilerplate code we have written so far. This would be an extension to it.


How to dynamically create a component in Angular?

Let us start making changes from the AppComponent. If you remember this is the component we have added in the bootstrap property of our root module. Go to the HTML content of this component, remove all the code we had added before for routing and replace it with the following code -

<div class="hello-world">
  <h1>{{title}}</h1>


  <button (click)="genComponent('Botton 1 Clicked!')">Button 1</button>
  <button (click)="genComponent('Botton 2 Clicked!')">Button 2</button>


  <template #messagecontainer>
  </template>


</div>

It is a simple HTML content. Let me explain this first. 

  • We have a simple h1 tag which has a bound value called title. title value would be provided by the corresponding component definition in the typescript file. We will come to the changes in typescript file in a moment. 
  • Next, we have 2 buttons that call the function genComponent on the click event. This function definition would again go in the typescript file which we will see shortly.
  • Lastly, we have a template tag where we will dynamically load our component. This new component as we will see has a bound value called msg which is what you see as the argument of genComponent function.
Now let's see how the typescript file for this looks like -

I am going to create a new component in the same AppComponent file. If you wish you can create a new file for it. The component is as follows -

@Component({
  selector: 'app-child',
  template: `
<h2>Message : {{msg}}</h2>
`
})
export class ChildComponent {
  @Input() msg: string;
}

As you can see this ChildComponent which we intend to create dynamically take an argument msg as input. We will see how we can pass this input when we dynamically create this. You can also see it's HTML content - It simply has an H2 tag with the bound msg input.

Now, let's take a look at AppComponents definition -

import { Component, Input, ViewChild, ViewContainerRef, ComponentFactoryResolver, OnDestroy } from '@angular/core';




@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnDestroy {
  title = 'angulardemo';
  componentRef: any;


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


  constructor(private resolver: ComponentFactoryResolver) { }


  genComponent(msg) {
    this.entry.clear();
    const factory = this.resolver.resolveComponentFactory(ChildComponent);
    this.componentRef = this.entry.createComponent(factory);
    this.componentRef.instance.msg = msg;
  }
  destroyComponent() {
    this.componentRef.destroy();
  }


  ngOnDestroy() {
    this.destroyComponent();
  }
}

Let us see what this is doing -
  • We already say the content of app.component.html above where we removed the navigator outlet and added 2 buttons and a template for dynamically loading our CHildComponent.
  • Inside the component definition, we have a variable called title which we say in the HTML template inside h1 tag.
  • Then we have an entry variable which points to the template tag we have created in AppCpmponents HTML file.
  • We can access template as the ViewChild inside the Component class. The template is a container in which, we want to load the ChildComponent dynamically. Therefore, we have to access template as ViewConatinerRef.ViewContainerRef represents container where one or more view can be attached.
  • Next, we have defined a constructor where we have injected ComponentFactoryResolver dependency. We need this to create the component dynamically.
  • Then we have 2 methods  - genComponent and destroyComponent that is used to create and destroy our ChildComponent dynamically. Notice how we are storing the reference of the new component in componentRef and using it.
  • In genCompinent we are doing the following -
    • Clearing the container
    • Creating a factory for ChildComponent
    • Creating component using above factory
    • Passing the value msg input using component reference instance method
  • Also, notice we are calling destroyComponent in ngOnDestroy lifecycle hook of angular component. You could add this login inside an event of your own as well. 
  • You also saw how our ChildComponent has an input called msg and you can see in genComponent function we are passing this input as this.componentRef.instance.msg.
  • Also, take care of all the imports that are on the 1st line of the above code.
NOTE: The entry component which is a reference to the template tag has APIs to create and destroy components.

The final changes that we need would be in the AppModule itself. We need to define our new Child component in declaration and entry components. So import the new ChildComponent in the AppModule and put it under declaration and entryComponents 


import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { NewEmployeeComponent } from './employee/new-employee/new-employee.component';
import { SubempComponent } from './employee/subemp/subemp.component';



@NgModule({
  declarations: [
    AppComponent,
    NewEmployeeComponent,
    SubempComponent,
    ChildComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule
  ],
  providers: [],
  bootstrap: [AppComponent],
  entryComponents: [ChildComponent]
})
export class AppModule { }

Now you should be all set to test out your code. Start your angular application with -

  • ng serve
and open your application on the set port (default 4200)-






Related Links

No comments:

Post a comment

t> UA-39527780-1 back to top