在上一篇文章中,我們使用Reactive Form動態產生表單欄位,但有時候我們難免也會遇到需要動態產生Component的需求。舉例來說,各位如果有用過Google Keep,它的每一個Note就是一個獨立的Component,當每次想要新增一個Note,勢必需要以動態的方式來產生Note Component。
這次我們以Note例子示範使用ComponentFactoryResolver動態產生Component,其重點流程如下:
1. 新增Note Component,並且在NgModule裡面加入entryComponents: [NoteComponent]。
import { Component, OnInit} from '@angular/core';
import { FormGroup, FormControl} from '@angular/forms';
@Component({
selector: 'app-note',
templateUrl: './note.component.html',
styleUrls: ['./note.component.css']
})
export class NoteComponent implements OnInit {
Form: FormGroup;
NoteID: number;
constructor( ) {
}
ngOnInit() {
this.InitForm();
}
InitForm(): void {
this.Form = new FormGroup({
Title: new FormControl(null),
Content: new FormControl(null)
});
}
}
<div class="col-sm-12">
<div class="card">
<div class="card-header font-weight-bold text-white bg-info">
{{'Note['+this.NoteID+']'}}
</div>
<div class="card-body">
<form [formGroup]="this.Form" class="form-horizontal" role="form">
<div class="form-row">
<div class="form-group col-sm-12">
<h5 style="display:inline">
<span class="badge badge-info col-sm-12 text-left">Title
<span style="color:crimson">*</span>
</span>
</h5>
<input formControlName="Title" class="form-control" type="text" />
</div>
</div>
<div class="form-row">
<div class="form-group col-sm-12">
<h5 style="display:inline">
<span class="badge badge-info col-sm-12 text-left">Content
<span style="color:crimson">*</span>
</span>
</h5>
<textarea formControlName="Content" class="form-control" rows="10"></textarea>
</div>
</div>
</form>
</div>
<div class="card-footer">
<button type="button" [attr.noteid]="this.NoteID" class="btn btn-secondary col-sm-3 offset-9">Close</button>
</div>
</div>
</div>
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { ReactiveFormsModule, FormsModule } from '@angular/forms';
import { SampleCodeRoutingModule } from './sample-code-routing.module';
import { DynamicComponentSampleComponent } from './dynamic-component-sample/dynamic-component-sample.component';
import { NoteComponent } from './note/note.component';
@NgModule({
imports: [
CommonModule,
ReactiveFormsModule,
FormsModule,
SampleCodeRoutingModule
],
declarations: [ DynamicComponentSampleComponent, NoteComponent],
exports: [DynamicComponentSampleComponent, NoteComponent],
entryComponents: [NoteComponent]
})
export class SampleCodeModule { }
2. 新增Parent Component,以讓我們在該Component動態產生Note Component。3. 在Template加入<ng-container>作為Note Component的容器。
4. 新增AddNoteComponent方法,並使用@ViewChild、ViewContainerRef以及ComponentFactoryResolver建立Note Component。
5. 如果想要綁定Note Component的Event,必須在其DOM Render 出來後才能綁定Event,所以需要在ngAfterViewChecked這個生命週期中做綁定Event的動作。
import {
Component, OnInit, ComponentFactoryResolver,
ViewContainerRef, ViewChild, ComponentRef, AfterViewChecked
} from '@angular/core';
import { NoteComponent } from '../note/note.component';
import * as _ from 'lodash';
import { fromEvent, pipe, Subscription } from 'rxjs';
declare var $: any;
declare var toastr: any;
@Component({
selector: 'app-dynamic-component-sample',
templateUrl: './dynamic-component-sample.component.html',
styleUrls: ['./dynamic-component-sample.component.css']
})
export class DynamicComponentSampleComponent implements OnInit, AfterViewChecked {
@ViewChild('Container', { read: ViewContainerRef }) _Container: ViewContainerRef;
private _NoteComponents = [];
private _NoteComponentCount = 0;
private _CurNoteComponent: NoteComponent;
constructor(private componentFactory: ComponentFactoryResolver) {
}
ngOnInit() {
this.AddNoteComponent();
}
AddNoteComponent(): void {
const componentFactory = this.componentFactory.resolveComponentFactory(NoteComponent);
const componentRef: ComponentRef = this._Container.createComponent(componentFactory);
this._CurNoteComponent = componentRef.instance;
this._CurNoteComponent.NoteID = this._NoteComponentCount;
this._NoteComponentCount++;
}
GetAllNotes() {
const dataSet: any[] = [];
for (const i of this._NoteComponents) {
dataSet.push(i.Component.Form.value);
}
toastr.options = {
positionClass: 'toast-bottom-left'
};
toastr.warning(JSON.stringify(dataSet));
}
ngAfterViewChecked(): void {
// tslint:disable-next-line:triple-equals
if (!_.find(this._NoteComponents, (i) => i.NoteComponent.NoteID as number == this._CurNoteComponent.NoteID)) {
this._NoteComponents.push({
NoteComponent: this._CurNoteComponent,
DelNoteComponentEvent: fromEvent(
$('button[noteid="' + this._CurNoteComponent.NoteID + '"]'), 'click')
.subscribe((e: MouseEvent) => {
const noteID: number = $(e.target).attr('noteid') as number;
$.each($('button[noteid]'), (index, val) => {
// tslint:disable-next-line:triple-equals
if ($(val).attr('noteid') as number == noteID) {
// tslint:disable-next-line:triple-equals
this._NoteComponents = this._NoteComponents.filter((i) => i.NoteComponent.NoteID as number != noteID);
this._Container.remove(index);
return false;
}
});
})
});
}
}
}
<div class="container-fluid">
<div class="row justify-content-center">
<div class="w-100">
<ng-container #Container>
</ng-container>
</div>
</div>
</div>
<button type="button" class="btn btn-sm btn-warning text-white" style="position: fixed; z-index: 1;
right:80px; top:90vh;width: 50px; height: 50px; border-radius: 50px;" (click)="this.GetAllNotes()">
<i class="fa fa-info fa-2x" style="margin:3px 0px"></i>
</button>
<button type="button" class="btn btn-sm btn-danger text-white" style="position: fixed; z-index: 1;
right:20px; top:90vh;width: 50px; height: 50px; border-radius: 50px;" (click)="this.AddNoteComponent()">
<i class="fa fa-plus fa-2x" style="margin:3px 0px"></i>
</button>
Source Code : https://github.com/LiChengJhe/DynamicComponentSampleCode-Angular
留言
張貼留言