Topic: Example of two autocompleters on the same page
Jordan free asked 5 years ago
I am having trouble getting 2 auto-completers to work independently on the same form. I used separate methods and variables for them both but when I select an option for the first autocomplete, that selection seems to be used as filter criteria that is applied to the second auto complete. Should I have two instances of " @ViewChild(MdbAutoCompleterComponent) completer: MdbAutoCompleterComponent;"?
Also, I am having issue getting the selected value committed to the form model. For example:
I would type "Acco" and then select the "Accounting" option. However, when the form is saved, only the value ("Acco" ) I explicitly typed is saved.
TS:
import { Component, ViewChild, AfterViewInit, Inject, LOCALE_ID } from "@angular/core";
import { formatDate } from "@angular/common";
import { ListItem, DataGroup, Client } from "../../api-client/app.generated";
import { MdbTableEditorDirective } from "mdb-table-editor";
import { MdbAutoCompleterComponent } from "ng-uikit-pro-standard"
import {
FormGroup, FormControl, Validators
//, ReactiveFormsModule
} from "@angular/forms";
import { of } from "rxjs";
@Component({
selector: "app-data-groups-component",
templateUrl: "./data-groups.component.html"
})
export class DataGroupsComponent implements AfterViewInit {
@ViewChild("table") mdbTableEditor: MdbTableEditorDirective;
@ViewChild(MdbAutoCompleterComponent) completer: MdbAutoCompleterComponent;
public availableAmsRoles = [""];
public searchAmsRolesTextForMember = "";
public amsRolesResultsForMember: any;
public searchAmsRolesTextForAdmin = "";
public amsRolesResultsForAdmin: any;
constructor(private apiClient: Client
, @Inject(LOCALE_ID) private locale: string) {
this.dataGroupForm = this.createForm();
this.apiClient.amsUserRoles_GetAmsUserRoles()
.toPromise()
.then((result: string[]) => {
this.availableAmsRoles = result;
});
this.amsRolesResultsForMember = this.searchAmsRolesForMember(this.searchAmsRolesTextForMember);
this.amsRolesResultsForAdmin = this.searchAmsRolesForAdmin(this.searchAmsRolesTextForAdmin);
}
public ngAfterViewInit() {
this.completer.selectedItemChanged().subscribe((data: any) => {
this.searchAmsRolesTextForMember = data.text;
this.getFilteredAmsRolesForMember();
this.searchAmsRolesTextForAdmin = data.text;
this.getFilteredAmsRolesForAdmin();
});
}
public searchAmsRolesForAdmin(term: string) {
return of(this.availableAmsRoles.filter((data: any) => data.toString().toLowerCase().includes(term.toString().toLowerCase())));
}
public searchAmsRolesForMember(term: string) {
return of(this.availableAmsRoles.filter((data: any) => data.toString().toLowerCase().includes(term.toString().toLowerCase())));
}
public getFilteredAmsRolesForMember() {
this.amsRolesResultsForMember = this.searchAmsRolesForMember(this.searchAmsRolesTextForMember);
}
public getFilteredAmsRolesForAdmin() {
this.amsRolesResultsForAdmin = this.searchAmsRolesForAdmin(this.searchAmsRolesTextForAdmin);
}
public onMemberChange() {
this.getFilteredAmsRolesForMember();
}
public onAdminChange() {
this.getFilteredAmsRolesForAdmin();
}
public onAdminSelect(role: string) {
console.log("onAdminSelect");
console.log(role);
this.dataGroupForm.controls["amsAdminRole"].patchValue(role);
this.dataGroupForm.controls["amsAdminRole"].updateValueAndValidity();
}
public onMemberSelect(role: string) {
console.log("onMemberSelect");
console.log(role);
this.dataGroupForm.controls["amsMemberRole"].patchValue(role);
this.dataGroupForm.controls["amsMemberRole"].updateValueAndValidity();
}
protected createForm(): FormGroup {
return new FormGroup({
amsAdminRole: new FormControl(null, Validators.required),
amsMemberRole: new FormControl(null, Validators.required),
});
}
}
HTML:
<div class="md-form input-group mb-5">
<input mdbValidate
formControlName="amsAdminRole"
type="text"
class="completer-input form-control mdb-autocomplete mb-0"
(keydown)="searchAmsRolesTextForAdmin = $event.target.value"
(input)="getFilteredAmsRolesForAdmin()"
(ngModelChange)="onAdminChange()"
[mdbAutoCompleter]="autoAdmin"
placeholder="Enter the AMS function for admin access">
<mdb-auto-completer #autoAdmin="mdbAutoCompleter" textNoResults="No roles match your criteria...">
<mdb-option *ngFor="let option of amsRolesResultsForAdmin | async"
[value]="option"
(select)="onAdminSelect(option)">
{{option}}
</mdb-option>
</mdb-auto-completer>
<mdb-error *ngIf="amsAdminRole.invalid && (amsAdminRole.dirty || amsAdminRole.touched)">
Please specify an AMS role
</mdb-error>
</div>
<div class="md-form input-group mb-5">
<input mdbValidate
formControlName="amsMemberRole"
type="text"
class="completer-input form-control mdb-autocomplete mb-0"
(keydown)="searchAmsRolesTextForMember = $event.target.value"
(input)="getFilteredAmsRolesForMember()"
(ngModelChange)="onMemberChange()"
[mdbAutoCompleter]="autoMember"
placeholder="Enter the AMS function for member access">
<mdb-auto-completer #autoMember="mdbAutoCompleter" textNoResults="No roles match your criteria...">
<mdb-option *ngFor="let option of amsRolesResultsForMember | async"
[value]="option"
(select)="onMemberSelect(option)">
{{option}}
</mdb-option>
</mdb-auto-completer>
<mdb-error *ngIf="amsMemberRole.invalid && (amsMemberRole.dirty || amsMemberRole.touched)">
Please specify an AMS role
</mdb-error>
</div>
</form>
Damian Gemza staff answered 5 years ago
Dear @Jordan
Please take a look at the below code. There's separated methods and fields for every mdb-auto-completer
component. Also, I have changed the @ViewChild
to target by template reference (#).
.html:
<div class="container">
<div class="row">
<div class="col-md-6 mx-auto my-5">
<form>
<div class="md-form">
<input type="text" class="completer-input form-control mdb-autocomplete"
[(ngModel)]="searchText"
name="completer1"
(input)="getFilteredData()" (ngModelChange)="onChange()"
[mdbAutoCompleter]="auto"
placeholder="Choose your color">
<mdb-auto-completer #completer1 #auto="mdbAutoCompleter" textNoResults="I have found no results :(">
<mdb-option *ngFor="let option of results | async" [value]="option">
{{option}}
</mdb-option>
</mdb-auto-completer>
</div>
<div class="md-form">
<input type="text" class="completer-input form-control mdb-autocomplete"
[(ngModel)]="searchText2"
name="completer2"
(input)="getFilteredData2()" (ngModelChange)="onChange2()"
[mdbAutoCompleter]="auto2"
placeholder="Choose your number">
<mdb-auto-completer #completer2 #auto2="mdbAutoCompleter" textNoResults="I have found no results :(">
<mdb-option *ngFor="let option of results2 | async" [value]="option">
{{option}}
</mdb-option>
</mdb-auto-completer>
</div>
</form>
</div>
</div>
</div>
ts:
import {Component, ViewChild} from '@angular/core';
import {of} from "rxjs";
import {MdbAutoCompleterComponent} from "ng-uikit-pro-standard";
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent {
@ViewChild('completer1') completer: MdbAutoCompleterComponent;
@ViewChild('completer2') completer2: MdbAutoCompleterComponent;
searchText = '';
searchText2 = '';
results: any;
results2: any;
data: any = ['red', 'green', 'blue', 'cyan', 'magenta', 'yellow', 'black'];
data2: any = [1, 2, 3, 4, 5, 6, 7];
constructor() {
this.results = this.searchEntries(this.searchText);
this.results2 = this.searchEntries2(this.searchText2);
}
getDataItems() {
return this.data;
}
getDataItems2() {
return this.data2;
}
searchEntries(term: string) {
return of(this.getDataItems().filter((data: any) => data.toString().toLowerCase().includes(term.toString().toLowerCase())));
}
searchEntries2(term: string) {
return of(this.getDataItems2().filter((data: any) => data.toString().toLowerCase().includes(term.toString().toLowerCase())));
}
getFilteredData() {
this.results = this.searchEntries(this.searchText);
}
getFilteredData2() {
this.results2 = this.searchEntries2(this.searchText2);
}
onChange() {
this.getFilteredData();
}
onChange2() {
this.getFilteredData2();
}
ngAfterViewInit() {
this.completer.selectedItemChanged().subscribe((data: any) => {
this.searchText = data.text;
this.getFilteredData();
});
this.completer2.selectedItemChanged().subscribe((data: any) => {
this.searchText2 = data.text;
this.getFilteredData2();
});
}
}
Best Regards,
Damian
Jordan free commented 5 years ago
Perfect. That was what I was looking for.
Also note that in order for the selected value to recognized/valid, I had to add the patchvalue method for the control.
this.memberCompleter.selectedItemChanged().subscribe((memberCompleterData: any) => {
console.log("onMemberSelect");
console.log(memberCompleterData);
this.searchAmsRolesTextForMember = memberCompleterData.text;
this.getFilteredAmsRolesForMember();
this.dataGroupForm.controls['amsMemberRole'].patchValue(memberCompleterData.text);
this.dataGroupForm.controls['amsMemberRole'].updateValueAndValidity();
});
Damian Gemza staff answered 5 years ago
Dear @Jordan
That's quite easy. In the @ViewChild()
we're not targetting the mdb-auto-completer
by template reference (# sign), but by class name (MdbAutoCompleterComponent
).
With this approach, we're able to use the instance of the mdb-auto-completer
component without using template reference.
If something isn't clear for you, feel free to ask!
Best Regards,
Damian
Jordan free commented 5 years ago
But that still doesn't give me an understanding of what I need to do.Do I need one or two instances of @ViewChild(MdbAutoCompleterComponent) completer: MdbAutoCompleterComponent;?How will the subscription to the completer components work?Can you send me code example?
Jordan free commented 5 years ago
Apologies... but I have limited experience with Angular.
Arkadiusz Idzikowski staff answered 5 years ago
All variables should be unique for every mdb-autocompleter component. Please try to use different template reference variables and ViewChild declarations or wrap the autocompleters in your custom components so they don't have access to the same variables.
Jordan free commented 5 years ago
I dont understand how the completer declared in the TS is bound to the html component. Your example (https://mdbootstrap.com/docs/angular/forms/autocomplete/#preselect-reactive) uses: this.completer. But that is not referenced in the HTML (at least by name).
FREE CONSULTATION
Hire our experts to build a dedicated project. We'll analyze your business requirements, for free.
Resolved
- ForumUser: Free
- Premium support: No
- Technology: MDB Angular
- MDB Version: 7.5.0
- Device: PC
- Browser: Chrome
- OS: Windows 10
- Provided sample code: No
- Provided link: No