Ngx-bootstrap: feat(typeahead): support multiple search field values

Created on 19 Dec 2016  路  18Comments  路  Source: valor-software/ngx-bootstrap

I am trying to use typeahead in a scenario where we need to filter the search result based on multiple fields. Lets say we have an object like this

[{
id: 1
name: "Stockholm",
country: "Sweden"
}, {
id: 1
name: "Gothenburg",
country: "Sweden"
}]

Then if I search on "Stock" I want the Stockholm object as result. If I search for "Swe" I want both Stockholm and Gothenburg. The matching function should therefore look for a match on both the name and country property.

WIP comp(typeahead) enhancement

Most helpful comment

@valorkin This doesn't feel like a concern of typeahead. Instead @Abrissirba you should write a service that does this. This is based on the asynchronous example in the documentation.

public getCitiesAsObservable(token:string):Observable<any> { return Observable.of( this.citiesComplex.filter((city:any) => { return city.name.startsWith(token) || city.country.startsWith(token); }) ); }

I use this technique searching for people when matching on first and last name.

All 18 comments

multi select is planned feature

@valorkin This doesn't feel like a concern of typeahead. Instead @Abrissirba you should write a service that does this. This is based on the asynchronous example in the documentation.

public getCitiesAsObservable(token:string):Observable<any> { return Observable.of( this.citiesComplex.filter((city:any) => { return city.name.startsWith(token) || city.country.startsWith(token); }) ); }

I use this technique searching for people when matching on first and last name.

@critchie elegant solution! Thank you :)

This is very much needed, I don't think the OP wants there to be multi select, just be able to filter the drop down list by more than one field in an array of objects. He or she can then select just one from that list.

Once you have moved beyond the simple string/array of strings comparison the typeahead really can't make any assumptions about the filtering algorithm. To do what is being requested @valorkin and company would have to make assumptions about the filtering algorithm. The first assumption that comes to mind is what relational operator to use when determining a match. Is it "name AND country" or is it "name OR country"?

Returning an Observable where you explicitly define the filtering is much cleaner IMO.

Or allow predicates, or configurable pipes
BTW 2ND will be available in next Typeahead version ;)

how is the progress on this?

@sanyooh nearest 2 weeks I am focused on new datepicker (available from v1.9.0)
http://valor-software.com/ngx-bootstrap/#/datepicker#examples
then merge to ng v4+ (and 5) only (1-2 days)
then I will focus on typeahead

Looking forward to this, it would be great to be able to do something like in uib-typeahead, ie: typeaheadOptionField(id+', '+title)

I have a similar problem. I have an array of objects. I would like the typeahead to allow me to specify a field to use to match, and a field to use for display, and another field to use for the value.

  • A match should return an object from my collection.
  • The input field should show the value of the 'display' property.
  • The value should be set to the value of the named 'value' property.
  • I should still be able to use a template to display any combination of properties from my objects. (This currently works fine)

Do you think that would be possible?

I am getting ERROR TypeError: Cannot read property 'startsWith' of undefined.
Can you please help me?

Search by multiple fields, Any updates on this implementation ?

I'm with @jschank in that Typeahead should be more flexible in what is matched, displayed, and used for the selected value(s).

Any updates on this?

I'm not sure if I need to open a new issue, but I was wondering if TypeAheadOptionField supports multiple strings. I'd like to display a second property in my current dropdown on the Options field:

image

The html for the dropdown:

  <div class="row">
                                                    <div class="col-lg-12">
                                                        <input name="typeahead" [(ngModel)]="asyncSelected"
                                                               [typeaheadAsync]="true" [typeahead]="dataSource"
                                                               (typeaheadLoading)="changeTypeaheadLoading($event)"
                                                               (typeaheadOnSelect)="typeaheadOnSelect($event)"
                                                               [typeaheadOptionsLimit]="7"
                                                               typeaheadOptionField="schoolName"
                                                               placeholder="Start typing School Name..."
                                                               typeaheadWaitMs="1000" class="form-control">
                                                        <div *ngIf="typeaheadLoading">Loading</div>
                                                    </div>
                                                </div>

and the way I'm binding to it from my TS component:

this.dataSource = Observable.create((observer: any) => {
            observer.next(this.asyncSelected);
        })
            .pipe(
                mergeMap((token: string) => {
                    return this.schoolService.searchSchool(token, 1).pipe(map(data => data.schools));
                })
            );
    }
    asyncSelected: string;
    typeaheadLoading: boolean;
    typeaheadNoResults: boolean;
    dataSource: Observable<any>;

    changeTypeaheadLoading(e: boolean): void {
        this.typeaheadLoading = e;
    }

    typeaheadOnSelect(e: TypeaheadMatch): void {
        this.asyncSelected = null;
        this.addSchool(e.item);
    }

My school component also has a string address property that needs to also display in the search results. Not exactly sure how I'd do it.

Also the documentation link provided above: https://valor-software.com/ng2-bootstrap/#/typeahead is broken can someone update the link?

any updates on this? :100:

I solved it doing this

  ngOnInit(): void {
    // we want the typeahead to search on email and
    // firstName + lastName... so we create a searchQuery property
    // and duplicate records
    this.storeService.users$
      .pipe(
        filter(res => res !== null),
        takeUntil(this.destroy$)
      )
      .subscribe(res => {
        // get the users by email address
        // and create a searchQuery property
        const usersByEmail = _.cloneDeep(res);
        usersByEmail.forEach(x => {
          x.searchQuery = `${x.email}`.toLocaleLowerCase().replace(/ /gi, '');
        });

        // get the users with firstName and lastName
        const usersWithFullName = _.cloneDeep(res).filter(x => x.firstName);
        usersWithFullName.forEach(x => {
          x.searchQuery = `${x.firstName}${x.lastName}`.toLocaleLowerCase().replace(/ /gi, '');
        });

        this.users = [...usersWithFullName, ...usersByEmail];
      });
  }

The solution for @awaheed1 is to use a templated list.

<input [(ngModel)]="asyncSelected"
       [typeahead]="dataSource"
       [typeaheadAsync]="true"
       [typeaheadScrollable]="true"       
       [typeaheadItemTemplate]="rct"
       [typeaheadOptionField]="iFormat"
       [typeaheadOptionsInScrollableView]="10"
       (typeaheadOnSelect)="onSelect($event)"
       placeholder="Locations loaded via observable"
       class="form-control">

<!-- template for resource typeahead -->
<ng-template #rct let-rc="item">
  <div class="mb-0">
    <h5 class="m-1">{{rc.resourceRef}} - {{rc.newResourceType}}</h5>
  </div>
</ng-template>

This allows you to display multiple options with your own make-up/lay-out.
image

The problem I am facing thereafter is that the typeaheadOptionField can only display a single field from the object, it does not appear to be able to show me the exact selected result?

Now I understand I can map the two properties to a single string and have that be the search and selection criteria with the original object as a seperate property, but that doesn't seem to be an elegant solution?

ngb-typeahead allows you to use a function as the displayed value, but that doesn't seem to work on ngx-typeahead?

@critchie 's answer is still valid. The update link is below:
https://valor-software.com/ngx-bootstrap/old/3.0.1/#/typeahead#async-data

Was this page helpful?
0 / 5 - 0 ratings

Related issues

tuoitrexuquang picture tuoitrexuquang  路  3Comments

mounthorse-slns picture mounthorse-slns  路  3Comments

RolfVeinoeSorensen picture RolfVeinoeSorensen  路  3Comments

MihaiHoriaPopescu picture MihaiHoriaPopescu  路  3Comments

ravirajhalli picture ravirajhalli  路  3Comments