角度ngx-mat-select-search自定义组件

时间:2019-06-11 02:06:23

标签: javascript angular typescript components

我正在尝试使用ngx-mat-select-search组件在应用程序中放置带有搜索栏的mat-select样式下拉菜单。 https://www.npmjs.com/package/ngx-mat-select-search

我的下拉菜单工作正常,但是我试图将其转换为自定义指令,然后可以在应用程序中的多个页面上调用和重用该指令。

到目前为止,我有这个:site-dropdown-component.ts

import {AfterViewInit, Component, OnDestroy, OnInit, ViewChild} from '@angular/core';
import {FormControl} from '@angular/forms';
import {ReplaySubject, Subject} from 'rxjs';
import {MatSelect} from '@angular/material';
import {take, takeUntil} from 'rxjs/operators';

@Component({
  selector: 'app-site-dropdown',
  template: `
    <mat-form-field class="w-100">
      <mat-select [formControl]="siteCtrl" placeholder="Site" #singleSelect>
        <mat-option>
          <ngx-mat-select-search [formControl]="siteFilterCtrl" [placeholderLabel]="'Search Sites...'"></ngx-mat-select-search>
        </mat-option>
        <mat-option *ngFor="let site of filteredSites | async" [value]="site">{{site.name}}</mat-option>
      </mat-select>
    </mat-form-field>
  `
})
export class SiteDropdownComponent implements OnInit, OnDestroy, AfterViewInit {
  /** list of sites */
  protected sites: Site[] = SITES;

  /** control for the selected site */
  public siteCtrl: FormControl = new FormControl();

  /** control for the MatSelect filter keyword */
  public siteFilterCtrl: FormControl = new FormControl();

  /** list of sites filtered by search keyword */
  public filteredSites: ReplaySubject<Site[]> = new ReplaySubject<Site[]>(1);

  @ViewChild('singleSelect') singleSelect: MatSelect;

  /** Subject that emits when the component has been destroyed. */
  protected onDestroy = new Subject<void>();
  constructor() { }

  ngOnInit(): void {
    // set initial selection
    this.siteCtrl.setValue(this.sites);
    // load the initial site list
    this.filteredSites.next(this.sites.slice());
    // listen for search field value changes
    this.siteFilterCtrl.valueChanges
      .pipe(takeUntil(this.onDestroy))
      .subscribe(() => {
        this.filterSites();
      });
  }

  ngAfterViewInit(): void {
    this.setInitialValue();
  }

  ngOnDestroy(): void {
    this.onDestroy.next();
    this.onDestroy.complete();
  }

  /**
   * Sets the initial value after the filteredBanks are loaded initially
   */
  protected setInitialValue() {
    this.filteredSites
      .pipe(take(1), takeUntil(this.onDestroy))
      .subscribe(() => {
        // setting the compareWith property to a comparison function
        // triggers initializing the selection according to the initial value of
        // the form control (i.e. _initializeSelection())
        // this needs to be done after the filteredBanks are loaded initially
        // and after the mat-option elements are available
        this.singleSelect.compareWith = (a: Site, b: Site) => a && b && a.id === b.id;
      });
  }

  protected filterSites() {
    if (!this.sites) {
      return;
    }
    // get the search keyword
    let search = this.siteFilterCtrl.value;
    if (!search) {
      this.filteredSites.next(this.sites.slice());
      return;
    } else {
      search = search.toLowerCase();
    }
    // filter the sites
    this.filteredSites.next(
      this.sites.filter(site => site.name.toLowerCase().indexOf(search) > -1)
    );
  }
}


export interface Site {
  id: string;
  name: string;
}

export const SITES: Site[] = [
  {id: 'site1', name: 'Site 1'},
  {id: 'site2', name: 'Site 2'},
  {id: 'site3', name: 'Site 3'},
];

对于试图在其中使用该组件的组件,我有:

<app-site-dropdown formControlName="site"></app-site-dropdown>

在组件类内部,我有一个表单:

this.mySearchForm = this.formBuilder.group( {
  site: []
}); 

我可以看到下拉菜单并与之交互,但是,当我提交表单时,无法获得所选选项的值。当我尝试null

时,它总是返回mySearchForm.controls['site'].value

我缺少什么能够插入自定义下拉菜单组件并在提交表单时检索其值?

更新:

我能够通过执行以下操作使其工作:

site-dropdown.component.ts内部,我更改了

protected siteCtrl: FormControl;

@Input() siteCtrl: FormControl;

在我的html中使用自定义下拉菜单,我添加了:

<app-site-dropdown [siteCtrl]="myForm.get('site')"></app-site-dropdown>

这使我可以在提交时将所选值保存到表单中。

1 个答案:

答案 0 :(得分:2)

您可以通过使SiteDropdownComponent如下实现ControlValueAccessor接口来获得所选选项的值,从而使SiteDropdownComponent成为表单控件并允许访问该值与例如<app-site-dropdown formControlName="site"></app-site-dropdown>

...
import { forwardRef } from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';

@Component({
  selector: 'app-site-dropdown',
  template: ...
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => SiteDropdownComponent),
      multi: true
    }
  ],
})
export class SiteDropdownComponent implements OnInit, OnDestroy, AfterViewInit, ControlValueAccessor {
  ...

  onChange: Function = (_: any) => {};
  onTouched: Function = (_: any) => {};

  constructor() { }

  ngOnInit() {
    ...
    // call this.onChange to notify the parent component that the value has changed
    this.siteCtrl.valueChanges
      .pipe(takeUntil(this.onDestroy))
      .subscribe(value => this.onChange(value))
  }

  writeValue(value: string) {
    // set the value of siteCtrl when the value is set from outside the component
    this.siteCtrl.setValue(value);
  }

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

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

}

例如参见https://github.com/bithost-gmbh/ngx-mat-select-search/blob/d7ea78d511bbec45143c58c855f013a44d0d5055/src/app/mat-select-search/mat-select-search.component.ts#L134

相关问题