2017年1月1日 星期日

[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



沒有留言:

張貼留言