How to clear the typeahead input after a result is selected?
Asked Answered
D

2

20

I'm using the ng-bootstrap typeahead component to search a customer database. When the user selects a customer from the typeahead results list, I navigate to a customer details page. I've got this working, but I want to clear the input field after navigation has taken place. I've tried setting the model to null or an empty string in the selectItem event logic, but this isn't working:

customer-search-typeahead.component.html

<template #resultTemplate let-r="result" let-t="term">
  <div>
    <div>
      {{r.resource.name[0].given}} {{r.resource.name[0].family}}
    </div>
    <div>
      {{r.resource.birthDate | date: 'dd/MM/yyyy'}}
    </div>
  </div>
</template>

<input type="text" class="form-control" [resultTemplate]="resultTemplate" (selectItem)="onSelect($event)"
       [(ngModel)]="model" placeholder="Start typing a customer name..." [ngbTypeahead]="search"/>

customer-search-typeahead.component.ts

@Component({
  selector: 'customer-search-typeahead',
  template: require('./customer-search-typeahead.component.html'),
  styles: [`.form-control { width: 300px; }`]
})
export class CustomerSearchTypeaheadComponent {

  model: any;
  searching: boolean;

  constructor(private customerService: CustomerService, private router: Router) {}

  onSelect($event) {
    this.router.navigate(['/customers', $event.item.resource.id]);
    this.model = null;
  };

  search = (text$: Observable<string>) =>
   //omitted for brevity
}

The typeahead input looks like this after a selection has been made:

enter image description here


Solution

customer-search-typeahead.component.html

<input type="text" class="form-control" #input [ngbTypeahead]="search" (selectItem)="onSelect($event); input.value='' ">

customer-search-typeahead.component.ts

onSelect($event, input) {
    $event.preventDefault();
    this.router.navigate(['/customers', $event.item.resource.id]);
  };
Dodecahedron answered 30/9, 2016 at 4:55 Comment(0)
B
44

The issue you witnessing arises from the fact that the NgModel directive is updating model binding asynchronously and the actual model is updated after the onSelect method gets executed. So your model update gets overridden by the NgModel functionality.

Fortunately we (ng-bootstrap authors) got all the flex points in place to cover your use-case :-) There are a couple of things that you could do.

Firstly the $event object passed to the onSelect method has the preventDefault() method and you can call it to veto item selection (and as a result writing back to the model and input field update).

$event.preventDefault() will make sure that the model is not updated and the input field is not updated with the selected item. But text entered by a user will still be part of the input so if you want to clear up this as well you can directly update the input's value property.

Here is code demonstrating all those techniques together:

onSelect($event, input) {
    $event.preventDefault();
    this.selected.push($event.item);
    input.value = '';
  }

where input argument is a reference to the input DOM element:

<input type="text" class="form-control" #input 
   [ngbTypeahead]="search" (selectItem)="onSelect($event, input)">

Finally here is a plunker showing all this in practice: http://plnkr.co/edit/kD5AmZyYEhJO0QQISgbM?p=preview

Brewton answered 30/9, 2016 at 17:23 Comment(5)
I've just noticed that this works for the first search and the input box is cleared, but subsequent searches result in the search string remaining in the input field. I've tried all the combinations you've suggested.Dodecahedron
@BlakeMumford I see! I've updated my answer based on your use-case and comments. Hope it covers your needs right now.Brewton
Hi, that works well, but then my component is performing DOM manipulation. I have updated my question with the solution I eventually went with. Thanks for your help!Dodecahedron
@BlakeMumford you can always go through the Renderer abstraction if you are worried about direct DOM manipulation: this._renderer.setElementProperty(input, 'value', '');. Updated plunk: plnkr.co/edit/n3G2kEp7sZFNy0cGOSxv?p=previewBrewton
As Renderer is deprecated, you should use Renderer2. So, firstly import it import { Renderer2 } from '@angular/core';, then inject constructor(private renderer: Renderer2) {} and use like this in your selectItem($event, input: HTMLInputElement) method: this.renderer.setProperty(input, 'value', '');Ely
C
1

The above one is template ref value solution.

This is for ngModel solution.

Html code:

<input type="text" class="form-control" [resultTemplate]="resultTemplate" (selectItem)="onSelect($event)"
       [(ngModel)]="model" placeholder="Start typing a customer name..." [ngbTypeahead]="search"/>

Component code:

onSelect($event) {
    $event.preventDefault();
    this.model = null;
    this.router.navigate(['/customers', $event.item.resource.id]);
};

$event.preventDefault();

for ngModel value change empty

Celebrate answered 8/5, 2019 at 14:21 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.