iT邦幫忙

0

Angular 19 新功能 - ngComponentOutlet componentInstance

  • 分享至 

  • xImage
  •  

Angular 19.1.0 對 ngComponentOutlet 新增 componentInstance getter。 透過 componentInstance,開發人員可以直接在模板和元件 class 中與渲染的元件進行互動。

定義 Greeting 服務

import { Injectable, signal } from '@angular/core';

@Injectable({
 providedIn: 'root'
})
export class AdminGreetingService {
 greeting = signal('');

 setGreeting(msg: string) {
   this.greeting.set(msg);
 }
}

AdminGreetingService 是一個具有 setGreeting 方法的服務,該服務將被注入到渲染的元件中。

建立使用者表單

import { ChangeDetectionStrategy, Component, model } from '@angular/core';
import { FormsModule } from '@angular/forms';

@Component({
 selector: 'app-user-form',
   imports: [FormsModule],
 template: `
   @let choices = ['Admin', 'User', 'Intruder'];  
   @for (c of choices; track c) {
     @let value = c.toLowerCase();
     <div>
       <input type="radio" [id]="value" [name]="value" [value]="value"
         [(ngModel)]="userType" />
       <label for="admin">{{ c }}</label>
     </div>
   }
   Name: <input [(ngModel)]="userName" />
 `,
 changeDetection: ChangeDetectionStrategy.OnPush,
})
export class UserFormComponent {
 userType = model.required<string>();
 userName = model.required<string>();
}

UserFormComponent 有一個輸入元素,您可以在其中輸入名稱和單選按鈕以選擇使用者類型。當使用者選 "Admin"時,示範會渲染 AdminComponent 元件。當使用者選擇 "User" 時,它會渲染 UserComponent 元件。

動態渲染元件

// app.component.html

<h2>{{ type() }} Component</h2>
<p>Name: {{ name() }}</p>
<h2>Permissions</h2>
<ul>
@for (p of permissions(); track p) {
 <li>{{ p }}</li>
} @empty {
 <li>No Permission</li>
}
</ul>
@Component({
 selector: 'app-admin',
 templateUrl: `app.component.html`,
 changeDetection: ChangeDetectionStrategy.OnPush,
})
export class AdminComponent implements Permission {
 permissions = input.required<string[]>();
 name = input('N/A');
 type = input.required<string>();
 service = inject(GREETING_TOKEN);

 getGreeting(): string {
   return `I am an ${this.type()} and my name is ${this.name()}.`;
 }

 constructor() {
   effect(() => this.service.setGreeting(`Hello ${this.name()}, you have all the power.`));
 }
}
@Component({
 selector: 'app-user',
 templateUrl: `app.component.html`,
 changeDetection: ChangeDetectionStrategy.OnPush,
})
export class UserComponent implements Permission {
 type = input.required<string>();
 permissions = input.required<string[]>();
 name = input('N/A');
 service = inject(GREETING_TOKEN);

 getGreeting(): string {
   return `I am a ${this.type()} and my name is ${this.name()}.`;
 }

 constructor() {
   effect(() => this.service.setGreeting(`Hello ${this.name()}.`));
 }
}

此示範有兩個元件,AdminComponentUserComponent,用於動態渲染。每個元件都有 typepermissionsname 的訊號輸入。它有一個 getGreeting 方法來傳回問候語。當 name 輸入更新時,effect 會執行回呼以在 AdminGreetingService 中設定問候語。

定義動態元件配置

export const GREETING_TOKEN = new InjectionToken<{ setGreeting: (name: string) => void }>('GREETING_TOKEN');
const injector = Injector.create({
 providers: [{
   provide: GREETING_TOKEN,
   useClass: AdminGreetingService
 }]}
);

GREETING_TOKEN 是一個 injection token,它提供實作 setGreeting 函數的物件。 Injector.create 靜態方法建立一個 injector,當程式碼注入 GREETING_TOKEN 時,該 injector 會傳回 AdminGreetingService

export const configs = {
 "admin": {
   type: AdminComponent,
   permissions: ['create', 'edit', 'view', 'delete'],
   injector
 },
 "user": {
   type: UserComponent,
   permissions: ['view'],
   injector
 },
}

configs 物件將鍵對應到動態元件 (type)、permissions 和 injector。

使用 ngComponentOutlet 以程式渲染元件

@Component({
 selector: 'app-root',
 imports: [NgComponentOutlet, UserFormComponent],
 template: `
   <app-user-form [(userType)]="userType" [(userName)]="userName"  />
   @let ct = componentType();
   <ng-container [ngComponentOutlet]="ct.type"
     [ngComponentOutletInputs]="inputs()"
     [ngComponentOutletInjector]="ct.injector" 
     #instance="ngComponentOutlet"
   />
   @let componentInstance = instance?.componentInstance;
   <p>Greeting from componentInstance: {{ componentInstance?.getGreeting() }}</p>
   <p>Greeting from componentInstance's injector: {{ componentInstance?.service.greeting() }}</p>
   <button (click)="concatPermissionsString()">Permission String</button>
   hello: {{ permissionsString().numPermissions }}, {{ permissionsString().str }}
 `,
})
export class App {
 userName = signal('N/A');
 userType = signal<"user" | "admin" | "intruder">('user');

 componentType = computed(() => configs[this.userType()]);
 inputs = computed(() => ({
   permissions: this.componentType().permissions,
   name: this.userName(),
   type: `${this.userType().charAt(0).toLocaleUpperCase()}${this.userType().slice(1)}`
 }));

 outlet = viewChild.required(NgComponentOutlet);
 permissionsString = signal({
   numPermissions: 0,
   str: '',
 });

 concatPermissionsString() {
   const permissions = this.outlet().componentInstance?.permissions() as string[];
   this.permissionsString.set({
     numPermissions: permissions.length,
     str: permissions.join(',')
   });
 }
}
componentType = computed(() => configs[this.userType()]);

componentType 是一個計算訊號,當使用者選擇使用者類型時,它會尋找type、injector 和 permissions。

<ng-container [ngComponentOutlet]="ct.type"
     [ngComponentOutletInputs]="inputs()"
     [ngComponentOutletInjector]="ct.injector" 
     #instance="ngComponentOutlet"
/>

App 元件建立一個 NgContainer 並將 type、inputs 和 injector 分配給 ngComponentOutletngComponentOutletInputsngComponentOutletInjector 輸入。

此外,ngComponentOutlet directive 將 componentInstance 公開給 instance 模板變數。

在模板中使用 componentInstance

@let componentInstance = instance?.componentInstance;
<p>Greeting from componentInstance: {{ componentInstance?.getGreeting() }}</p>
<p>Greeting from componentInstance's injector: {{ componentInstance?.service.greeting() }}</p>

在模板中,我可以依靠 componentInstance 顯示 getGreeting 方法的值。 此外,我存取 AdminGreetingService 服務並顯示 greeting 訊號的值。

在 component class 中使用 componentInstance

outlet = viewChild.required(NgComponentOutlet);
permissionsString = signal({
   numPermissions: 0,
   str: '',
});

concatPermissions() {
   const permissions = this.outlet().componentInstance?.permissions() as string[];
   this.permissionsString.set({
     numPermissions: permissions.length,
     str: permissions.join(',')
   });
}

viewChild.required 函數查詢 NgComponentOutletthis.outlet().componentInstance 公開渲染的元件。 concatPermissions 方法連接渲染元件的 permissions 輸入,並將結果指派給 permissionsString 訊號。

<button (click)="concatPermissions()">Permission String</button>
hello: {{ permissionsString().numPermissions }}, {{ permissionsString().str }}

點選按鈕會呼叫 concatPermissions 方法來更新 permissionString 訊號,並且模板會顯示訊號值。

總之,componentInstance 公開了渲染的元件,供 Angular 開發人員呼叫其訊號、輸入、方法和內部服務。

參考:


.
圖片
  直播研討會

尚未有邦友留言

立即登入留言