带有多选表单更改事件的angular6-json-schema-form问题 [英] angular6-json-schema-form issue with multi-select form change event

查看:85
本文介绍了带有多选表单更改事件的angular6-json-schema-form问题的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

当前多次选择的实现未显示<mat-select [formControl]="toppings" multiple>,因为

The current implementation for multi-select doesn't show <mat-select [formControl]="toppings" multiple> because here for type 'array' and 'enum' it shows 'checkboxes'. So, I have overridden that behavior in the following way:

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

我已经创建了MaterialSelectComponent文件,该文件是"angular6-json-schema-form"中同一文件的副本,然后添加了如下自定义小部件.

I have created a MaterialSelectComponent file which is a copy of the same file from 'angular6-json-schema-form' and then added the custom widget like below.

<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个元素,一个文本,一个日期,一个单选和一个多选,如下所示.

I have 4 elements, one text, one date, one single select, and one multi-select like below.

    {
    "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" : []
    }
}

现在的问题是,它不只为该"my_multi_select"元素捕获方法 form-group.functions.ts 文件中的数据更改事件.对于这3个元素的其余部分,任何更改都将得到回调并捕获值.我在 json-schema.form.services.ts 下面进行了调试,所有控件都已注册为订阅.在我的4个元素中,多选类型为"FormArray",其余为"FormControl".

Now the issue is it's not capturing the data change event in method form-group.functions.ts file only for that "my_multi_select" element. For the rest of the 3 elements any change is getting a callback and the values are getting captured. I have debugged here below json-schema.form.services.ts where all the controls are getting registered for subscription. In my 4 elements, multi-select is of type "FormArray" and rest are "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类型订阅或事件发射器是否存在已知错误?

Is there a known bug with FormArray type subscription or event emitter?

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

I have also tried to use ViewChild to get the values, but I still only get the values of others except that Multi-Select. I still don't understand that in the UI when I select multiple values it still shows there, which means it's stored somewhere (may be in controlValue) but why there is no way to access that value (without onchange event)?

<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>

推荐答案

我团队中的一个人解决了此问题.请参见下面的组件代码. 基本上,这里使用Checkboxes代码来发出值.如代码所示,使用了 selectionChange()方法.我们在数据绑定方面也遇到了另一个问题,已通过添加 value

One guy from my team fixed this issue. Please see below the component code. Basically the Checkboxes code was used here to emit the values. selectionChange() method was used for this as shown in the code. We had faced another issue with the data binding as well which we had fixed it adding value

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.

I hope this helps you guys. Anyone can use this code. I am going to add a PR to the angular6-json-schema-form project.

这篇关于带有多选表单更改事件的angular6-json-schema-form问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆