###### tags: `Angular` # 🥔基於工廠模式實現的應用 (一) 使用篇 本篇將示範如何使用下列`物件`建構應用。 ![](https://i.imgur.com/SIeoIug.png) ### dynamic.directive.ts ==**src/app/dynamic/dynamic.directive.ts**== #### 🌱說明 - 共用元件容器 #### 🌱原始碼 ```typescript= import { Directive, ViewContainerRef } from '@angular/core'; @Directive({ selector: 'alle-dynamic' }) export class DynamicDirective<C> { component: C; vcRef = this.viewContainerRef; constructor(private viewContainerRef: ViewContainerRef) { } } ``` #### 🌱使用方法 **page01.component.html** ```htmlembedded= <alle-dynamic #menu></alle-dynamic> ``` 1. 將 DynamicDirective 放置在將呈現共用元件的 `HTML` 內。 `<alle-dynamic></alle_dynamic>` 2. 加上 `#` 讓 `Component` 可以識別該元件。 `#menu` **page01.component.ts** ```typescript= import { Component, ViewChild } from '@angular/core'; import { DynamicDirective } from './../../dynamic/dynamic.directive'; import { MenuAComponent } from './../../menu/menu-a/menu-a.component'; @Component({ selector: 'app-page01', templateUrl: './page01.component.html', styleUrls: ['./page01.component.scss'] }) export class Page01Component { @ViewChild('menu', { read: DynamicDirective }) menu: DynamicDirective<MenuAComponent>; } ``` 3. 在 `Component` 內使用 `@ViewChild()` 來識別動態元件。 `@ViewChild('menu', { read: DynamicDirective }) menu` 4. 為了能正確取得動態元件型別,要在 `@ViewChild()` 宣告 DynacmicDirective 型別,並泛型動態元件型別。 `@ViewChild('menu', { read: DynamicDirective }) menu: DynamicDirective<MenuAComponent>;` ### dynamic.factory.ts ==**src/app/dynamic/dynamic.factory.ts**== #### 🌱說明 - 載入共用元件 #### 🌱原始碼 ```typescript= import { ComponentFactoryResolver, Injectable, Injector, Type } from "@angular/core"; import { DynamicDirective } from "./dynamic.directive"; import { IFactory } from './factory.interface'; @Injectable({ providedIn: 'root' }) export class DynamicFactory { constructor( private _cfr: ComponentFactoryResolver, private _injector: Injector) { } create<C, Cp>(dynamicD: DynamicDirective<C>, dynamicC: Type<C>, factory?: IFactory<C, Cp>, factoryPara?: Cp): C { const comFactory = this._cfr.resolveComponentFactory(dynamicC); const comVCRef = dynamicD.vcRef; comVCRef.clear(); const comRef = comVCRef.createComponent(comFactory, null, this._injector); if (factory) { factory.run(comRef.instance, factoryPara); } comRef.changeDetectorRef.detectChanges(); return comRef.instance; } } ``` #### 🌱使用方法 **page01.component.ts** ```typescript= import { AfterViewInit, Component, ViewChild } from '@angular/core'; import { DynamicDirective } from './../../dynamic/dynamic.directive'; import { DynamicFactory } from './../../dynamic/dynamic-factory'; import { MenuAComponent } from './../../menu/menu-a/menu-a.component'; @Component({ selector: 'app-page01', templateUrl: './page01.component.html', styleUrls: ['./page01.component.scss'] }) export class Page01Component implements AfterViewInit { @ViewChild('menu', { read: DynamicDirective }) menu: DynamicDirective<MenuAComponent>; constructor( private dynamicF: DynamicFactory, ) { } ngAfterViewInit(): void { this.menu.component = this.dynamicF.create(this.menu, MenuAComponent); } } ``` 1. 注入依賴 DynamicFactory 。 `private dynamicF: DynamicFactroy` 2. 使用 DynamicFactory 來產生共用元件,這裡必須要放上`共用元件容器的參數`及`共用元件的類別`。 `this.dynacmicF.create(this.menu, MenuAComponent)` :::warning 在這邊要注意使用 DynamicFactory 來產生元件的時機,必須要在 ngAfterViewInit 後。 ::: ### component.factory.ts ==**src/app/menu/factory/route-menu.factory.ts**== #### 🌱說明 - 共用元件的邏輯實現 #### 🌱原始碼 ```typescript= import { Injectable } from "@angular/core"; import { Router } from '@angular/router'; import { AuthNameRoute } from "src/app/route/route.type"; import { IFactory } from '../../dynamic/factory.interface'; import { IMenu } from "../menu.interface"; import { IAuth } from './../../auth/auth.interface'; import { CaService } from './../../service/ca.service'; export interface IRouteMenuPara {} @Injectable({ providedIn: 'root' }) export class RouteMenuFactory<CI extends IMenu<any> = any> implements IFactory<CI, IRouteMenuPara> { constructor( private router: Router, private ca: CaService ) { } run(component: CI) { component.setMenuList(this.getMenuListByRoute()); component.currentMenu = component.menuList.find(menu => `/${menu.route}` === `${this.router.url}`) component.clickMenu.subscribe(this.clickMenu) } checkPara(para: IRouteMenuPara): IRouteMenuPara { throw new Error('Method not implemented.'); } getMenuListByRoute() { const routes = this.router.config; const menuList = routes .filter(r => r?.data?.displayName) .map(r => { return { displayName: r.data.displayName, route: r.path, isHide: !this.isValidAuth(r as AuthNameRoute) } }) return menuList; } clickMenu = (menu) => { this.router.navigate([menu.route]); } isValidAuth(data: AuthNameRoute) { if (!data.data) { console.warn('請設定權限!'); return false; } const country = this.ca.getCountry(); const main = this.ca.getMain(); let isValid = true; isValid = isValid ? this.check(country, data.data.country) : isValid; isValid = isValid ? this.check(main, data.data.main) : isValid; return isValid; } check<AE>(e: AE, auth: IAuth<AE>) { if (auth.type === 'allow') { return auth.list.filter(x => x === e).length > 0; } else if (auth.type === 'block') { return auth.list.filter(x => x === e).length < 1; } } } ``` #### 🌱使用方法 **page01.component.ts** ```typescript= import { AfterViewInit, Component, ViewChild } from '@angular/core'; import { DynamicDirective } from './../../dynamic/dynamic.directive'; import { DynamicFactory } from './../../dynamic/dynamic-factory'; import { MenuAComponent } from './../../menu/menu-a/menu-a.component'; import { RouteMenuFactory } from 'src/app/menu/factory/route-menu.factory'; @Component({ selector: 'app-page01', templateUrl: './page01.component.html', styleUrls: ['./page01.component.scss'] }) export class Page01Component implements AfterViewInit { @ViewChild('menu', { read: DynamicDirective }) menu: DynamicDirective<MenuAComponent>; constructor( private dynamicF: DynamicFactory, private routeMenuF: RouteMenuFactory, ) { } ngAfterViewInit(): void { this.menu.component = this.dynamicF.create(this.menu, MenuAComponent, this.routeMenuF); } } ``` 1. 注入依賴共用元件的工廠 RouteMenuFactory。 `private routeMenuF: RouteMenuFactory` 2. 在使用 this.dynamicF.create() 時,第三個參數放工廠的參數。 `this.menu.component = this.dynamicF.create(this.menu, MenuBComponent, this.routeMenuF);` ### 小試身手 - 請依上方的方式在 page01.component.ts 動態產生 TableAComponent。