亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > JavaScript > 正文

Angular 4.x 動態創建表單實例

2019-11-19 16:44:07
字體:
來源:轉載
供稿:網友

本文將介紹如何動態創建表單組件,我們最終實現的效果如下:

在閱讀本文之前,請確保你已經掌握 Angular 響應式表單和動態創建組件的相關知識,如果對相關知識還不了解,推薦先閱讀一下 Angular 4.x Reactive Forms Angular 4.x 動態創建組件 這兩篇文章。對于已掌握的讀者,我們直接進入主題。

創建動態表單

創建 DynamicFormModule

在當前目錄先創建 dynamic-form 目錄,然后在該目錄下創建 dynamic-form.module.ts 文件,文件內容如下:

dynamic-form/dynamic-form.module.ts

import { NgModule } from '@angular/core';import { CommonModule } from '@angular/common';import { ReactiveFormsModule } from '@angular/forms';@NgModule({ imports: [ CommonModule, ReactiveFormsModule ]})export class DynamicFormModule {}

創建完 DynamicFormModule 模塊,接著我們需要在 AppModule 中導入該模塊:

import { NgModule } from '@angular/core';import { BrowserModule } from '@angular/platform-browser';import { DynamicFormModule } from './dynamic-form/dynamic-form.module';import { AppComponent } from './app.component';@NgModule({ imports: [BrowserModule, DynamicFormModule], declarations: [AppComponent], bootstrap: [AppComponent]})export class AppModule { }

創建 DynamicForm 容器

進入 dynamic-form 目錄,在創建完 containers 目錄后,繼續創建 dynamic-form 目錄,然后在該目錄創建一個名為 dynamic-form.component.ts 的文件,文件內容如下:

import { Component, Input, OnInit } from '@angular/core';import { FormGroup, FormBuilder } from '@angular/forms';@Component({ selector: 'dynamic-form', template: ` <form [formGroup]="form"> </form> `})export class DynamicFormComponent implements OnInit { @Input() config: any[] = []; form: FormGroup; constructor(private fb: FormBuilder) {} ngOnInit() { this.form = this.createGroup(); } createGroup() { const group = this.fb.group({}); this.config.forEach(control => group.addControl(control.name, this.fb.control(''))); return group; }}

由于我們的表單是動態的,我們需要接受一個數組類型的配置對象才能知道需要動態創建的內容。因此,我們定義了一個 config 輸入屬性,用于接收數組類型的配置對象。

此外我們利用了 Angular 響應式表單,提供的 API 動態的創建 FormGroup 對象。對于配置對象中的每一項,我們要求該項至少包含兩個屬性,即 (type) 類型和 (name) 名稱:

  1. type - 用于設置表單項的類型,如 inputselect、button
  2. name - 用于設置表單控件的 name 屬性

createGroup() 方法中,我們循環遍歷輸入的 config 屬性,然后利用 FormGroup 對象提供的 addControl() 方法,動態地添加新建的表單控件。

接下來我們在 DynamicFormModule 模塊中聲明并導出新建的 DynamicFormComponent 組件:

import { DynamicFormComponent } from './containers/dynamic-form/dynamic-form.component';@NgModule({ imports: [ CommonModule, ReactiveFormsModule ], declarations: [ DynamicFormComponent ], exports: [ DynamicFormComponent ]})export class DynamicFormModule {}

現在我們已經創建了表單,讓我們實際使用它。

使用動態表單

打開 app.component.ts 文件,在組件模板中引入我們創建的 dynamic-form 組件,并設置相關的配置對象,具體示例如下:

app.component.ts

import { Component } from '@angular/core';interface FormItemOption { type: string; label: string; name: string; placeholder?: string; options?: string[]}@Component({ selector: 'exe-app', template: ` <div> <dynamic-form [config]="config"></dynamic-form> </div> `})export class AppComponent { config: FormItemOption[] = [ { type: 'input', label: 'Full name', name: 'name', placeholder: 'Enter your name' }, { type: 'select', label: 'Favourite food', name: 'food', options: ['Pizza', 'Hot Dogs', 'Knakworstje', 'Coffee'], placeholder: 'Select an option' }, { type: 'button', label: 'Submit', name: 'submit' } ];}

上面代碼中,我們在 AppComponent 組件類中設置了 config 配置對象,該配置對象中設置了三種類型的表單類型。對于每個表單項的配置對象,我們定義了一個 FormItemOption 數據接口,該接口中我們定義了三個必選屬性:type、label 和 name 及兩個可選屬性:options 和 placeholder。下面讓我們創建對應類型的組件。

自定義表單項組件

FormInputComponent

dynamic-form 目錄,我們新建一個 components 目錄,然后創建 form-inputform-select form-button 三個文件夾。創建完文件夾后,我們先來定義 form-input 組件:

form-input.component.ts

import { Component, ViewContainerRef } from '@angular/core';import { FormGroup } from '@angular/forms';@Component({ selector: 'form-input', template: ` <div [formGroup]="group"> <label>{{ config.label }}</label> <input type="text" [attr.placeholder]="config.placeholder" [formControlName]="config.name" /> </div> `})export class FormInputComponent { config: any; group: FormGroup;}

上面代碼中,我們在 FormInputComponent 組件類中定義了 config group 兩個屬性,但我們并沒有使用 @Input 裝飾器來定義它們,因為我們不會以傳統的方式來使用這個組件。接下來,我們來定義 select button 組件。

FormSelectComponent

import { Component } from '@angular/core';import { FormGroup } from '@angular/forms';@Component({ selector: 'form-select', template: ` <div [formGroup]="group"> <label>{{ config.label }}</label> <select [formControlName]="config.name"> <option value="">{{ config.placeholder }}</option> <option *ngFor="let option of config.options">  {{ option }} </option> </select> </div> `})export class FormSelectComponent { config: Object; group: FormGroup;}

FormSelectComponent 組件與 FormInputComponent 組件的主要區別是,我們需要循環配置中定義的options屬性。這用于向用戶顯示所有的選項,我們還使用占位符屬性,作為默認的選項。

FormButtonComponent

import { Component } from '@angular/core';import { FormGroup } from '@angular/forms';@Component({ selector: 'form-button', template: ` <div [formGroup]="group"> <button type="submit"> {{ config.label }} </button> </div> `})export class FormButtonComponent{ config: Object; group: FormGroup;}

以上代碼,我們只是定義了一個簡單的按鈕,它使用 config.label 的值作為按鈕文本。與所有組件一樣,我們需要在前面創建的模塊中聲明這些自定義組件。打開 dynamic-form.module.ts 文件并添加相應聲明:

// ...import { FormButtonComponent } from './components/form-button/form-button.component';import { FormInputComponent } from './components/form-input/form-input.component';import { FormSelectComponent } from './components/form-select/form-select.component';@NgModule({ // ... declarations: [ DynamicFormComponent, FormButtonComponent, FormInputComponent, FormSelectComponent ], exports: [ DynamicFormComponent ]})export class DynamicFormModule {}

到目前為止,我們已經創建了三個組件。若想動態的創建這三個組件,我們將定義一個指令,該指令的功能跟 router-outlet 指令類似。接下來在 components 目錄內部,我們新建一個 dynamic-field 目錄,然后創建 dynamic-field.directive.ts 文件。該文件的內容如下:

import { Directive, Input } from '@angular/core';import { FormGroup } from '@angular/forms';@Directive({ selector: '[dynamicField]'})export class DynamicFieldDirective { @Input() config: Object; @Input() group: FormGroup;}

我們將指令的 selector 屬性設置為 [dynamicField],因為我們將其應用為屬性而不是元素。

這樣做的好處是,我們的指令可以應用在 Angular 內置的 <ng-container> 指令上。 <ng-container> 是一個邏輯容器,可用于對節點進行分組,但不作為 DOM 樹中的節點,它將被渲染為 HTML中的 comment 元素。因此配合 <ng-container> 指令,我們只會在 DOM 中看到我們自定義的組件,而不會看到 <dynamic-field> 元素 (因為 DynamicFieldDirective 指令的 selector 被設置為 [dynamicField] )。

另外在指令中,我們使用 @Input 裝飾器定義了兩個輸入屬性,用于動態設置 config group 對象。接下來我們開始動態渲染組件。

動態渲染組件,我們需要用到 ComponentFactoryResolver ViewContainerRef 兩個對象。ComponentFactoryResolver 對象用于創建對應類型的組件工廠 (ComponentFactory),而 ViewContainerRef 對象用于表示一個視圖容器,可添加一個或多個視圖,通過它我們可以方便地創建和管理內嵌視圖或組件視圖。

讓我們在 DynamicFieldDirective 指令構造函數中,注入相關對象,具體代碼如下:

import { ComponentFactoryResolver, Directive, Input, OnInit,  ViewContainerRef } from '@angular/core';import { FormGroup } from '@angular/forms';@Directive({ selector: '[dynamicField]'})export class DynamicFieldDirective implements OnInit { @Input() config; @Input() group: FormGroup;  constructor( private resolver: ComponentFactoryResolver, private container: ViewContainerRef ) {}  ngOnInit() {  }}

上面代碼中,我們還添加了 ngOnInit 生命周期鉤子。由于我們允許使用 input select 類型來聲明組件的類型,因此我們需要創建一個對象來將字符串映射到相關的組件類,具體如下:

// ...import { FormButtonComponent } from '../form-button/form-button.component';import { FormInputComponent } from '../form-input/form-input.component';import { FormSelectComponent } from '../form-select/form-select.component';const components = { button: FormButtonComponent, input: FormInputComponent, select: FormSelectComponent};@Directive(...)export class DynamicFieldDirective implements OnInit { // ...}

這將允許我們通過 components['button'] 獲取對應的 FormButtonComponent 組件類,然后我們可以把它傳遞給 ComponentFactoryResolver 對象以獲取對應的 ComponentFactory (組件工廠):

// ...const components = { button: FormButtonComponent, input: FormInputComponent, select: FormSelectComponent};@Directive(...)export class DynamicFieldDirective implements OnInit { // ... ngOnInit() { const component = components[this.config.type]; const factory = this.resolver.resolveComponentFactory<any>(component); } // ...}

現在我們引用了配置中定義的給定類型的組件,并將其傳遞給 ComponentFactoryRsolver 對象提供的resolveComponentFactory() 方法。您可能已經注意到我們在 resolveComponentFactory 旁邊使用了 <any>,這是因為我們要創建不同類型的組件。此外我們也可以定義一個接口,然后每個組件都去實現,如果這樣的話 any 就可以替換成我們已定義的接口。

現在我們已經有了組件工廠,我們可以簡單地告訴我們的 ViewContainerRef 為我們創建這個組件:

@Directive(...)export class DynamicFieldDirective implements OnInit { // ... component: any;  ngOnInit() { const component = components[this.config.type]; const factory = this.resolver.resolveComponentFactory<any>(component); this.component = this.container.createComponent(factory); } // ...}

我們現在已經可以將 config group 傳遞到我們動態創建的組件中。我們可以通過 this.component.instance 訪問到組件類的實例:

@Directive(...)export class DynamicFieldDirective implements OnInit { // ... component;  ngOnInit() { const component = components[this.config.type]; const factory = this.resolver.resolveComponentFactory<any>(component); this.component = this.container.createComponent(factory); this.component.instance.config = this.config; this.component.instance.group = this.group; } // ...}

接下來,讓我們在 DynamicFormModule 中聲明已創建的 DynamicFieldDirective 指令:

// ...import { DynamicFieldDirective } from './components/dynamic-field/dynamic-field.directive';@NgModule({ // ... declarations: [ DynamicFieldDirective, DynamicFormComponent, FormButtonComponent, FormInputComponent, FormSelectComponent ], exports: [ DynamicFormComponent ]})export class DynamicFormModule {}

如果我們直接在瀏覽器中運行以上程序,控制臺會拋出異常。當我們想要通過 ComponentFactoryResolver 對象動態創建組件的話,我們需要在 @NgModule 配置對象的一個屬性 - entryComponents 中,聲明需動態加載的組件。

@NgModule({ // ... entryComponents: [ FormButtonComponent, FormInputComponent, FormSelectComponent ]})export class DynamicFormModule {}

基本工作都已經完成,現在我們需要做的就是更新 DynamicFormComponent 組件,應用我們之前已經 DynamicFieldDirective 實現動態組件的創建:

@Component({ selector: 'dynamic-form', template: ` <form class="dynamic-form" [formGroup]="form"> <ng-container *ngFor="let field of config;" dynamicField [config]="field" [group]="form"> </ng-container> </form> `})export class DynamicFormComponent implements OnInit { // ...}

正如我們前面提到的,我們使用 <ng-container>作為容器來重復我們的動態字段。當我們的組件被渲染時,這是不可見的,這意味著我們只會在 DOM 中看到我們的動態創建的組件。

此外我們使用 *ngFor 結構指令,根據 config (數組配置項) 動態創建組件,并設置 dynamicField 指令的兩個輸入屬性:config 和 group。最后我們需要做的是實現表單提交功能。

表單提交

我們需要做的是為我們的 <form> 組件添加一個 (ngSubmit) 事件的處理程序,并在我們的動態表單組件中新增一個 @Output 輸出屬性,以便我們可以通知使用它的組件。

import { Component, Input, Output, OnInit, EventEmitter} from '@angular/core';import { FormGroup, FormBuilder } from '@angular/forms';@Component({ selector: 'dynamic-form', template: ` <form  [formGroup]="form" (ngSubmit)="submitted.emit(form.value)"> <ng-container *ngFor="let field of config;" dynamicField [config]="field" [group]="form"> </ng-container> </form> `})export class DynamicFormComponent implements OnInit { @Input() config: any[] = []; @Output() submitted: EventEmitter<any> = new EventEmitter<any>(); // ...}

最后我們同步更新一下 app.component.ts 文件:

import { Component } from '@angular/core';@Component({ selector: 'exe-app', template: ` <div class="app"> <dynamic-form  [config]="config" (submitted)="formSubmitted($event)"> </dynamic-form> </div> `})export class AppComponent { // ... formSubmitted(value: any) { console.log(value); }}

Toddmotto 大神線上完整代碼請訪問- toddmott/angular-dynamic-forms。

我有話說

在自定義表單控件組件中 [formGroup]="group" 是必須的么?

form-input.component.ts

<div [formGroup]="group"> <label>{{ config.label }}</label> <input type="text" [attr.placeholder]="config.placeholder" [formControlName]="config.name" /></div>

如果去掉 <div> 元素上的 [formGroup]="group" 屬性,重新編譯后瀏覽器控制臺將會拋出以下異常:

Error: formControlName must be used with a parent formGroup directive. You'll want to add a formGroup directive and pass it an existing FormGroup instance (you can create one in your class).Example:<div [formGroup]="myGroup"> <input formControlName="firstName"></div>In your class:this.myGroup = new FormGroup({ firstName: new FormControl()});

formControlName 指令中,初始化控件的時候,會驗證父級指令的類型:

private _checkParentType(): void { if (!(this._parent instanceof FormGroupName) && this._parent instanceof AbstractFormGroupDirective) { ReactiveErrors.ngModelGroupException(); } else if ( !(this._parent instanceof FormGroupName) &&  !(this._parent instanceof FormGroupDirective) && !(this._parent instanceof FormArrayName)) { ReactiveErrors.controlParentException(); } }

那為什么要驗證,是因為要把新增的控件添加到對應 formDirective 對象中:

private _setUpControl() { this._checkParentType(); this._control = this.formDirective.addControl(this); if (this.control.disabled && this.valueAccessor !.setDisabledState) { this.valueAccessor !.setDisabledState !(true); } this._added = true;}

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持武林網。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
欧美日韩不卡合集视频| 国产精品va在线播放| 欧美在线免费视频| 亚洲精品永久免费精品| 97视频人免费观看| 国产v综合v亚洲欧美久久| 日韩禁在线播放| 亚洲色图在线观看| 三级精品视频久久久久| 国产一区二区三区日韩欧美| 国产精品一区电影| 亚洲成人av片在线观看| 精品国产区一区二区三区在线观看| 国产精品免费视频xxxx| 91wwwcom在线观看| 国产女精品视频网站免费| 成人亲热视频网站| 国产这里只有精品| 亚洲天堂第一页| 91国语精品自产拍在线观看性色| 久久不射电影网| 日本最新高清不卡中文字幕| 欧美激情中文字幕在线| 日韩免费黄色av| 日韩精品极品在线观看播放免费视频| 日韩精品小视频| 91精品成人久久| 久久久久久久久久久91| 亚洲精品日韩av| 亚洲美女精品久久| 久久久久久久久电影| 亚洲专区在线视频| 亚洲免费视频一区二区| 久久这里有精品视频| 超碰精品一区二区三区乱码| 久久久久五月天| 亚洲视频999| 精品国产老师黑色丝袜高跟鞋| 亚洲网站视频福利| 粉嫩av一区二区三区免费野| 久久99国产精品自在自在app| 日韩精品日韩在线观看| 岛国av在线不卡| 亚洲第一中文字幕在线观看| 久久久精品免费视频| 91精品国产精品| 国产精品美女在线观看| 国产精品一区二区三区成人| 国产成人精品一区二区在线| 日韩在线视频免费观看| 91免费的视频在线播放| 九九热在线精品视频| 97色在线观看免费视频| 亚洲视频自拍偷拍| 国产精品视频网址| 国产成人精彩在线视频九色| 久久久久久久久久婷婷| 久久精品中文字幕一区| 精品福利在线视频| 亚洲国产日韩欧美综合久久| 中文字幕在线观看日韩| 不卡毛片在线看| 国产精品久久久久久久app| 久久精品国产亚洲精品| 日韩电影中文字幕av| 亚洲欧美日韩国产中文| 国产精品视频专区| 亚洲国产天堂久久综合网| 国产成人精品久久二区二区91| 久久99国产精品久久久久久久久| 日韩欧中文字幕| 97精品在线观看| 国内精品久久久久影院 日本资源| 国产精品mp4| 日韩成人av在线播放| 91精品久久久久久久久久入口| 国产日韩欧美在线观看| 久久中文字幕在线| 97视频在线观看视频免费视频| 欧美在线视频网站| 成人日韩av在线| 国产精品www网站| 亚洲第一区第二区| 亚洲人成电影网站| 久久久久久久久国产精品| 日韩成人中文字幕在线观看| 国产一区二区三区视频免费| 国产乱人伦真实精品视频| 色av吧综合网| 久久人人爽亚洲精品天堂| 国产91精品高潮白浆喷水| 国产色婷婷国产综合在线理论片a| 日韩精品亚洲元码| 国产亚洲激情视频在线| 亚洲欧美日韩中文在线制服| 97视频免费观看| 在线日韩日本国产亚洲| 日韩国产高清视频在线| 国产一区二区三区毛片| 国产国语刺激对白av不卡| 黄色91在线观看| 国产精品日日做人人爱| 国产日韩欧美在线视频观看| 欧美乱妇40p| 亚洲一区二区三区成人在线视频精品| 国产亚洲精品美女久久久久| 欧美激情亚洲综合一区| 国产99视频精品免视看7| 不卡av电影在线观看| 欧美成人午夜视频| 91精品国产乱码久久久久久蜜臀| 亚洲tv在线观看| 高清欧美性猛交xxxx黑人猛交| 久久av资源网站| 午夜精品久久久久久久久久久久久| 91免费国产网站| 欧美成年人视频| 成人午夜高潮视频| 在线日韩中文字幕| 亚洲一区二区三区视频播放| 黑人巨大精品欧美一区二区免费| 日韩成人在线电影网| 欧美成人黑人xx视频免费观看| 亚洲精品一区二三区不卡| 成人性生交大片免费看小说| 久久久999成人| 国产精品美女久久| 亚洲人成电影网站色www| 亚洲精品福利资源站| 国产成人在线播放| 日韩一二三在线视频播| 日韩av在线播放资源| 国内精品久久久久久影视8| 久久亚洲一区二区三区四区五区高| 成人精品网站在线观看| 亚洲国产美女精品久久久久∴| 欧美xxxx14xxxxx性爽| 久久久久久久亚洲精品| 久久久久99精品久久久久| 欧美精品一区二区三区国产精品| 国产欧美 在线欧美| 神马久久久久久| 久久精品亚洲精品| 国产伦精品免费视频| 55夜色66夜色国产精品视频| 91亚洲国产成人久久精品网站| 国产成人一区二区| 热99精品只有里视频精品| 亚洲乱码国产乱码精品精| 国产精品三级在线| 国产精品无码专区在线观看| 亚洲理论在线a中文字幕| 久久精品视频在线| 成人a免费视频| 国产亚洲精品久久久久久777| 国产精品视频区| 久久久久久久久电影| 伊人久久大香线蕉av一区二区| 国产一区玩具在线观看| 日韩高清中文字幕| 日本精品久久久久久久| 亚洲一区二区三区久久| 精品视频9999| 一二美女精品欧洲|