Angular2:自定义选择组件和反应形式

时间:2018-10-27 17:17:50

标签: angular typescript angular5 angular2-forms

我在使用反应式表单和创建自定义选择组件时遇到麻烦。

我需要创建一些自定义选择组件。

我已经查看了关于Stackoverflow的多个答案,其中涉及提供“ ControlValueAccessor”的实现。那些看起来像他们会工作,但对于我需要的东西却非常重要。

我也一直在尝试扩展“ SelectControlValueAccessor”,但这似乎不是很平常的事情。如果不常见,我会问这是否是解决问题的正确方法。

基本上,我需要一个自定义的select组件,该组件会自动进行服务调用并使用反应式表单。

这与我要执行的操作类似:

@Component({
    selector: 'customer-select',
    styleUrls: ['./customer-select.component.css'],
    templateUrl: './customer-select.component.html'
})
export class CustomerSelectComponent extends SelectControlValueAccess implements OnInit {
    customers: ICustomer[];

    constructor(
        private render: Renderer2,
        private elementRef: ElementRef,
        private customerService: CustomerService,
    ) {
        super(render, elementRef);
    }

    ngOnInit(): void {
        this.customerService.getCustomers()
           .subscribe((response: IApiResponse<ICustomer[]>) => {
                    this.customers = response.Data;
                   this.customers.sort(this.getFuncToSortMostUsedToDefaultOrdering());

                   // additional logic goes here 
                },
               (err: any) => console.log(err),
                () => console.log('getCustomers() retrieved workflows')
           );
    }

    private getCompareToStrings(firstEl: string, secondEl: string) {
        if (firstEl < secondEl) {
            return -1;
        }
        if (firstEl > secondEl) {
            return 1;
        }
        return 0;
    }

    private getFuncToSortMostUsedToDefaultOrdering() {
        // Assuming that we have two customers.
        return (firstElement: ICustomer, secondElement: ICustomer) => {
            return SomeLogicHere.Compare(firstElement, secondElement)
    }

}

这是HTML代码:

<!-- Need the formControlName somehow passed in ---> 
<select id="customer" class="form-control" formControlName="customer">
    <option *ngFor="let customer of customers" [ngValue]="customer">
        {{customer.CustomerNumber}}
    </option>
</select>

请随时提及我可能会遗漏的任何细节。也许是我忽略的问题或设计讨论。

也许我可以在继承上使用合成,并在仍然实现“ ControlValueAccessor”的同时编写“ SelectControlValueAccess”?

任何不涉及太多麻烦的琐碎解决方案的小问题?看起来对于此类琐碎的事情,其余解决方案是如此复杂。

编辑:之所以这样做,是因为此“客户选择”将在应用程序的很多地方使用。

此外,我将不得不为其他5个选择执行此操作,这就是为什么我不喜欢那么多琐碎的代码的原因。

编辑:

我认为,如果有人对此代码有任何输入(也许我忽略了某些内容),那么该代码就可以正常工作,请分享:永远不要破碎

@Component({
    selector: 'customer-select',
    templateUrl: './customer-select.component.html',
    providers: [
        {
            provide: NG_VALUE_ACCESSOR,
            useExisting: forwardRef(() => CustomerSelectComponent),
            multi: true
        }
    ]
})
export class CustomerSelectComponent extends SelectControlValueAccessor implements OnInit {
    customers: ICustomer[];

    constructor(
        private render: Renderer2, 
        private elementRef: ElementRef,
        private dataService: DataService,
        private fb: FormBuilder
    ) {
        super(render, elementRef);
    }

    ngOnInit(): void {
        this.dataService.getCustomers()
            .subscribe((response: IApiResponse<ICustomer[]>) => {
                    this.customers = response.Data;
                    // Additional Logic
                },
                (err: any) => console.log(err),
                () => console.log('getCustomers() retrieved workflows')
            );
    }
}

HTML:

<select id="customer" class="form-control">
    <option *ngFor="let customer of customers" [ngValue]="customer">
        {{customer.CustomerNumber}}
    </option>
</select>

1 个答案:

答案 0 :(得分:0)

我们在Reactive Form Control中为Bootstrap 4创建了一个自定义选择组件,该组件实现了ControlValueAccessors。这是代码,请根据您的需要进行修改。

组件:

import {
  Component,
  OnInit,
  Input,
  Output,
  EventEmitter,
  SimpleChanges,
  forwardRef,
  ElementRef,
  ViewChild,
  OnChanges
} from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';

@Component({
  selector: 'input-customselectcontrol',
  templateUrl: '...',
  styleUrls: ['......'],
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      multi: true,
      useExisting: forwardRef(() => SelectFormControlComponent)
    }
  ]
})
export class SelectCustomFormControlComponent implements OnInit, ControlValueAccessor {
  @Output() dropdownEventUpdate: EventEmitter<any> = new EventEmitter();
  public selectedDropDownValue = '';
  @Input() dropDownListArray: Array<any>;

  constructor() {}

  ngOnInit() {}

  writeValue(value: any) {
    if (value) {
      const matchObj = _.find(this.dropDownListArray, function(o) {
          return o.text === value;
        });
        this.selectedDropDownValue = matchObj && matchObj.text ? matchObj.text : '';
      }else {
      this.selectedDropDownValue = 'Select';
    }
  }

  propagateChange(time: any) {
    console.log(time);
  }

  registerOnChange(fn) {
    this.propagateChange = fn;
  }

  registerOnTouched() {}

  // tslint:disable-next-line:use-life-cycle-interface
  ngOnChanges(changes: SimpleChanges) {

  }

  onDropDownChange = function(id, value) {
    this.selectedDropDownValue = value;
    this.dropdownEventUpdate.emit({
      id: id,
      text: value
    });
  };
}

模板:

<div ngbDropdown class="d-inline-block custom-btn-color col-md px-0 w-100" #inputDropdown="ngbDropdown">
  <button class="btn btn-outline-primary custom-height px-2 dropdown-custom w-100 custom-drop-down-text-override pr-4 text-left" [ngClass]="{'input-errorCls': isRequiredError}" id="sortMenu" ngbDropdownToggle>{{selectedDropDownValue}}</button>
  <div ngbDropdownMenu aria-labelledby=" sortMenu" class="w-100">
    <button class="dropdown-item px-3 custom-drop-down-text-override" *ngFor="let value of dropDownListArray" (click)="onDropDownChange(value.id, value.text);$event.stopPropagation();">{{value.text}}</button>
  </div>
</div>

呼叫模板代码:

<input-customselectcontrol (dropdownEventUpdate)="updateTopicDropdownEvent($event, i)" [dropDownListArray]="TopicsArray"  name="selectdropdownvalue" formControlName="selectdropdownvalue"></input-customselectcontrol>