###### tags: `Angular`
# 🥔基於工廠模式實現的應用 (一) 使用篇
本篇將示範如何使用下列`物件`建構應用。

### 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。