npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@supine/dy-form

v11.0.2

Published

- [介绍](#DyForm) - [相关库](#相关库) - [定制动态表单](#定制动态表单) - [使用定制好的的动态表单](#使用定制好的的动态表单) - [自定义控件布局](#自定义控件布局) - [自定义布局](#自定义布局) - [自定义控件](#自定义控件) - [在定义控件模型的时候使用表单模型的上下文](#在定义控件模型的时候使用表单模型的上下文) - [自定义验证](#自定义验证) - [填充表单](#填充表单) - [动态控制表单模型](#动态控制表单模型)

Readme

文档内容

DyForm

@supine/dy-form是基于Angular表单封装的动态表单库。

通过引入一组可维护的表单控件模型和动态表单控件组件,它完全自动化了表单UI创建。

@supine/dy-form 不依赖于任何Angular UI框架, 任何Angular UI框架都可以基于@supine/dy-form封装成特定风格的动态表单

@supine/dy-form 一般也不会直接在项目中使用,因为内部并没有任何控件模型,只是提供了基础表单控件的接口

目前已经适配NG-ZORRO --- @supine/dy-form-zorro

主要特性

  • 解耦

    • 传统的Angular表单开发需要成百上千行HTML,在组件内部维护大量与表单相关的代码,不利于后期维护,可读性不强。 @supine/dy-form 最主要的初衷就是将与表单相关的业务集中到表单模型中解决,从而减弱与组件的耦合度
  • 高可读性

    • 所有控件配置都在表单模型中,表单结构一目了然
  • 快速开发

    • 不需要成百上千行HTML,组件内部也不需要维护大量与表单相关的代码
    • 大多数情况 只需要一行HTML代码和几行TS代码即可生成期望的表单
    • 表单校验,一般来说只需一行代码搞定
  • 面向对象

    • 表单模型、控件模型都基于class实现的,表单模型、控件模型都提供了基类,可以基于基类继承进行拓展
  • 易拓展性

    • 轻松实现自定义控件
    • 轻松实现自定义布局
    • 轻松拓展内置校验规则

相关库

定制动态表单

基于@supine/dy-form 定制不同Angular UI框架的动态表单(自定义控件)

  • 安装
ng add @supine/dy-form
  • 在模块中导入 DyFormModule

  • 实现控件模型(以input为例)

import {BaseModel, ModelPartial, Const} from '@supine/dy-form';

export class InputModelControl<M = any> extends BaseModel<M> {
  /**
   * type 必须要实现 不同的type代表不同的控件
   */
  @Const('INPUT') // 表示type只能为INPUT 不允许被修改
  type!: string;

  /**
   * 只读 如果为true 表示该控件为只读状态
   */
  readonly = false;
  // 如果需要其他属性 添加相应的属性即可
  constructor(init?: ModelPartial<InputModelControl>) {
    super();
    this.init(init);
  }
}
  • 实现控件模型对应的装饰器(以input为例)
import {InputModelControl as Model} from '../model/input-model.control';
import {BaseDecorator, ModelPartial} from '@supine/dy-form';

export function InputModel<M>(model?: ModelPartial<Model<M>>): PropertyDecorator {
  const newModel = new Model();
  if (model) {
    Object.assign(newModel, model);
  }
  return BaseDecorator(newModel);
}
  • 生成一个Angular组件&并配置模板
<!--模板-->
<!--通用错误提示-->
<ng-template #errorTpl let-control>
  <ng-container *ngIf="hasError(control)">
    {{getError(control)}}
  </ng-container>
</ng-template>
<ng-template #label let-model>
  <nz-form-label [nzRequired]="model.required"
                 jdDyFormLabelDef
                 [style.flex]="dyForm?.dyFormRef.labelColLayout[dyForm.breakpoint]"
                 [ngClass]="dyForm.labelClass(model)"
                 [ngStyle]="model.labelStyle"
                 [nzFor]="model.controlName">{{model.label}}
  </nz-form-label>
</ng-template>
<ng-template #inputControl let-model let-control=control>
  <input nz-input
         [id]="model.name"
         [placeholder]="model.placeHolder"
         [formControl]="control"
         [disabled]="model.disabled"
         [nzSize]="model.size"
         [type]="model.inputType"
         [readOnly]="model.readonly"/>
</ng-template>
<jd-dy-form [dyFormRef]="dyFormRef" #dyForm>
  <!-- INPUT 就是上面实现的input模型的type属性
       model 对应的就是控件模型
       control 代表的是表单控件实例
   -->
  <nz-form-item *jdDyFormColumnDef="let control; let model = model name 'INPUT'">
    <ng-template [ngTemplateOutlet]="label" [ngTemplateOutletContext]="{$implicit: model}"></ng-template>
     <nz-form-control jdDyFormControlDef
                      [nzHasFeedback]="model.hasFeedback"
                      [nzExtra]="model.extra"
                      [nzValidatingTip]="model.validatingTip"
                      [ngStyle]="model.controlStyle"
                      [nzValidateStatus]="control"
                      [ngClass]="dyForm.controlClass(model)">
        <ng-template [ngTemplateOutlet]="inputControl"
                     [ngTemplateOutletContext]="{$implicit: model, control: control}">
        </ng-template>
    </nz-form-control>
  </nz-form-item>
  <!-- 布局 可以实现各种各样的布局 在我封装的@supine/dy-form-zorro 中有3中内置布局(inline, horizontal, vertical)和一种自定义布局 -->
  <jd-form-layout>
     <form nz-form nzLayout="inline" [formGroup]="dyForm.formArea">
       <ng-container *ngFor="let control of dyForm.options">
         <ng-container jdDyFormLayoutItemName="{{control.name}}"></ng-container>
       </ng-container>
     </form>
  </jd-form-layout>
</jd-dy-form>
// TS
import {
  AfterContentInit,
  ChangeDetectionStrategy,
  Component,
  ContentChildren,
  Input,
  OnInit,
  QueryList,
  ViewChild,
  ViewEncapsulation
} from '@angular/core';
import {DyFormColumnDef, DyFormComponent, DyFormFooterDef, DyFormHeaderDef, DyFormRef} from '@supine/dy-form';

@Component({
  selector: 'jd-dy-form-zorro',
  templateUrl: './dy-form-zorro.component.html',
  styleUrls: ['./dy-form-zorro.component.scss'],
  encapsulation: ViewEncapsulation.None,
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class DyFormZorroComponent implements OnInit, AfterContentInit {
  @ViewChild(DyFormComponent, {static: true}) dyForm: DyFormComponent;

  @ContentChildren(DyFormHeaderDef, {descendants: true}) _formHeaderDefs: QueryList<DyFormHeaderDef>;

  @ContentChildren(DyFormFooterDef, {descendants: true}) _formFooterDefs: QueryList<DyFormFooterDef>;

  @ContentChildren(DyFormColumnDef, {descendants: true}) _formColumnDefs: QueryList<DyFormColumnDef>;

  @Input() dyFormRef: DyFormRef<any>;

  hasError(control: FormControl) {
    return control.errors && Object.keys(control.errors).length;
  }

  getError(control: FormControl) {
    const errors = Object.getOwnPropertyNames(control.errors);
    return control.getError(errors[0]);
  }

  constructor() {
  }

  ngOnInit(): void {
  }

  ngAfterContentInit(): void {
    // 注册form Footer 可以有多行
    this._formFooterDefs.forEach(item => this.dyForm.addFooterRowDef(item));
    // 注册form Header 可以有多行
    this._formHeaderDefs.forEach(item => this.dyForm.addHeaderRowDef(item));
    // 注册表单控件模板
    this._formColumnDefs.forEach(item => this.dyForm.addColumnDef(item));
  }

}
  • 上面的例子我们只实现了input, 按照以上的步骤即可封装一个完整的动态表单来

使用定制好的的动态表单

  • 定义表单模型
import {BaseFormModel, ValidatorRule} from '@supine/dy-form';
import {InputModel} from '../decorator/input.model';

export class LoginModel extends BaseFormModel {
  @InputModel<LoginModel>({label: '用户名'})
  @ValidatorRule(['required&max:15&min:4'], {required: '用户名字段是必填的', max: '用户名长度最多为15个字符', min: '用户名长度最少为4个字符'})
  username = [null];

  @InputModel<LoginModel>({label: '密码'})
  @ValidatorRule(['required&max:15&min:4'], {required: '密码字段是必填的', max: '密码长度最多为15个字符', min: '密码长度最少为4个字符'})
  password = [null];

  /**
   * 更新表单模型钩子
   * @param formValue 当表单初始化后 formValue就为表单对象的value 否则为null
   * @param model 注册了的模型配置数组 可以根据某些条件进行过滤 来动态控制表单
   * @param params 调用 executeModelUpdate方法传的参数 以此来更加灵活来动态控制表单
   * @return 如果返回值为void 则渲染所有注册的表单控件 如果返回表单控件数组 则只渲染该数组中的控件模型
   */
  modelUpdateHook(formValue: any, model: FormControlModel[], ...params: any[]): FormControlModel[] | void {
    return model;
  }


  /**
   * 结合我封装的HTTP模块 可轻松实现批量对接与表单相关的接口
   * HTTP模块 目前还没开源
   * 即便不使用我封装的HTTP模块 按照以下模板 也容易实现
   */
  httpRequest() {
    return this.updateValueAndValidity()
      .pipe(
        // 如果验证未通过 则过滤掉
        filter(value => value),
        map(() => {
           // 获取表单数据 如果不能满足需要 可以在子类重写value的获取
           // const body = this.value;
           // 获取提交表单的一些外部参数 比如更新的参数ID  attachValue 通过 model.withAttachValue(数据)进行设置
           // const {mapId, id} = this.attachValue;
           //
           // body.id = id;
           // 组装接口所需要的参数
           // const tempBody = {
           //   mapId,
           //   area_info: body
           // };
   
           // 一系列与表单相关的接口
           // const httpRequestMap: HttpRequestMap = {
           // update: [this.http.editAreaBaseInfo, [tempBody]]
           /* UnloadMineralArea: [this.http.setUnLoadMineralArea, [body, mapId]],
                UnloadWasteArea: [this.http.setUnLoadWasteArea, [body, mapId]],
                LoadArea: [this.http.setLoadArea, [body, mapId]],
                Road: [this.http.setRoad, [body, mapId]],
                PassableArea: [this.http.setPassableArea, [body, mapId]],
                ImpassableArea: [this.http.setImPassableArea, [body, mapId]],
                Junction: [this.http.setJunction, [body, mapId]],
                create: [this.http.createMapUtil, [body, mapId]] */
             // };
   
             // const [handle, params] = httpRequestMap[this.actionType];
             //
             // return handle(...params);
           })
         );
  }
}
  • 然后在HTML中声明动态表单
<jd-dy-form-zorro [dyFormRef]="dyFormRef"></jd-dy-form-zorro>
  • 在组件中定义dyFormRef属性
import {Component, OnInit} from '@angular/core';
import {DyFormRef} from '@supine/dy-form';
import {FormModel} from './form.model';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit {
  dyFormRef = new DyFormRef(FormModel);

  ngOnInit(): void {
  }

  constructor() {
    // 执行这行代码才会渲染
    this.dyFormRef.executeModelUpdate();
  }

}
  • 至此就可以看到我们想要的表单啦

Image text

  • 组件内部我们只需要维护极少数代码就能完成表单的相关操作啦

自定义控件布局

  • 修改模型
import {BaseFormModel, ValidatorRule} from '@supine/dy-form';
import {InputModel} from '../decorator/input.model';

export class LoginModel extends BaseFormModel {
  // 布局容器 我们可以指定自定义类型 type=phone  默认值为LAYOUT_GROUP
  @LayoutGroupModel({type: 'phone'})
  layout;
   
  // parent: 'layout' 指定容器 layout
  @InputModel<LoginModel>({label: '手机号码', parent: 'layout'})
  @ValidatorRule(['required&phoneNum'], {required: '用户名字段是必填的', phoneNum: '请填写正确的手机号码'})
  phone = [null];

  @InputModel<LoginModel>({label: '用户名'})
  @ValidatorRule(['required&max:15&min:4'], {required: '用户名字段是必填的', max: '用户名长度最多为15个字符', min: '用户名长度最少为4个字符'})
  username = [null];

  @InputModel<LoginModel>({label: '密码'})
  @ValidatorRule(['required&max:15&min:4'], {required: '密码字段是必填的', max: '密码长度最多为15个字符', min: '密码长度最少为4个字符'})
  password = [null];

  /**
   * 更新表单模型钩子
   * @param formValue 当表单初始化后 formValue就为表单对象的value 否则为null
   * @param model 注册了的模型配置数组 可以根据某些条件进行过滤 来动态控制表单
   * @param params 调用 executeModelUpdate方法传的参数 以此来更加灵活来动态控制表单
   * @return 如果返回值为void 则渲染所有注册的表单控件 如果返回表单控件数组 则只渲染该数组中的控件模型
   */
  modelUpdateHook(formValue: any, model: FormControlModel[], ...params: any[]): FormControlModel[] | void {
    return model;
  }


  /**
   * 结合我封装的HTTP模块 可轻松实现批量对接与表单相关的接口
   * HTTP模块 目前还没开源
   * 即便不使用我封装的HTTP模块 按照以下模板 也容易实现
   */
  httpRequest() {
    /* .... */
  }
}
  • 接下来修改模板
<!--通用错误提示-->
<ng-template #errorTpl let-control>
  <ng-container *ngIf="control.hasError(control.name)">
    {{control.getError(control.name)}}
  </ng-container>
</ng-template>
<ng-template #label let-model>
  <nz-form-label [nzRequired]="model.required"
                 jdDyFormLabelDef
                 [ngStyle]="model.labelStyle"
                 [nzFor]="model.controlName">{{model.label}}
  </nz-form-label>
</ng-template>

<jd-dy-form-zorro [dyFormRef]="dyFormRef">
<!--  phone 跟模型中的layout字段的元数据type是相对应的-->
<!--  groupModel 包含布局容器内所有字控件的配置信息 比如我要访问容器内phone控件的配置可以这样访问 groupModel.phone-->
<!--  childControl 包含布局容器内所有字控件的formControl 比如我要访问容器内phone控件可以这样访问 childControl.phone-->
  <ng-container *jdDyFormColumnDef="let model = model name 'phone', let groupModel = groupInfo, let childControl = childControl">
    <nz-form-item>
      <ng-template [ngTemplateOutlet]="label" [ngTemplateOutletContext]="{$implicit: groupModel.phone}"></ng-template>
      <nz-form-control
        jdDyFormControlDef
        [nzValidateStatus]="childControl.phone"
        [nzErrorTip]="errorTpl"
      >
        <nz-input-group [nzAddOnBefore]="addOnBeforeTemplate">
          <ng-template #addOnBeforeTemplate>
            <nz-select class="phone-select" [ngModel]="'+86'">
              <nz-option nzLabel="+86" nzValue="+86"></nz-option>
              <nz-option nzLabel="+87" nzValue="+87"></nz-option>
            </nz-select>
          </ng-template>
          <input [formControl]="childControl.phone" id="'phoneNumber'" nz-input/>
        </nz-input-group>
      </nz-form-control>
    </nz-form-item>
  </ng-container>
<!--此处省略布局代码-->
</jd-dy-form-zorro>
  • 预览图如下

Image text

  • 看上去挺多的 但只需要把常见的使用场景封装好了 以后开发就不要写什么模板了

自定义布局

超简单的自定义布局方式-再也不用担心动态表单布局难的问题了

<jd-dy-form [dyFormRef]="dyFormRef" #dyForm>
    <nz-form-item *jdDyFormColumnDef="let control; let model = model name 'INPUT'">
        <!--   省略控件定义部分     -->
    </nz-form-item>
    <nz-form-item *jdDyFormColumnDef="let control; let model = model name 'INPUT_NUMBER'">
        <!--   省略控件定义部分     -->
    </nz-form-item>
    <!--   省略控件定义部分 假设还有很多控件类型     -->
    <jd-form-layout>
        <form nz-form nzLayout="vertical" [formGroup]="dyForm.formArea">
            <div>
                <div>
                    <!--      controlName1 在表单模型中定义好的              -->
                    <ng-container jdDyFormLayoutItemName="controlName1"></ng-container>                
                </div>
            </div>
            <div>
                <div>
                    <!--      controlName2 在表单模型中定义好的              -->
                    <ng-container jdDyFormLayoutItemName="controlName2"></ng-container>                
                </div>
            </div>
            <!--      这个是@supine/dy-form-zorro所用的方式               -->
            <!--<ng-container *ngFor="let control of dyForm.options">
               <ng-container jdDyFormLayoutItemOutlet="{{control.name}}"></ng-container>
            </ng-container>-->
        </form>
    </jd-form-layout>
</jd-dy-form>

到此自定义布局就完成了 轻轻松松布局出任何想要的布局

自定义控件

  • (1)通过内置装饰器实现(更简单, 推荐临时使用的时候用该方式)
    • 修改模型
    import {BaseFormModel, ValidatorRule} from '@supine/dy-form';
    import {InputModel} from '../decorator/input.model';
      
    export class LoginModel extends BaseFormModel {
      // customPro 为自定义模型属性 将作为模板上下文属性 在模板中可以访问到
      // 可以定义多个自定义属性
      // type 默认为CUSTOM 允许修改
      @CustomModel({label: '自定义', type: 'custom', customPro: 'customPro'})
      custom = [null];
    
      @InputModel<LoginModel>({label: '手机号码'})
      @ValidatorRule(['required&phoneNum'], {required: '用户名字段是必填的', phoneNum: '请填写正确的手机号码'})
      phone = [null];
      
      @InputModel<LoginModel>({label: '用户名'})
      @ValidatorRule(['required&max:15&min:4'], {required: '用户名字段是必填的', max: '用户名长度最多为15个字符', min: '用户名长度最少为4个字符'})
      username = [null];
      
      @InputModel<LoginModel>({label: '密码'})
      @ValidatorRule(['required&max:15&min:4'], {required: '密码字段是必填的', max: '密码长度最多为15个字符', min: '密码长度最少为4个字符'})
      password = [null];
      
      /**
       * 更新表单模型钩子
       * @param formValue 当表单初始化后 formValue就为表单对象的value 否则为null
       * @param model 注册了的模型配置数组 可以根据某些条件进行过滤 来动态控制表单
       * @param params 调用 executeModelUpdate方法传的参数 以此来更加灵活来动态控制表单
       * @return 如果返回值为void 则渲染所有注册的表单控件 如果返回表单控件数组 则只渲染该数组中的控件模型
       */
      modelUpdateHook(formValue: any, model: FormControlModel[], ...params: any[]): FormControlModel[] | void {
        return model;
      }
      
      
      /**
       * 结合我封装的HTTP模块 可轻松实现批量对接与表单相关的接口
       * HTTP模块 目前还没开源
       * 即便不使用我封装的HTTP模块 按照以下模板 也容易实现
       */
      httpRequest() {
        /* .... */
      }
    }
    • 接下来修改模板
    <!--通用错误提示-->
    <ng-template #errorTpl let-control>
      <ng-container *ngIf="control.hasError(control.name)">
        {{control.getError(control.name)}}
      </ng-container>
    </ng-template>
    <ng-template #label let-model>
      <nz-form-label [nzRequired]="model.required"
                     jdDyFormLabelDef
                     [ngStyle]="model.labelStyle"
                     [nzFor]="model.controlName">{{model.label}}
      </nz-form-label>
    </ng-template>
      
    <jd-dy-form-zorro [dyFormRef]="dyFormRef">
      <!--  name 'custom' 对应模型中的 type字段值-->
      <ng-container *jdDyFormColumnDef="let control; let model = model name 'custom'">
        <nz-form-item>
          <ng-template [ngTemplateOutlet]="label" [ngTemplateOutletContext]="{$implicit: model}"></ng-template>
          <nz-form-control  jdDyFormControlDef
                            [nzValidateStatus]="control"
                            [nzErrorTip]="errorTpl">
            <!--        customPro 为模型中自定义属性-->
            <input type="text" nz-input [formControl]="control" [placeholder]="model.customPro">
          </nz-form-control>
        </nz-form-item>
      </ng-container>
      <!--此处省略布局代码-->
    </jd-dy-form-zorro>
    • 预览图 Image text
  • (2)通过集成基类模型实现(复用的解决方案, 推荐在封装通用的控件时使用)
    • 基于@supine/dy-form 定制不同Angular UI框架的动态表单 章节其实就是自定义控件

在定义控件模型的时候使用表单模型的上下文

  • 模型
import {BaseFormModel, ValidatorRule} from '@supine/dy-form';
import {InputModel} from '../decorator/input.model';

export class LoginModel extends BaseFormModel {
  sexModel = {
    label: '性别',
    optionContent: [
      {label: '保密', value: 0},
      {label: '男', value: 1},
      {label: '女', value: 2},
    ]
  };

  // 不使用表单模型上下文是这样的
  // @SelectModel<FormModel>({label: '性别', optionContent: [{label: '男', value: 1}, {label: '女', value: 1}]})
  // 使用表单模型上下文是这样的 当控件模型定义比较复杂的时候用这种方法有着很大的优势
  @SelectModel<FormModel>({initHook: (that, context) => Object.assign(that, context.sexModel)})
  sex: [null];

  @InputModel<FormModel>({label: '用户名'})
  @ValidatorRule(['required&max:15&min:4'], {required: '用户名字段是必填的', max: '用户名长度最多为15个字符', min: '用户名长度最少为4个字符'})
  username = [null];

  @InputModel<FormModel>({label: '手机号码'})
  @ValidatorRule(['required&phoneNum'], {required: '用户名字段是必填的', phoneNum: '请填写正确的手机号码'})
  phone = [null];

  @InputModel<LoginModel>({label: '密码'})
  @ValidatorRule(['required&max:15&min:4'], {required: '密码字段是必填的', max: '密码长度最多为15个字符', min: '密码长度最少为4个字符'})
  password = [null];

  /**
   * 更新表单模型钩子
   * @param formValue 当表单初始化后 formValue就为表单对象的value 否则为null
   * @param model 注册了的模型配置数组 可以根据某些条件进行过滤 来动态控制表单
   * @param params 调用 executeModelUpdate方法传的参数 以此来更加灵活来动态控制表单
   * @return 如果返回值为void 则渲染所有注册的表单控件 如果返回表单控件数组 则只渲染该数组中的控件模型
   */
  modelUpdateHook(formValue: any, model: FormControlModel[], ...params: any[]): FormControlModel[] | void {
    return model;
  }


  /**
   * 结合我封装的HTTP模块 可轻松实现批量对接与表单相关的接口
   * HTTP模块 目前还没开源
   * 即便不使用我封装的HTTP模块 按照以下模板 也容易实现
   */
  httpRequest() {
    /* .... */
  }
}
  • 模板
<jd-dy-form-zorro [dyFormRef]="dyFormRef"></jd-dy-form-zorro>
  • 预览图 Image text

自定义验证

  • 自定义同步验证器

    • 模型
    import {BaseFormModel, ValidatorRule} from '@supine/dy-form';
    import {InputModel} from '../decorator/input.model';
      
    export class LoginModel extends BaseFormModel {
      /* .... */
      @InputModel<LoginModel>({label: '用户名'})
      @ValidatorRule(['required&max:15&min:4'], {required: '用户名字段是必填的', max: '用户名长度最多为15个字符', min: '用户名长度最少为4个字符'})
      username = [null, [FormModel.userNameValidator], []];
      
      static userNameValidator = (control: FormControl): ValidationErrors | null => {
           if (control.value === 'JasonWood') {
              // '用户名已存在' 这是用来反馈用户的
              return { userName: '用户名已存在' };
           } else {
              return null
           }
      }
        
      /* .... */
      /**
       * 更新表单模型钩子
       * @param formValue 当表单初始化后 formValue就为表单对象的value 否则为null
       * @param model 注册了的模型配置数组 可以根据某些条件进行过滤 来动态控制表单
       * @param params 调用 executeModelUpdate方法传的参数 以此来更加灵活来动态控制表单
       * @return 如果返回值为void 则渲染所有注册的表单控件 如果返回表单控件数组 则只渲染该数组中的控件模型
       */
      modelUpdateHook(formValue: any, model: FormControlModel[], ...params: any[]): FormControlModel[] | void {
        return model;
      }
      
      
      /**
       * 结合我封装的HTTP模块 可轻松实现批量对接与表单相关的接口
       * HTTP模块 目前还没开源
       * 即便不使用我封装的HTTP模块 按照以下模板 也容易实现
       */
      httpRequest() {
        /* .... */
      }
    }
      
  • 自定义异步验证器

    • 模型
    import {BaseFormModel, ValidatorRule} from '@supine/dy-form';
    import {InputModel} from '../decorator/input.model';
      
    export class LoginModel extends BaseFormModel {
      /* .... */
      @InputModel<LoginModel>({label: '用户名'})
      @ValidatorRule(['required&max:15&min:4'], {required: '用户名字段是必填的', max: '用户名长度最多为15个字符', min: '用户名长度最少为4个字符'})
      username = [null, [], [FormModel.userNameAsyncValidator]];
      
      static userNameAsyncValidator = (control: FormControl) =>
        new Observable((observer: Observer<ValidationErrors | null>) => {
          setTimeout(() => {
            if (control.value === 'JasonWood') {
              // '用户名已存在' 这是用来反馈用户的
              observer.next({ userName: '用户名已存在' });
            } else {
              observer.next(null);
            }
              observer.complete();
            }, 3000);
      })
      /* .... */
      /**
       * 更新表单模型钩子
       * @param formValue 当表单初始化后 formValue就为表单对象的value 否则为null
       * @param model 注册了的模型配置数组 可以根据某些条件进行过滤 来动态控制表单
       * @param params 调用 executeModelUpdate方法传的参数 以此来更加灵活来动态控制表单
       * @return 如果返回值为void 则渲染所有注册的表单控件 如果返回表单控件数组 则只渲染该数组中的控件模型
       */
      modelUpdateHook(formValue: any, model: FormControlModel[], ...params: any[]): FormControlModel[] | void {
        return model;
      }
      
      
      /**
       * 结合我封装的HTTP模块 可轻松实现批量对接与表单相关的接口
       * HTTP模块 目前还没开源
       * 即便不使用我封装的HTTP模块 按照以下模板 也容易实现
       */
      httpRequest() {
        /* .... */
      }
    }
      
    • 预览图 Image text Image text
  • 拓展内置验证器校验规则 - 这种方式复用性强,对复用性有要求的可以用这种方式
    自定义的详细描述请参考 validator

import {DY_FORM_VALIDATOR} from '@supine/dy-form';
@NgModule({
  declarations: [
    /* ... */
  ],
  imports: [
    /* ... */
  ],
  providers: [
    // XValidator这就是基于ZlValidator拓展的验证器
    // 自定义验证规则 在定义验证规则有详细描述
    {provide: DY_FORM_VALIDATOR, useValue: XValidator}
  ],
  bootstrap: [AppComponent]
})
export class AppModule {
}

填充表单

  ngOnInit(): void {
    const {formValue, type} = this.data;
    this.dyFormRef.executeModelUpdate();

    this.dyFormRef.model // model 表单模型实例
      .withAttachValue(formValue || {}) // 将要给表单赋值的数据
      .withActionType(type) // 类型  比如新增或者修改
      .patchValue(); // 当表单初始化后会进行表单填充
  }

动态控制表单模型

modelUpdateHook(不常用,但很有用) - 动态控制表单模型

  • 表单模型
import {
BaseFormModel,
ValidatorRule,
SelectOptionContent,
InputNumberGroupModel,
InputModel
} from '@supine/dy-form';

export class MapUtilFormModel extends BaseFormModel {
  areaTypeContent: SelectOptionContent[] = [
    { label: '卸矿区', value: 'UnloadMineralArea' },
    { label: '装载区', value: 'LoadArea' },
    { label: '道路', value: 'Road' },
    { label: '可通行区域', value: 'PassableArea' },
    { label: '不可通行区域', value: 'ImpassableArea' }
  ];

  _roadType = [
    { label: '主线', value: 0 },
    { label: '支线', value: 1 },
    { label: '连接线', value: 2 }
  ];

  _capacity = [
    { label: '单车道', value: 1 },
    { label: '双车道', value: 2 }
  ];

  // 路口类型
  crossingType = [
    { label: '十字路口', value: 0 },
    { label: '丁字路口', value: 1 }
  ];
  // 排土类型
  _unloadWasteType = [
    { label: '边缘式', value: 'cliff' },
    { label: '定点式', value: 'point' }
  ];
  // 矿物类型
  mineralType = [];

  @SelectModel<MapUtilFormModel>({
    label: '区域类型',
    initHook: (that, context) => that.optionContent = context.areaTypeContent
  })
  @ValidatorRule('required', { required: '请选择区域类型' })
  areaType = [null];

  @InputModel({ label: '名称' })
  @ValidatorRule(['required&maxLength:20'], { maxLength: '最大输入20个字符', required: '请输入单元名称' })
  name = [null];

  // hide: true 隐藏控件
  @InputModel({ label: '区域编号', hide: true })
  areaId = [null];

  @InputNumberGroupModel<MapUtilFormModel>({
    label: '限速',
    addOnAfter: 'km/h',
    max: 100, min: 0,
    precision: 2,
    // 当类型为 ImpassableArea 限速禁用
    initHook: (that, context) => that.disabled = context.actionType === 'ImpassableArea'
  })
  @ValidatorRule(['required&max:100&min:0'], { max: '最大值为100', min: '最小值为0', required: '请输入区域限速' })
  speed = [0];

  @InputNumberGroupModel<MapUtilFormModel>({
    label: '车数量限制',
    addOnAfter: '辆',
    max: 100, min: 0,
    precision: 2,
  })
  @ValidatorRule(['required&max:100&min:0'], { max: '最大值为100', min: '最小值为0', required: '请输入车数量限制' })
  vehicleMax = [10];

  @SelectModel<MapUtilFormModel>({
    label: '装载类型',
    initHook: (that, context) => that.optionContent = context.mineralType
  })
  @ValidatorRule('required', { required: '请选择装载类型' })
  loadType = [null];

  @SelectModel<MapUtilFormModel>({
    label: '排土类型',
    aliasName: 'type',
    initHook: (that, context) => that.optionContent = context._unloadWasteType
  })
  @ValidatorRule('required', { required: '请选择装载类型' })
  unloadWasteType = [null];

  @InputNumberModel({ label: '排土次数上限', addOnAfter: '次' })
  @ValidatorRule(['max:999'], { max: '最大排土次数999' })
  unloadMax = [null];

  @SelectModel<MapUtilFormModel>({
    label: '卸矿类型',
    initHook: (that, context) => that.optionContent = context.mineralType
  })
  @ValidatorRule(['required'], { required: '请选择卸矿类型' })
  unloadType = [null];

  @SelectModel<MapUtilFormModel>({
    label: '道路类型',
    aliasName: 'type',
    initHook: (that, context) => that.optionContent = context._roadType
  })
  @ValidatorRule(['required'], { required: '请选择道路类型' })
  roadType = [null];

  @SelectModel<MapUtilFormModel>({
    label: '容量',
    initHook: (that, context) => that.optionContent = context._capacity
  })
  @ValidatorRule(['required'], { required: '请选择容量' })
  capacity = [null];

  @SelectModel<MapUtilFormModel>({
    label: '路口类型',
    aliasName: 'type', // 控件别名 表单真实的key 也就是获取表单value的时候该控件对应的key是type而不是junctionType
    initHook: (that, context) => that.optionContent = context.crossingType
  })
  @ValidatorRule(['required'], { max: '请选择路口类型' })
  junctionType = [null];
  /**
   * 更新表单模型钩子
   * @param formValue 当表单初始化后 formValue就为表单对象的value 否则为null
   * @param model 注册了的模型配置数组 可以根据某些条件进行过滤 来动态控制表单
   * @param params 调用 executeModelUpdate方法传的参数 以此来更加灵活来动态控制表单
   * @return 如果返回值为void 则渲染所有注册的表单控件 如果返回表单控件数组 则只渲染该数组中的控件模型
   */
  modelUpdateHook(formValue: any, model: FormControlModel[], ...params: any[]): FormControlModel[] | void {
    const typeMap = {
      create: ['areaType', 'speed', 'name'], // actionType==='create' 显示 'areaType', 'speed', 'name'这几个控件 以下以此类推
      ParkingLot: ['speed', 'name', 'areaId'],
      UnloadMineralArea: ['speed', 'name', 'areaId'],
      UnloadWasteArea: ['speed', 'name', 'unloadWasteType', 'unloadMax', 'areaId'],
      // 编号、类型、名称、限速、车数量限制
      LoadArea: ['areaType', 'speed', 'name'/*, 'loadType'*//*, 'areaId'*/, 'vehicleMax'],
      ImpassableArea: ['speed', 'name', 'areaId'],
      PassableArea: ['speed', 'name', 'areaId'],
      Road: ['speed', 'name', 'roadType', 'capacity', 'areaId'],
      Junction: ['speed', 'name', 'junctionType', 'areaId']
    };
    const controls = typeMap[this.actionType];
    return model.filter(value => controls.includes(value.name));
  }


  /**
   * 结合我封装的HTTP模块 可轻松实现批量对接与表单相关的接口
   * HTTP模块 目前还没开源
   * 即便不使用我封装的HTTP模块 按照以下模板 也容易实现
   */
  httpRequest() {
    /* .... */
  }
}
  • 组件
import {Component, OnInit} from '@angular/core';
import {DyFormRef} from '@supine/dy-form';
import {MapUtilFormModel} from './form.model';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit {
  dyFormRef = new DyFormRef(MapUtilFormModel, {mode: 'vertical'});

  ngOnInit(): void {
  }

  constructor() {
    // 附加类型
    this.dyFormRef.model.withActionType('create');
    // 执行这行代码才会渲染  会执行表单模型中的modelUpdateHook
    this.dyFormRef.executeModelUpdate();
  }
}
  • 预览图 Image text Image text