包装角材料组件

时间:2019-05-07 07:05:47

标签: angular typescript angular-material

我目前正在尝试为Angular Material组件编写包装器/自定义组件,以便它们具有自己的命名空间和一些自定义。我在这里找到了一些人们试图包装单个组件的帖子,我设法对其中的一些组件进行了修改,但是我不确定是否保留了原始组件的100%的功能。

例如,我在this answer之后创建了自己的自动填充组件,并添加了一些缺少的东西。我使用Angular Material Component API作为参考。

这是我的组件当前的外观,其中大部分功能都在其中:

autocomplete.component.html

<mat-form-field>
  <input #input matInput [type]="type" class="form-control" [matAutocomplete]="autocomplete" (input)="valueChanged($event)" [readonly]="readonly"
    (focus)="$event.target.select()" (blur)="onTouched()">
  <mat-autocomplete #autocomplete="matAutocomplete" [autoActiveFirstOption]="autoActiveFirstOption" [disableRipple]="disableRipple" [panelWidth]="panelWidth" [displayWith]="displayFunction" (optionSelected)="onOptionSelected($event)">
    <mat-option *ngFor="let option of filteredOptions" [value]="option">
        {{ displayFunction(option) }}
    </mat-option>
  </mat-autocomplete>
</mat-form-field>

autocomplete.component.ts

import { Component, Input, AfterViewInit, ViewChild, OnChanges, SimpleChanges, forwardRef, Injector, Output, EventEmitter } from '@angular/core';
import { NG_VALUE_ACCESSOR, ControlValueAccessor, NgControl, Validator, AbstractControl, NG_VALIDATORS } from '@angular/forms';
import { MatAutocompleteTrigger, MatInput, ErrorStateMatcher } from '@angular/material';

@Component({
  selector: 'custom-autocomplete',
  templateUrl: './autocomplete.component.html',
  styleUrls: ['./autocomplete.component.scss'],
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => AutocompleteComponent),
      multi: true
    },
    {
      provide: NG_VALIDATORS,
      useExisting: forwardRef(() => AutocompleteComponent),
      multi: true
    }
  ]
})
export class AutocompleteComponent implements AfterViewInit, OnChanges, ControlValueAccessor, Validator {
  // Autocomplete inputs
  @Input() autoActiveFirstOption: boolean = true;
  @Input() classList: string = '';
  @Input() disableRipple: boolean = false;
  @Input() panelWidth: string | number = "1rem";
  @Input() displayFunction: (value: any) => string = this.defaultDisplayFn;
  @Input() filterFunction: (value: any) => any[] = this.defaultFilterFn;
  @Input() options: any[] = [];

  // Autocomplete outputs
  @Output() closed: EventEmitter<void>;
  @Output() opened: EventEmitter<void>;

  // Input inputs
  @Input() readonly = false;
  @Input() type: string = "text";
  @Input() errorStateMatcher: ErrorStateMatcher;

  @ViewChild(MatAutocompleteTrigger) trigger: MatAutocompleteTrigger;
  @ViewChild(MatInput) matInput: MatInput;

  filteredOptions: any[];
  optionSelected = '';
  onChange = (val: any) => {};
  onTouched = () => {};

  constructor(
    private injector: Injector
  ) { }

  ngAfterViewInit() {
    // enables autocomplete by using tab
    this.trigger.panelClosingActions
      .subscribe(
        e => {
          if (this.trigger.activeOption) {
            const value = this.trigger.activeOption.value;
            this.writeValue(value);
            this.onChange(value);
          }
        }
      );

    // this is needed in order for the mat-form-field to be marked as invalid when the control is invalid
    setTimeout(() => {
      this.matInput.ngControl = this.injector.get(NgControl, null);
    });
  }

  ngOnChanges(changes: SimpleChanges) {
    if (changes.options) {
      this.filterOptions(this.optionSelected);
    }
  }

  writeValue(obj: any): void {
    if (obj) {
      this.trigger.writeValue(obj);
      this.optionSelected = obj;
      this.filterOptions(obj);
    }
  }

  registerOnChange(fn: any): void {
    this.onChange = fn;
  }

  registerOnTouched(fn: any): void {
    this.onTouched = fn;
  }

  setDisabledState?(isDisabled: boolean): void {
    this.matInput.disabled = isDisabled;
    this.trigger.setDisabledState(isDisabled);
  }

  validate(c: AbstractControl): { [key: string]: any; } {
    return { 'custom': true };
  }

  valueChanged(event) {
    const value = event.target.value;
    this.optionSelected = value;
    this.onChange(value);
    this.filterOptions(value);
  }

  onOptionSelected(event) {
    const value = event.option.value;
    this.optionSelected = value;
    this.onChange(value);
    this.filterOptions(value);
  }

  filterOptions(value) {
    this.filteredOptions = this.filterFunction(value);
  }

  private defaultFilterFn(value) {
    let name = value;

    if (value && typeof value === 'object') {
      name = value.name;
    }

    return this.options.filter(
      o => o.name.toLowerCase().indexOf(name ? name.toLowerCase() : '') !== -1
    );
  }

  defaultDisplayFn(value) {
    return value ? value.name : value;
  }
}

现在我不确定如何实现其他属性,例如

@Output() closed: EventEmitter<void>;
@Output() opened: EventEmitter<void>;

id: string - Unique ID to be used by autocomplete trigger's "aria-owns" property.

isOpen: boolean - Whether the autocomplete panel is open.

panel: ElementRef - Element for the panel containing the autocomplete options.

showPanel: boolean - Whether the autocomplete panel should be visible, depending on option length.

我需要对每个Angular Material组件执行此操作(这在时间/难度上是现实的吗?)。 任何帮助将不胜感激。

0 个答案:

没有答案