Dynamic components right way 
Tags: angular, typescript, javascript, webdev
Published: 2019-08-30 08:14:57

 For all of you young people, there was a time when TV had no remote. And you had to press some mechanical buttons to switch the channel. 
![Old TV](https://img1.cgtrader.com/items/651435/838098ce4d/old-tv-3d-model-max-obj.jpg)
In those times there were only 3 channels and Tv had 8 channel buttons. Now the time has passed a few years. Now we have 8 channels. No problem but we see there are no more slots anymore. That's right “Houston we have a problem”.

Now imagine we have a component like that. Component that by some condition switches and loads other presenter components in a template with ngSwitch. By adding new presenter components your template will become unreadable and just ugly. So what to do. We need to load as many different kinds of presenter components that we like in our parent component and make our template clean and readable. Not to mention that we need communication between them in both ways. The solution is angular dynamic components.[Official documentation](https://angular.io/guide/dynamic-component-loader)

###Let's start 
You can pull working an example project from this [GitHub repo](https://github.com/ikonezg/angular-dynamic-example). 


I will not go over how to create a new angular project but jump to the interesting part and try to explain it. Life is to short :)

First things first. We have to create a directive that will expose our placeholder reference.
```
ng c d placeholder
```
```javascript
import { Directive, ViewContainerRef } from '@angular/core';

@Directive({
  selector: '[appPlaceholder]'
})
export class PlaceholderDirective {

  constructor(public viewContainerRef: ViewContainerRef) { }  

}
```
The main thing in this directive is that we are making ViewContainerRef public so that we can access it from outside.
###Magic time
```
ng g c dynamic
```
dynmaic.component.ts --> Explanations are in the comments
```javascript
import { Component, OnInit, Input, ComponentFactoryResolver, ViewChild, OnChanges, ChangeDetectionStrategy, SimpleChanges, Output, EventEmitter } from '@angular/core';
import { Component1Component } from '../component1/component1.component';
import { PlaceholderDirective } from '../placeholder.directive';
import { Component2Component } from '../component2/component2.component';
import { IComp } from '../icomp';


@Component({
  selector: 'app-dynamic',
  templateUrl: './dynamic.component.html',
  styleUrls: ['./dynamic.component.css'],
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class DynamicComponent implements OnInit, OnChanges{
  /**Here we grab reference placeholder directive  */
  @ViewChild(PlaceholderDirective, {static: true}) placeholder: PlaceholderDirective;
  @Input() inputData: number;
  @Output() changeEmit: EventEmitter<string> = new EventEmitter<string>();
  /**An array where we register what component we want to load */
  components = [Component1Component, Component2Component];

  constructor(private componentFactoryResolver: ComponentFactoryResolver) {}

  ngOnInit() {
  }
  ngOnChanges(changes: SimpleChanges): void {
    /**
     * We are using this hook for change detection and invoking loadComponent() method
     * There are more ways to do this but for the simplicity of this example I have decided on this way
     */
    if(changes.inputData.currentValue != undefined){ // We need to check if inputData has some value
      this.loadComponent();
    }
  }

  /**
   * A method that loads and creates instances of components
   */
  loadComponent(){
    /** Preparing our component for creation */
    const componentFactory = this.componentFactoryResolver.resolveComponentFactory(this.components[this.inputData]);
    /** Grabbing reference of our view placeholder */
    const viewContainerRef = this.placeholder.viewContainerRef;
    /** Clearing our placeholder  */
    viewContainerRef.clear();
    /** Magic of creating a component instance  */
    const componentRef = viewContainerRef.createComponent(componentFactory);
    /** 
     * @Input data into our instance.
     */
    (componentRef.instance as IComp).text = ''+this.inputData;
    /** @Output data from our instance  */
    (componentRef.instance as IComp).event.subscribe(
      data => this.changeEmit.emit(data)
    );
  }
}


```
**Important -- you have to import presenter components as entryComponents to work.**

dynamaic.component.html
```html
<ng-template appPlaceholder></ng-template>
```
icomp.ts --> interface for this example
```javascript
export interface IComp {
    text: string;
    event: any;
}
```
Now let us see the component that we want to load. 
```javascript
import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
import { IComp } from '../icomp';

@Component({
  selector: 'app-component1',
  templateUrl: './component1.component.html',
  styleUrls: ['./component1.component.css']
})
export class Component1Component implements OnInit, IComp {
  @Output() event = new EventEmitter<string>();
  @Input() text: string;
  textbox: string;
  constructor() { }

  ngOnInit() {
  }

  onEmit(){
    this.event.emit(this.textbox);
  }
}
```
Nothing special. Right. Only that we are implementing our interface for standardization of output and inputs. 

###That's it. 

####Github Repo 
{% github https://github.com/ikonezg/angular-dynamic-example %}