1

多选的当前实现没有显示 <mat-select [formControl]="toppings" multiple>,因为这里对于类型“数组”和“枚举”它显示“复选框”。因此,我通过以下方式覆盖了该行为:

myCustomWidgets = {
    submit: NoneComponent,
    checkboxes: CustomMultiSelectComponent 
  };

我创建了一个 MaterialSelectComponent 文件,它是来自“angular6-json-schema-form”的同一文件的副本,然后添加了如下所示的自定义小部件。

<json-schema-form loadExternalAssets="true"
                      [schema]="formData?.schema"
                      [form]="formData?.form"
                      framework="material-design"
                      [widgets]="myCustomWidgets"
                      (isValid)="isFormValid($event)"
                      (onChanges)="onFormChange($event)"
                      (onSubmit)="onFormSubmit($event)">
    </json-schema-form>

我有 4 个元素,一个文本,一个日期,一个单选和一个多选,如下所示。

    {
    "form": [{
            "type": "section",
            "htmlClass": "row",
            "items": [{
                    "type": "section",
                    "htmlClass": "col-xs-6 item-padding",
                    "items": ["my_text"]
                }, {
                    "type": "section",
                    "htmlClass": "col-xs-6 item-padding",
                    "items": ["my_date"]
                }
            ]
        }, {
            "type": "section",
            "htmlClass": "row",
            "items": [{
                    "type": "section",
                    "htmlClass": "col-xs-6 item-padding",
                    "items": ["my_multi_select"]
                }, {
                    "type": "section",
                    "htmlClass": "col-xs-6 item-padding",
                    "items": ["my_single_select"]
                }
            ]
        }
    ],
    "schema": {
        "schema": "http://json-schema.org/draft-06/schema#",
        "type": "object",
        "title": "Form Details",
        "description": "",
        "properties": {
            "my_multi_select": {
                "titleSource": "my_multi_select",
                "fieldDisplay": "Select More",
                "title": "Select More",
                "type": "array",
                "pattern": null,
                "description": "Multi Select",
                "format": "",
                "required": false,
                "multiple": true,
                "uniqueItems": true,
                "items": {
                    "type": "string",
                    "enum": ["A", "B", "C", "D"]
                },
                "readonly": false
            },
            "my_text": {
                "titleSource": "my_text",
                "fieldDisplay": "My Text",
                "title": "My Text",
                "type": "string",
                "pattern": "",
                "description": "Enter Text",
                "format": "",
                "required": true,
                "readonly": false
            },
            "my_date": {
                "titleSource": "my_date",
                "fieldDisplay": "My Date",
                "title": "My Date",
                "type": "string",
                "pattern": "",
                "description": "Enter Date",
                "format": "date",
                "required": true,
                "readonly": false
            },
            "my_single_select": {
                "titleSource": "my_single_select",
                "fieldDisplay": "My Single Select",
                "title": "My Single Select",
                "type": "string",
                "pattern": "",
                "description": "Enter Date",
                "format": "date",
                "required": true,
                "readonly": false,
                "enum": [
                    "One",
                    "Two",
                    "Three",
                    "Four"
                ]
            }
        },
        "required": ["my_text", "my_date", "my_single_select"]
    },
    "data": {
        "my_text": "",
        "my_date": "",
        "my_single_select": "",
        "my_multi_select" : []
    }
}

现在的问题是它没有捕获方法form-group.functions.ts文件中仅针对“my_multi_select”元素的数据更改事件。对于其余 3 个元素,任何更改都会得到回调并且值会被捕获。我已经在json-schema.form.services.ts下面调试了所有控件都注册订阅的地方。在我的 4 个元素中,多选是“FormArray”类型,其余是“FormControl”。

buildFormGroup() {
    this.formGroup = <FormGroup>buildFormGroup(this.formGroupTemplate);
    if (this.formGroup) {
      this.compileAjvSchema();
      this.validateData(this.formGroup.value);

      // Set up observables to emit data and validation info when form data changes
      if (this.formValueSubscription) { this.formValueSubscription.unsubscribe(); }
      this.formValueSubscription = this.formGroup.valueChanges
        .subscribe(formValue => this.validateData(formValue));
    }
  }

FormArray 类型订阅或事件发射器是否存在已知错误?

我也尝试使用 ViewChild 来获取值,但除了多选之外,我仍然只能获取其他人的值。我仍然不明白,当我在 UI 中选择多个值时它仍然显示在那里,这意味着它存储在某个地方(可能在 controlValue 中)但是为什么无法访问该值(没有 onchange 事件)?

<json-schema-form #myJsonSchema
                          loadExternalAssets="true"
                          [schema]="formData?.schema"
                          [form]="formData?.form"
                          framework="material-design"
                          [widgets]="myCustomWidgets"
                          (isValid)="isFormValid($event)"
                          (onChanges)="onFormChange($event)"
                          (onSubmit)="onFormSubmit($event)">
        </json-schema-form>
4

1 回答 1

0

我团队的一个人解决了这个问题。请参阅下面的组件代码。基本上,这里使用 Checkboxes 代码来发出值。如代码所示,为此使用了selectionChange()方法。我们还遇到了数据绑定的另一个问题,我们已经修复了它增加了价值

import {AbstractControl} from '@angular/forms';
import {buildTitleMap, isArray, JsonSchemaFormService} from 'angular6-json-schema-form';
import {Component, Inject, Input, OnInit, Optional} from '@angular/core';
import {MAT_LABEL_GLOBAL_OPTIONS} from '@angular/material/core';
import {MAT_FORM_FIELD_DEFAULT_OPTIONS} from '@angular/material/form-field';

@Component({
  // tslint:disable-next-line:component-selector
  selector: 'material-select-widget',
  template: `
    <mat-form-field
      [appearance]="options?.appearance || matFormFieldDefaultOptions?.appearance || 'standard'"
      [class]="options?.htmlClass || ''"
      [floatLabel]="options?.floatLabel || matLabelGlobalOptions?.float || (options?.notitle ? 'never' : 'auto')"
      [hideRequiredMarker]="options?.hideRequired ? 'true' : 'false'"
      [style.width]="'100%'">
      <mat-label *ngIf="!options?.notitle">{{options?.title}}</mat-label>
      <span matPrefix *ngIf="options?.prefix || options?.fieldAddonLeft"
            [innerHTML]="options?.prefix || options?.fieldAddonLeft"></span>
      <mat-select *ngIf="boundControl"
                  [attr.aria-describedby]="'control' + layoutNode?._id + 'Status'"
                  [attr.name]="controlName"
                  [id]="'control' + layoutNode?._id"
                  [multiple]="options?.multiple"
                  [placeholder]="options?.notitle ? options?.placeholder : options?.title"
                  [required]="options?.required"
                  [style.width]="'100%'"
                  [(value)]="selectedValues"
                  (selectionChange)="selectionChange($event)"
                  (blur)="options.showErrors = true">
        <ng-template ngFor let-selectItem [ngForOf]="selectList">
          <mat-option *ngIf="!isArray(selectItem?.items)"
                      [value]="selectItem?.value">
            <span [innerHTML]="selectItem?.name"></span>
          </mat-option>
          <mat-optgroup *ngIf="isArray(selectItem?.items)"
                        [label]="selectItem?.group">
            <mat-option *ngFor="let subItem of selectItem.items"
                        [value]="subItem?.value">
              <span [innerHTML]="subItem?.name"></span>
            </mat-option>
          </mat-optgroup>
        </ng-template>
      </mat-select>
      <mat-select *ngIf="!boundControl"
                  [attr.aria-describedby]="'control' + layoutNode?._id + 'Status'"
                  [attr.name]="controlName"
                  [disabled]="controlDisabled || options?.readonly"
                  [id]="'control' + layoutNode?._id"
                  [multiple]="options?.multiple"
                  [placeholder]="options?.notitle ? options?.placeholder : options?.title"
                  [required]="options?.required"
                  [style.width]="'100%'"
                  [value]="controlValue"
                  (blur)="options.showErrors = true"
                  (selectionChange)="selectionChange($event)">
        <ng-template ngFor let-selectItem [ngForOf]="selectList">
          <mat-option *ngIf="!isArray(selectItem?.items)"
                      [attr.selected]="selectItem?.value === controlValue"
                      [value]="selectItem?.value">
            <span [innerHTML]="selectItem?.name"></span>
          </mat-option>
          <mat-optgroup *ngIf="isArray(selectItem?.items)"
                        [label]="selectItem?.group">
            <mat-option *ngFor="let subItem of selectItem.items"
                        [attr.selected]="subItem?.value === controlValue"
                        [value]="subItem?.value">
              <span [innerHTML]="subItem?.name"></span>
            </mat-option>
          </mat-optgroup>
        </ng-template>
      </mat-select>
      <span matSuffix *ngIf="options?.suffix || options?.fieldAddonRight"
            [innerHTML]="options?.suffix || options?.fieldAddonRight"></span>
      <mat-hint *ngIf="options?.description && (!options?.showErrors || !options?.errorMessage)"
                align="end" [innerHTML]="options?.description"></mat-hint>
    </mat-form-field>
    <mat-error *ngIf="options?.showErrors && options?.errorMessage"
               [innerHTML]="options?.errorMessage"></mat-error>`,
  styles: [`
    mat-error {
      font-size: 75%;
      margin-top: -1rem;
      margin-bottom: 0.5rem;
    }

    ::ng-deep json-schema-form mat-form-field .mat-form-field-wrapper .mat-form-field-flex
    .mat-form-field-infix {
      width: initial;
    }
  `],
})
export class CustomMultiSelectComponent implements OnInit {
  formControl: AbstractControl;
  controlName: string;
  controlValue: any;
  controlDisabled = false;
  boundControl = false;
  options: any;
  selectList: any[] = [];
  isArray = isArray;
  @Input() layoutNode: any;
  @Input() layoutIndex: number[];
  @Input() dataIndex: number[];

  selectedValues = []

  constructor(
    @Inject(MAT_FORM_FIELD_DEFAULT_OPTIONS) @Optional() public matFormFieldDefaultOptions,
    @Inject(MAT_LABEL_GLOBAL_OPTIONS) @Optional() public matLabelGlobalOptions,
    private jsf: JsonSchemaFormService
  ) {
  }

  ngOnInit() {
    this.options = this.layoutNode.options || {};

    this.selectList = buildTitleMap(
      this.options.titleMap || this.options.enumNames,
      this.options.enum, !!this.options.required, !!this.options.flatList
    );

    this.jsf.initializeControl(this, !this.options.readonly);
    if (!this.options.notitle && !this.options.description && this.options.placeholder) {
      this.options.description = this.options.placeholder;
    }
    if (this.boundControl) {
      const formArray = this.jsf.getFormControl(this);
      for (const selectItem of this.selectList) {
        selectItem.checked = formArray.value.includes(selectItem.value);
      }
    }

    this.selectedValues = this.selectList.filter(item => item.checked)
                                         .map(item => item.value);
  }

  selectionChange(event) {
    this.options.showErrors = true;
    this.selectList.map(item => item.checked = this.selectedValues.includes(item.value));
    if (this.boundControl) {
      this.jsf.updateArrayCheckboxList(this, this.selectList);
    }
  }

}

我希望这对你们有帮助。任何人都可以使用此代码。我将在 angular6-json-schema-form 项目中添加一个 PR。

于 2019-10-16T17:53:14.197 回答