Binding 'this' in Angular Material Autocomplete displayWith using Angular 5
Asked Answered
A

5

20

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?

Almandine answered 20/4, 2018 at 10:15 Comment(0)
T
35
displayFn = value => {
  // now you have access to 'this' 
  this.someMethod();
  return 'formatted display';
}
Twelve answered 23/4, 2019 at 18:41 Comment(2)
It was not obvious to me why this works. I learned that arrow functions differ in that they always establish "this" based on the scope where they are defined.Rabbitfish
that saved me Thanks!Twocycle
B
8

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.

Biggers answered 31/1, 2020 at 14:57 Comment(0)
D
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.

Darvon answered 20/4, 2018 at 10:30 Comment(3)
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 DOMAlmandine
The second option here (using a proxy function to return your function) worked for me.Ommatophore
@SriramJayaraman I guess, you forgot the get before the function definitionGaelic
A
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

Annabelannabela answered 20/4, 2018 at 10:22 Comment(8)
I checked out your suggestion but I am not able to pass the id from the DOMAlmandine
Where is id coming from then?Annabelannabela
The id is auto-generated by angular material without passing any such paramtersAlmandine
OK I see, what happens if you just do [displayWith]="displayFn(cThis)" ?Annabelannabela
yes i get one param and that is is this binded to itAlmandine
But then I assume id is undefined? If that's the case, could you not extract id from the option object?Annabelannabela
yes! in that case id is undefined. option object is only the list, it is not the selected optionAlmandine
Let us continue this discussion in chat.Annabelannabela
D
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;
}   
Dre answered 4/6, 2018 at 8:48 Comment(1)
I wanted to call another method within the displayFn method.Almandine

© 2022 - 2024 — McMap. All rights reserved.