动态表单生成之拖拽生成表单(下)
我们的动态表单,最终要实现的效果与Form.io的在线生成表单的效果类似,可以参考它的demo地址:https://codepen.io/travist/full/xVyMjo
准备工作
首先,我们在过程中会需要用到一个弹出层控件,这里引用KendoUI的Dialogs,使用下面的命令安装:
npm i --save @progress/kendo-angular-dialog
安装完成后,会提示几个可选依赖没有安装,我们继续使用命令完成安装:
npm i --save @progress/kendo-angular-buttons jquery popper.js
然后,我们将上一个列子中的动态表单的相关代码Copy过来,用于我们本次动态表单的生成,首先是HTML中的代码,我们将它放到拖拽释放区域,代码如下:
<div class="col-md-8">
<h4>请将表单元素拖拽到这里</h4>
<div style="min-height: 300px;background-color: #EDEDEE" droppable (onDrop)="onDropToForm($event)">
<form [formGroup]="formGroup" class="k-form">
<dynamic-kendo-form [group]="formGroup"
[model]="formModel"
>
</dynamic-kendo-form>
</form>
</div>
</div>
然后,我们我们将Component中的相关代码删掉FormModel中的具体控件的定义后复制过来,代码如下:
formModel: DynamicFormControlModel[] = []; formGroup: FormGroup; constructor(private formService: DynamicFormService) { } ngOnInit() { this.formGroup = this.formService.createFormGroup(this.formModel); }
好,准备工作到此完成。
动态添加文本框到表单中
下面,我们来实现将文本框添加到表单中。大概流程是这样的,当我们将文本框控件拖拽到表单中时,弹出一个窗口,让用户输入文本框的Id、Label、Placeholder,然后点击保存后,我们将文本框按用户输入的属性动态的添加到表单中。
我们一步步来~
首先,定义一个弹出窗口,并定义了一些属性和方法绑定到这个窗口中,其中:
textboxWindowIsOpen属性来绑定窗口是否可见
closeTextboxWindow方法用于关闭窗口
saveTextBox方法用于用户点击保存按钮后的动态添加控件到表单的相关事件处理
Id,Label,Placeholder文本框均与txtBox的相关属性绑定,txtBox属性的类型为DynamicInputModel,即ng-dynamic-forms中定义的文本框的类型。
<kendo-window title="请输入表单元素属性" *ngIf="textboxWindowIsOpen" (close)="closeTextboxWindow()"
[minWidth]="250" [width]="450">
<form class="k-form">
<label class="k-form-field">
<span>ID</span>
<input class="k-textbox" placeholder="Text Box Id" name="id" [(ngModel)]="txtBox.id"/>
</label>
<label class="k-form-field">
<span>Label</span>
<input class="k-textbox" placeholder="Text Box Label" name="label" [(ngModel)]="txtBox.label"/>
</label>
<label class="k-form-field">
<span>Placeholder</span>
<input class="k-textbox" placeholder="Text Box Placeholder" name="placeholder" [(ngModel)]="txtBox.placeholder"/>
</label>
<div class="text-right">
<button type="button" class="k-button" (click)="closeTextboxWindow()">关闭</button>
<button type="button" class="k-button k-primary" (click)="saveTextBox()">保存</button>
</div>
</form>
</kendo-window>
然后让我们来看看最终ts代码中逻辑的实现吧:
首先是属性的定义:
textboxWindowIsOpen = false; txtBox: DynamicInputModel = new DynamicInputModel({});
其次当文本框控件拖拽到表单区域时,由上篇中提到的onDrop时间的触发来打开上面定义的window:
onDropToForm(event) { switch (event.dragData.type) { case 'TextBox': this.openTextboxWindow(); break; default: break; } }
打开和关闭window的方法其实就是修改了如下textboxWindowIsOpen属性的值:
openTextboxWindow() { this.textboxWindowIsOpen = true; } closeTextboxWindow() { this.textboxWindowIsOpen = false; }
最后,当用户点击保存时,动态添加文本框到表单中:
saveTextBox() { const newTxtbox = new DynamicInputModel({ id: this.txtBox.id, label: this.txtBox.label, placeholder: this.txtBox.placeholder }); this.formModel.push(newTxtbox); this.formGroup = this.formService.createFormGroup(this.formModel); this.closeTextboxWindow(); }
保存的这段代码中,有两个点需要注意:
1.不能直接将我们定义的txtBox属性push到formModel中,因为txtBox是对象,为引用类型,而我们的txtBox是一个公用的对象,如果直接push该对象,则后续该对象发生变化,formModel中的该对象也会跟着变。
2.push完成后,必须再次使用formService创建表单,这里的原因我也没有找到,不这样做会报错~
this.formGroup = this.formService.createFormGroup(this.formModel);
Show Demo
让我们来看看效果吧(这只是个Demo,有非常明显的Bug还请大家见谅)
另外,对于其他的控件,其实原理都是一样的,只是在弹出的窗口中的属性不同而已,这里提供给大家思路,如果我把这些元素都实现了,我会放出来给大家~
代码
拖拽相关的所有代码打包如下:
1 <div style="padding:20px;"> 2 <div class="row" style="margin-top:20px;border: 1px solid;padding:10px;"> 3 <div class="col-md-4"> 4 <ul class="list-group"> 5 <li class="list-group-item" draggable [dragData]="{type:'TextBox'}">TextBox</li> 6 <li class="list-group-item" draggable [dragData]="{type:'Select'}">Select</li> 7 <li class="list-group-item" draggable [dragData]="{type:'TextArea'}">TextArea</li> 8 <li class="list-group-item" draggable [dragData]="{type:'Password'}">Password</li> 9 <li class="list-group-item" draggable [dragData]="{type:'Number'}">Number</li> 10 </ul> 11 </div> 12 <div class="col-md-8"> 13 <h4>请将表单元素拖拽到这里</h4> 14 15 <div style="min-height: 300px;background-color: #EDEDEE" droppable (onDrop)="onDropToForm($event)"> 16 <form [formGroup]="formGroup" class="k-form"> 17 <dynamic-kendo-form [group]="formGroup" 18 [model]="formModel" 19 > 20 21 </dynamic-kendo-form> 22 </form> 23 </div> 24 </div> 25 </div> 26 </div> 27 28 <kendo-window title="请输入表单元素属性" *ngIf="textboxWindowIsOpen" (close)="closeTextboxWindow()" 29 [minWidth]="250" [width]="450"> 30 31 <form class="k-form"> 32 33 <label class="k-form-field"> 34 <span>ID</span> 35 <input class="k-textbox" placeholder="Text Box Id" name="id" [(ngModel)]="txtBox.id"/> 36 </label> 37 <label class="k-form-field"> 38 <span>Label</span> 39 <input class="k-textbox" placeholder="Text Box Label" name="label" [(ngModel)]="txtBox.label"/> 40 </label> 41 <label class="k-form-field"> 42 <span>Placeholder</span> 43 <input class="k-textbox" placeholder="Text Box Placeholder" name="placeholder" [(ngModel)]="txtBox.placeholder"/> 44 </label> 45 46 <div class="text-right"> 47 <button type="button" class="k-button" (click)="closeTextboxWindow()">关闭</button> 48 <button type="button" class="k-button k-primary" (click)="saveTextBox()">保存</button> 49 </div> 50 </form> 51 52 </kendo-window> 53
1 import {Component, OnInit} from '@angular/core'; 2 import {DynamicFormControlModel, DynamicFormService, DynamicInputModel} from "@ng-dynamic-forms/core"; 3 import {FormGroup} from "@angular/forms"; 4 5 @Component({ 6 selector: 'app-kendo-ui-drag-drop', 7 templateUrl: './kendo-ui-drag-drop.component.html', 8 styleUrls: ['./kendo-ui-drag-drop.component.css'] 9 }) 10 export class KendoUiDragDropComponent implements OnInit { 11 12 textboxWindowIsOpen = false; 13 txtBox: DynamicInputModel = new DynamicInputModel({}); 14 formModel: DynamicFormControlModel[] = []; 15 formGroup: FormGroup; 16 17 constructor(private formService: DynamicFormService) { 18 } 19 20 21 ngOnInit() { 22 this.formGroup = this.formService.createFormGroup(this.formModel); 23 } 24 25 onDropToForm(event) { 26 switch (event.dragData.type) { 27 case 'TextBox': 28 this.openTextboxWindow(); 29 break; 30 default: 31 break; 32 } 33 } 34 35 openTextboxWindow() { 36 this.textboxWindowIsOpen = true; 37 } 38 39 closeTextboxWindow() { 40 this.textboxWindowIsOpen = false; 41 } 42 43 saveTextBox() { 44 const newTxtbox = new DynamicInputModel({ 45 id: this.txtBox.id, 46 label: this.txtBox.label, 47 placeholder: this.txtBox.placeholder 48 }); 49 50 this.formModel.push(newTxtbox); 51 this.formGroup = this.formService.createFormGroup(this.formModel); 52 this.closeTextboxWindow(); 53 } 54 } 55