I am trying to use the ngx-mat-select-search ponent to put a mat-select style dropdown menu with a search bar in my application.
I have the dropdown working fine, but I am trying to turn it into a custom directive that I can then call and reuse on multiple pages through out the app.
So far I have this: site-dropdown-ponent.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 ponent 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.onDestroyplete();
}
/**
* Sets the initial value after the filteredBanks are loaded initially
*/
protected setInitialValue() {
this.filteredSites
.pipe(take(1), takeUntil(this.onDestroy))
.subscribe(() => {
// setting the pareWith property to a parison 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.singleSelectpareWith = (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'},
];
For the ponent im trying to use it in, i have:
<app-site-dropdown formControlName="site"></app-site-dropdown>
And inside the ponent class I have a form:
this.mySearchForm = this.formBuilder.group( {
site: []
});
I can see and interact with the dropdown just fine, but when i submit my form, I cannot get the value of the selected option. It just always returns null
when i try mySearchForm.controls['site'].value
What am I missing to be able to inject my custom dropdown ponent, and retrieve its value upon form submission?
UPDATE:
I was able to make it work by doing the following:
Inside site-dropdownponent.ts
, I changed
protected siteCtrl: FormControl;
to
@Input() siteCtrl: FormControl;
And inside my html using the custom dropdown, i added:
<app-site-dropdown [siteCtrl]="myForm.get('site')"></app-site-dropdown>
This allowed me to save the selected value into my form on submission.
I am trying to use the ngx-mat-select-search ponent to put a mat-select style dropdown menu with a search bar in my application. https://www.npmjs./package/ngx-mat-select-search
I have the dropdown working fine, but I am trying to turn it into a custom directive that I can then call and reuse on multiple pages through out the app.
So far I have this: site-dropdown-ponent.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 ponent 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.plete();
}
/**
* Sets the initial value after the filteredBanks are loaded initially
*/
protected setInitialValue() {
this.filteredSites
.pipe(take(1), takeUntil(this.onDestroy))
.subscribe(() => {
// setting the pareWith property to a parison 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.pareWith = (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'},
];
For the ponent im trying to use it in, i have:
<app-site-dropdown formControlName="site"></app-site-dropdown>
And inside the ponent class I have a form:
this.mySearchForm = this.formBuilder.group( {
site: []
});
I can see and interact with the dropdown just fine, but when i submit my form, I cannot get the value of the selected option. It just always returns null
when i try mySearchForm.controls['site'].value
What am I missing to be able to inject my custom dropdown ponent, and retrieve its value upon form submission?
UPDATE:
I was able to make it work by doing the following:
Inside site-dropdown.ponent.ts
, I changed
protected siteCtrl: FormControl;
to
@Input() siteCtrl: FormControl;
And inside my html using the custom dropdown, i added:
<app-site-dropdown [siteCtrl]="myForm.get('site')"></app-site-dropdown>
This allowed me to save the selected value into my form on submission.
Share Improve this question edited Jun 11, 2019 at 16:56 Cody Pritchard asked Jun 11, 2019 at 2:06 Cody PritchardCody Pritchard 8972 gold badges19 silver badges37 bronze badges 1- This was just was I was looking to for, so thanks! I implemented the server-side search, so I took out the setInitialValue() call after init and just passed in the initial value so I could have it display, but would then search after you clicked on it, but it's working great. – tclark333 Commented May 1, 2020 at 10:50
1 Answer
Reset to default 2you can get the value of the selected option by having your SiteDropdownComponent
implement the ControlValueAccessor interface as follows, resulting in your SiteDropdownComponent
behaving as a form control and allowing to access the value with e.g. <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 ponent 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 ponent
this.siteCtrl.setValue(value);
}
registerOnChange(fn: Function) {
this.onChange = fn;
}
registerOnTouched(fn: Function) {
this.onTouched = fn;
}
}
See e.g. https://github./bithost-gmbh/ngx-mat-select-search/blob/d7ea78d511bbec45143c58c855f013a44d0d5055/src/app/mat-select-search/mat-select-search.ponent.ts#L134
发布者:admin,转转请注明出处:http://www.yc00.com/questions/1745179690a4615363.html
评论列表(0条)