Sunday, January 1, 2017

[ASP.NET Core X Angular] (12) - Dynamically load Component (1)

 Angular    Dynamic Component     ComponentFactoryResolver   


▌Introduction


In this article, we will continue learning
ComponentFactoryResolver and create a dynamic-component-loader directive (or called “component-outlet”).

The component-outlet will have the ability to load any (but one) component at run-time and hence we can have much advanced usage and fun(?) with it J


▋Related articles




▌Environment


▋Angular 2.1.0




▌Implement


Our goal is to show customers’ data in LIST style or CARD style, which depends on the choosing of the user. We will make use of the Component-Outlet to show the different styles.

LIST



CARD
















▋Component Outlet

import { Directive, Component, ComponentFactory, OnChanges, Input, ViewContainerRef, Compiler, ComponentFactoryResolver } from '@angular/core';

@Directive({
    selector: '[component-outlet]'
})
export class ComponentOutlet implements OnChanges {
    @Input() selector: string;

    componentRef;

    constructor(
        private vcRef: ViewContainerRef,
        private resolver: ComponentFactoryResolver) {
       
        }

    ngOnChanges() {

        if (!this.selector) return;

        const factories = Array.from(this.resolver['_factories'].values());
        const factory: any = factories.find((x: any) => x.selector === this.selector);
        const compRef:any = this.vcRef.createComponent(factory);

        if (this.componentRef) {
            this.componentRef.destroy();
        }

        this.componentRef = compRef;
    }

    public ngOnDestroy() {
        if (this.componentRef) {
            this.componentRef.destroy();
            this.componentRef = null;
        }
    }
}

PS. OnChange will be fired while any data-bound property of a directive/component changes. For more information, see Lifecycle hooks.



▋Main component

Okay, let’s put the directive on the main component and load customerdynamic-list component (or customerdynamic-card component) in constructor.


▋app.component.ts

import {Component, OnInit} from '@angular/core';
import {BrowserModule} from '@angular/platform-browser'

@Component({
    selector: 'customermvc-index',
    template: `
    <div class="form-group row" style="max-width:70%">
        <div class="col-sm-3"><button (click)="showLists()">Show Lists</button></div>
        <div class="col-sm-3"><button (click)="showCards()">Show Cards</button></div>
    </div>
    <div>
      <div component-outlet selector="{{component}}"></div>
    </div>
  `,
})

export class CustomerDynamicIndexComponent implements OnInit {
    private component: string;

    constructor() {
        this.component = "customerdynamic-list"; //or "customerdynamic-card"
    }

    private showLists() {
        this.component = "customerdynamic-list";
}

private showCards() {
        this.component = "customerdynamic-card";
}
}

PS. We can use property binding on the Component-Outlet directive as well.

<div component-outlet [selector]="component"></div>



▋list.component.ts

I will ignore the html, cus it’s not the key point here.

import {Component, OnInit, Input} from '@angular/core';
import {Customer} from '../../../class/Customer';
import {CustomerService} from '../../../service/customer.service';
import {RestUriService} from '../../../service/resturi.service';

@Component({
    selector: 'customerdynamic-list',
    providers: [CustomerService, RestUriService],
    templateUrl: '/app/component/Basic/CustomerDynamic/list.component.html'
})

export class CustomerDynamicListComponent implements OnInit {

    customers: Customer[];
    constructor(private custService: CustomerService) {
    }


    ngOnInit() {
        this.initCustomers();
    }

    private initCustomers() {
        this.custService.getAll().then(
            data => {
                this.customers = data
            });
    }
}


▋card.component.ts

The card.component.ts is the same as list.component.ts except for their HTML.



▋Demo












▋Whaz next?

We will make the Component-Oulet support passing parameter and inject to the dynamic component for more flexible usage.



▌Github






▌Reference



No comments:

Post a Comment