Angular ngx-mat-select-search 自定义组件

Posted

技术标签:

【中文标题】Angular ngx-mat-select-search 自定义组件【英文标题】:Angular ngx-mat-select-search Custom Component 【发布时间】:2019-10-25 09:27:21 【问题描述】:

我正在尝试使用 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: []
); 

我可以很好地查看下拉列表并与之交互,但是当我提交表单时,我无法获得所选选项的值。当我尝试mySearchForm.controls['site'].value时,它总是返回null

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

更新:

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

site-dropdown.component.ts里面,我变了

protected siteCtrl: FormControl;

@Input() siteCtrl: FormControl;

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

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

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

【问题讨论】:

这正是我一直在寻找的,非常感谢!我实现了服务器端搜索,所以我在 init 之后取出了 setInitialValue() 调用,只是传入了初始值,所以我可以让它显示,但是在你点击它之后会搜索,但它工作得很好。跨度> 【参考方案1】:

您可以通过让您的SiteDropdownComponent 实现ControlValueAccessor 接口来获取所选选项的值,如下所示,导致您的SiteDropdownComponent 表现为表单控件并允许使用例如访问该值&lt;app-site-dropdown formControlName="site"&gt;&lt;/app-site-dropdown&gt;:

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

【讨论】:

我已经从 ng-mat-select-search 实现了问题部分给出的解决方案,它正在工作。但是我怎样才能使它对几个选择框通用?为此,我需要像 SITES 一样每次发送具有不同值的数组:[]。我可以为我的选择框使用带有不同数组的 site-dropdown-component.ts 吗? 地区:地区[],国家:国家[] @RahimjonRustamov 您可以传递任何数组 (items: any[]) 甚至更好,使用泛型类型 class SiteDropdownComponent&lt;T&gt; 并在 SiteDropdownComponent 中使用类型 T 而不是 Sites

以上是关于Angular ngx-mat-select-search 自定义组件的主要内容,如果未能解决你的问题,请参考以下文章

找不到模块'angular2/angular2'

Angular 6 和业力'无法加载“@angular-devkit/build-angular”,它未注册'

angular.js 的angular.copy angular.extend angular.merge

如何使用 @angular/upgrade 在 Angular 1 应用程序中使用 Angular 2 组件

Angular 1 和 Angular 2 集成:无缝升级的方法

Angular 6 中的 AuthHttp(从 Angular2 迁移到 Angular6)