19

I was trying to use the Material Angular autocomplete and I came across the displayWith function which can be apparently used to be the output that is displayed on selection. I wanted to call a custom function within the display function like

displayFn(id) {
 return this.getValue(id)
}
getValue(id) {
 /**return some string
}

For the autocomplete

<mat-autocomplete #autoOutlet="matAutocomplete" [displayWith]="displayFn">
  <mat-option *ngFor="let option of outletFilterOptions | async [value]="option.outletId">
   {{ option.outletName }}
  </mat-option>
</mat-autocomplete>

As you see I am using the id as the model instead of the entire object.

When the display function returned an error that this.getValue is undefined I searched Stack Overflow for a solution and was suggested that I use something like [displayWith]="displayFn.bind(this)".

But unfortunately, that isn't working for me either. I am using Angular material 5.1.0.

Is there something I am missing?

Sarvan Kumar
  • 926
  • 1
  • 11
  • 27
Sriram Jayaraman
  • 800
  • 1
  • 8
  • 15

5 Answers5

32
displayFn = value => {
  // now you have access to 'this' 
  this.someMethod();
  return 'formatted display';
}
IonutAnin
  • 335
  • 3
  • 5
7

It is because of this is not binding to the component and its binding to mat-select option

enter image description hereenter image description here

NOw for using component's function, you have to use arrow function, the preferable method or pass this from the HTML function

I will use the arrow function to use the component's function

Without arrow function

displayFn(data: any) {
    return data.Id?this.sometask(data):''
}

With arrow function

displayFn = (data: any) => {
    return data.Id?this.sometask(data):''
}

This work in my scenario and it worked in your scenario too.

VIKAS KOHLI
  • 8,164
  • 4
  • 50
  • 61
4

You could just change your template to be

<mat-autocomplete #autoOutlet="matAutocomplete" [displayWith]="displayFn(id, this)">

Inside of templates this is a reference to your Component. Then just change your function to

displayFn(id, _this) {
  return _this.getValue(id)
}

If [displayWith] needs to be a function, you could create a property that returns your displayFn like this:

get createDisplayFn() {
  return (id) => {
    return this.getValue(id)
  }
}

and change your binding to [displayWith]="createDisplayFn". As ES6 arrow function can't be rebinded, this should still be a reference to your component.

hnzlmnn
  • 393
  • 2
  • 14
  • I tried your first suggestion but I can't happen to pass the selected `id` with the displayFn. The second suggestion sets my display value with the function itself like `function { return this.... ` is printed in the DOM – Sriram Jayaraman Apr 20 '18 at 10:41
  • 1
    The second option here (using a proxy function to return your function) worked for me. – Josh Aug 15 '18 at 02:15
  • @SriramJayaraman I guess, you forgot the `get` before the function definition – TmTron Dec 27 '18 at 18:24
0

Define cThis = this as a property of your class, and then use it inside your displayFn function:

<mat-autocomplete #autoOutlet="matAutocomplete" [displayWith]="displayFn(id, cThis)">


cThis = this;
displayFn(id, cThis) {
 return cThis.getValue(id)
}
getValue(id) {
 /**return some string
}

Demo that shows binding in displayWith

bugs
  • 14,631
  • 5
  • 48
  • 52
0

You just missed an undefined check before using attribute.

<mat-autocomplete #auto="matAutocomplete" [displayWith]="displayFn" (optionSelected)="optionSelected($event)">

<mat-option *ngFor="let user of users" [value]="user" >
    {{ user.first_name }} {{ user.last_name }}
</mat-option>

displayFn(user) {
    if (!user) return '';
    return user.name;
}   
Sajin M Aboobakkar
  • 4,051
  • 4
  • 30
  • 39