Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add example for server-side filtering #26

Closed
macjohnny opened this issue May 8, 2018 · 17 comments
Closed

Add example for server-side filtering #26

macjohnny opened this issue May 8, 2018 · 17 comments
Labels
examples help wanted Extra attention is needed

Comments

@macjohnny
Copy link
Member

Add an example as described in #3 with the option implemented in #25

@macjohnny
Copy link
Member Author

@rfuhrer @altschuler @damianmigo would anyone of you like to add an example for server-side filtering? the "server-side" in the example can simply be an function / observable.

@macjohnny macjohnny added help wanted Extra attention is needed examples labels May 8, 2018
@macjohnny
Copy link
Member Author

FYI version 1.2.0 with the new @Input() clearSearchInput = false; has been released

@exlibris
Copy link

I too would love to see a working example of filtering a service. I am attempting to modify the bank example to filter against an observable tied to a service. Still no luck. If I succeed I will certainly share the code. If anyone else has functional examples I would be grateful if I could see your solution.

@rfuhrer
Copy link

rfuhrer commented May 22, 2018

Sorry guys, I've been super busy at work and I ended up just writing a horrible hack for the base autocomplete to make it work the way I wanted it to. It's on my list to come back and replace that with this component again later. I'll give an example of how I got it all working then.

@exlibris Following the example on the material docs under the Autocomplete should help. here's the code for that: https://stackblitz.com/angular/akvlgvjlapx?file=app%2Fautocomplete-filter-example.ts
The only thing to remember about hitting a real server is that it's nothing special, it's just another Observable (a really slow one, but you still handle it the same way).

So in this code just change the filter function to do something like

filter(val: string): string[] {
    //URL style depends on your server
    return this.http.get<string>(`someUrlOrControllerOrWhatever?search=${val}`); 
  }

Now you're getting some results from a server rather than a hardcoded list in memory. Using this plugin you'd just setup the Options area to pull from this filteredOptions: Observable<string[]>; and add in that async tag to automatically handle mapping observable values into normal values you want to display.

edit: ohh you might have to change the pipe for the control from a map to a switchmap? I'd have to actually try to build this too make sure, but basically if you have an observable of observables then a switch map gets you back to just an observable.

@rfuhrer
Copy link

rfuhrer commented May 23, 2018

Found another place where I do this with an input that searches for jobs that have a license number:

this.searchControl.valueChanges.pipe(
      debounceTime(500),
      distinctUntilChanged(),
      switchMap(term => this.jobService.licenseSearch(term))
    ).subscribe(results => this.jobs = results);

So this search control is just a text box (could be the input of the ngx-mat-select-search). I've set it up to wait 500 milliseconds for them to stop changing the input, then check if it's actually a new value (eg. typing 123 then 45 delete delete results in 123 again, no need to hit the server), then using switchmap I actually call a service that hits the server and gets back a result. I then subscribe to the results of this and dump it into the "jobs" variable which is what you'd bind to your options of the ngx-mat-select-search

Hope that shows a more standard way of doing it where you've broken out your server calls to be in a separate service rather than the example above.

@exlibris
Copy link

Thank you for the further code example. I think the problem I am having adapting the bank code to my project is not so much accessing the service results and displaying them, but the fact that my observable is blank when the component is first loaded and so the widget does not open and allow for input.

For example, in this application I have an observable which calls a service that retrieves place names from a Solr server:

    this.places$ = this.ctuSearchterm.valueChanges  
      .filter(ctuSearchterm => ctuSearchterm && ctuSearchterm.length >= 1)   
      .switchMap(ctuSearchterm => this.placeNameService.search(ctuSearchterm))   
      .share();

And if I have two form controls on the page, which is used to get user input, and both of which are displaying the results, it is clear that the ngx-mat-select-search is able to read and display the retrieved results. In the following code example retrieved place names are added to both autocomplete/select widgets:

<mat-form-field>
    <mat-select [formControl]="bankCtrl" placeholder="Bank" #singleSelect>
      <ngx-mat-select-search [formControl]="bankFilterCtrl"></ngx-mat-select-search>
      <mat-option *ngFor="let place of places$ | async" [value]="bank">
        {{place.place_name}}
      </mat-option>
    </mat-select>
  </mat-form-field>

  <mat-input-container style="width: 60%">
    <input matInput
           #ctuSearchBox           
           title="Location search"
           [formControl]="ctuSearchterm"
           [matAutocomplete]="auto"
           placeholder="Find a city, county, or zip...">
 <mat-autocomplete #auto="matAutocomplete" (optionSelected)="itemSelected($event)">
      <mat-option *ngFor="let place of places$ | async"
                  [value]="place.place_name">
        {{place.place_name}}
      </mat-option>

However, when I attempt to use the bankCtrl form control as the input for the places$ observable like so:

    this.places$ = this.bankCtrl.valueChanges
      .filter(ctuSearchterm => ctuSearchterm && ctuSearchterm.length >= 1)
      .switchMap(ctuSearchterm => this.placeNameService.search(ctuSearchterm))
      .share();

the "bank" input clearly gets focus (underline becomes darker) but it just sits there and it is impossible to type text into the control.

I am likely just very confused (this is my first Angular project) but in my minds eye I am imagining that the problem is that there is no initial values in the places$ observable so no select is appearing. I have been looking for ways to force in a blank value to my observable or otherwise initialize but no luck yet.

Thanks for any advice you can offer, and apologies for any grand ignorance displayed. If it is of any use, you can see the draft application where this interface will hopefully be used at http://ptappdev.gisdata.mn.gov/ptappd/ under "Search Filters" and then "Location". The autocomplete for "A city, county or zipcode" which currently uses mat-autocomplete is not behaving quite how my stakeholders want so I would love to work in your code.

@rfuhrer
Copy link

rfuhrer commented May 23, 2018

If you think the issue is that there's no starting value to kick things off, you can try the .startswith('') function. It's used in the angular material example I linked above. It basically simulates someone typing whatever you put in there as soon as the observable is constructed. but I don't believe that's the issue because it should still let you type in the control no matter how broken your observable is setup. I'd check your javascript console. Sounds like when a bad angular error happens and all your components basically stop working.

Another thing to look at if possible is to update to a new Angular/rxjs so you can start using pipes instead of the old rxjs operators that weren't very good at being tree-shaken and would increase your bundle size.

@macjohnny
Copy link
Member Author

@exlibris you can also just add a dummy-option that is always present when nothing is typed, i.e.

<mat-select>
  <ngx-mat-select-search [formControl]="bankFilterCtrl"></ngx-mat-select-search>
  <mat-option *ngIf="!bankFilterCtrl.value" [value]="false">Please type your zip-code</mat-option>
  <mat-option *ngFor="let bank of filteredBanks | async" [value]="bank">
      {{bank.name}}
  </mat-option>
</mat-select>

We are also using this trick for a different purpose (i.e. providing an option labelled "create new bank" with value false and if the select value is explicitly false, we show an additional input field below the select, allowing to create a new entry).
Hope this helps

@exlibris
Copy link

exlibris commented May 24, 2018

@macjohnny Vielen Dank für Ihre nützliche Rat! (Sorry, practicing for my summer trip to Deutschland...)

Anyhow, the dummy-option did the trick. I am able now to use your control to filter my place names. Still some dialing in but I may be able to get the code into our main branch in time for the public product release next week.

@irowbin

This comment has been minimized.

@macjohnny

This comment has been minimized.

@irowbin

This comment has been minimized.

@macjohnny

This comment has been minimized.

@irowbin

This comment has been minimized.

@macjohnny

This comment has been minimized.

@macjohnny
Copy link
Member Author

@irowbin I moved your issues to #33 and #34

@macjohnny macjohnny added help wanted Extra attention is needed and removed help wanted Extra attention is needed labels Jul 9, 2018
macjohnny added a commit that referenced this issue Jan 8, 2019
@macjohnny
Copy link
Member Author

example added in #87

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
examples help wanted Extra attention is needed
Projects
None yet
Development

No branches or pull requests

4 participants