乐趣区

关于angular:记一个用angular实现的动态表单demo可对不同角色的表单的字段进行新增删除操作

什么是动静表单

“表单”这个词大家其实并不生疏,即 angular 中的 form。所以对于“动静表单”的了解次要着眼于“动静”。就我集体了解而言,“动静”即是能够随状况的变动而进行变动的。按这个思维进行延长并与“表单”联合起来,我大抵将“动静表单”了解为:当用户进行不同的操作时,表单中的内容(如字段等)会产生动静变更。如下图:

角色抉择学生时和抉择老师时的所渲染的表单是不同的,且不仅仅是 V 层不同,C 层中对应的 formGroup 中的字段也必须是不同的。再高级一点那就是赋予用户新增和删除不同角色的表单的字段的权力。如下图:

点击“addField”按钮后,其余角色就会(在 V 层和 C 层的 formGroup 中)新增“年龄”字段;点击“delete”后“年龄”字段便被删除。

动静表单 demo 的 angular 实现

留神:此 dome 齐全在 angular 框架下写的,所以对于新增的字段的贮存是以缓存的模式贮存在 Window.sessionStorage 中,正式的开发此局部的内容应由后端进行解决。

实现思路

要实现表单到动静表单的变迁,首先咱们应该理解,就 angular 的 form 表单而言,是由两个重要局部组成的:一是在 V 层与用户交互的 UI,二是在 C 层中的用于接管字段的值的 formGroup。在个别的表单中,二者都是曾经写死的,即动态的。所以问题的要害便是上述二者的内容如何随着用户的操作而变更。简略思考一下:前者的动静实现须要将 V 层中动态的值替换成动静变量,后者的的动静实现即 formGroup 的动态创建也须要一个动静变量来作为参数(详情请看下文 createFormGroup() 办法,位于代码实现 ->C 层)。咱们可能发现:它们都须要一个动静变量。而这个动静变量须要可能由用户的操作而管制。咱们设那个动静变量为 A(位于 C 层中),其实咱们还须要一个变量 B(位于缓存中),用于解决字段的新增和删除。最终我的思路大抵如下图:

代码实现

首先在 angular 我的项目中轻易初始化一个组件(C 层、V 层、css 文件、测试文件)

V 层

<!-- 动静表单 demo-->
<div class="row">
  <div class="col-3">
    <h2> 动静表单 </h2>
    <div class="example-outlet">
      <label> 角色:</label>
      <select [formControl]="formSelect" (change)="formSelectChange()">
        <option value="student"> 学生 </option>
        <option value="teacher"> 老师 </option>
        <option value="other"> 其余 </option>
      </select>
      <form [formGroup]="formGroup" (ngSubmit)="onSubmit(formGroup)">
        <div *ngFor="let item of baseFrom">
          <label>{{item.label}}:</label>
          <input type="text" formControlName="{{item.key}}" placeholder="{{item.placeholder}}">
          <button *ngIf="item.key !=='name'&& item.key !=='number'"(click)="deleteField(item)">delete</button>
        </div>
        <button>submit</button>
      </form>
    </div>
  </div>
  <div class="col-3">
    <h2> 动静表单增加字段 </h2>
    <div class="example-outlet">
      <form [formGroup]="formGroupForAddField" (ngSubmit)="addField(formGroupForAddField)">
        <label> 角色:</label>
        <select formControlName="role">
          <option value="student"> 学生 </option>
          <option value="teacher"> 老师 </option>
          <option value="other"> 其余 </option>
        </select>
        <div>
          <label> 字段:</label>
          <input type="text" placeholder="请输出字段名称" formControlName="field">
        </div>
        <button>add field</button>
      </form>
    </div>
  </div>
</div>

C 层

// 动静表单 demo
  baseFrom: any = [];
  formGroup = this.createFormGroup(this.baseFrom);
  formSelect = new FormControl(null);
  formGroupForAddField = new FormGroup({field: new FormControl('', [Validators.required]),
    role: new FormControl('', [Validators.required]),
  });
  studentField: any = [];
  teacherField: any = [];
  otherField: any = [];
  ngOnInit(): void {this.initBseForm();
    // @ts-ignore
    const sessionStorage =  JSON.parse(window.sessionStorage.getItem(this.formSelect.value));
    if (sessionStorage !== null) {for (const item of sessionStorage) {
        const pre = [
          {
            key: item,
            label: item,
            value: null,
            placeholder: '请输出' + item,
            validators: [Validators.required]
          }
        ];
        this.baseFrom = [...this.baseFrom, ...pre];
      }
    }
    console.log('ngOnIit', this.baseFrom);
    this.formGroup = this.createFormGroup(this.baseFrom);
  }
  addField(formGroupForAddField: FormGroup): void {if (formGroupForAddField.get('field')?.value !== ''&& formGroupForAddField.get('role')?.value !=='') {console.log('formGroupForAddField', formGroupForAddField.value);
      const role = formGroupForAddField.get('role')?.value;
      const field = formGroupForAddField.get('field')?.value;
      let addField: any = [];
      if (role === 'student') {this.studentField[this.studentField.length] = field;
        addField = this.studentField;
      } else if (role === 'teacher') {this.teacherField[this.teacherField.length] = field;
        addField = this.teacherField;
    } else if (role === 'other') {this.otherField[this.otherField.length] = field;
      addField = this.otherField;
    }
      window.sessionStorage.setItem(role, JSON.stringify(addField));
      this.ngOnInit();}
  }
  createFormGroup(options: any[]): FormGroup {const group: any = {};
    options.forEach(item => {group[item.key] = new FormControl(item.value, item.validators);
    });
    return new FormGroup(group);
  }
  deleteField(item: any): void {
    // @ts-ignore
    const sessionStorage =  JSON.parse(window.sessionStorage.getItem(this.formSelect.value));
    const role = this.formGroupForAddField.get('role')?.value;
    if (sessionStorage !== null) {console.log('deleteField', sessionStorage);
      this.deleteItemOfArr(sessionStorage, item);
      this.deleteItemOfArr(this.baseFrom, item);
      if (role === 'student')        {this.deleteItemOfArr(this.studentField, item);
      } else if (role === 'teacher') {this.deleteItemOfArr(this.teacherField, item);
      } else if (role === 'other')   {this.deleteItemOfArr(this.otherField, item); }
      window.sessionStorage.setItem(this.formSelect.value, JSON.stringify(sessionStorage));
    }
    this.ngOnInit();}
  deleteItemOfArr(ary: [], el: string): [] {
    // @ts-ignore
    const index = ary.indexOf(el);
    const delEle = ary.splice(index, 1);
    return ary;
  }
  formSelectChange(): void {this.ngOnInit();
  }
  initBseForm(): void {if (this.formSelect.value === null || this.formGroupForAddField === null) {this.formSelect.setValue('student');
      this.formGroupForAddField.get('role')?.setValue('student');
    }
    this.baseFrom = [
      {
        key: 'name',
        label: '姓名',
        value: null,
        placeholder: '请输出姓名',
        validators: [Validators.required]
      },
      {
        key: 'number',
        label: '手机号',
        value: null,
        placeholder: '请输出手机号',
        validators: [Validators.required]
      }
    ];
  }
  onSubmit(formGroup: FormGroup): void {console.log('formGroup', formGroup.value);
  }

css 文件

.example-outlet {
  margin-bottom: 10px;
  padding: 10px;
  border: 1px dashed black;
  width: 350px;
  height: 250px;
}

成果预览

重构代码

最初从新扫视这个动静表单 demo,发现一股脑的将代码写在一个组件里并不是一个好习惯。每个人所处的团队标准或正在开发的我的项目标准不同,所以就代码重构这一块儿而言还请大家依据本人的理论状况进行。对于我来说的话,我的重构思路是:在 service 层新建一个 formService 文件,将此 demo 的大部分办法批改成可通用的办法挪到 formService 文件中。这样做的益处一是能够使得此组件中代码显得更加简洁精炼,二是也能够供其余组件一起应用。

参考文章

https://segmentfault.com/a/1190000022813874

退出移动版