ngx-bootstrap icon indicating copy to clipboard operation
ngx-bootstrap copied to clipboard

feat(typeahead): support multiple search field values

Open Abrissirba opened this issue 8 years ago • 20 comments

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.

Abrissirba avatar Dec 19 '16 12:12 Abrissirba

multi select is planned feature

valorkin avatar Dec 20 '16 10:12 valorkin

@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 avatar Dec 28 '16 00:12 critchie

@critchie elegant solution! Thank you :)

valorkin avatar Dec 28 '16 00:12 valorkin

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.

jtweeks avatar Jan 03 '17 19:01 jtweeks

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.

critchie avatar Jan 03 '17 21:01 critchie

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

valorkin avatar Jan 03 '17 22:01 valorkin

how is the progress on this?

sanyooh avatar Aug 23 '17 11:08 sanyooh

@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

valorkin avatar Aug 23 '17 15:08 valorkin

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

Steven-Garcia avatar Sep 08 '17 13:09 Steven-Garcia

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?

jschank avatar Oct 27 '17 23:10 jschank

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

lavanyakomarasamy avatar Jan 21 '18 05:01 lavanyakomarasamy

Search by multiple fields, Any updates on this implementation ?

bandhavya avatar May 18 '18 09:05 bandhavya

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

Mathachew avatar Sep 06 '18 14:09 Mathachew

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?

awaheed1 avatar May 08 '19 17:05 awaheed1

any updates on this? :100:

rickithadi avatar Sep 19 '19 19:09 rickithadi

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];
      });
  }

robvaneck avatar Dec 04 '20 13:12 robvaneck

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?

mtholen avatar Jan 07 '21 23:01 mtholen

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

burakgavaskargo avatar Feb 05 '21 20:02 burakgavaskargo

An typeaheadOptionFieldFn should be enough, so we can pass a callback receiving the items and returning the final string to search the hint with. Like this:

 <input id="username" type="text" class="form-control"
         [ngModel]="data"
          [typeahead]="users"  autocomplete="off"
          [typeaheadOptionFieldFn]="optionFieldFn"
          [typeaheadScrollable]="true">
optionFieldFn(match: TypeaheadMatch) {
  const {item} = match;
  return `${item.name}${item.surname}${item.id}`;
}

giovanni-bertoncelli avatar Dec 17 '21 11:12 giovanni-bertoncelli

typeaheadOptionField already takes function but its useless in this case as it doesn't provide any parameters

Et3rnal avatar Sep 29 '23 10:09 Et3rnal