1

I am trying to create a set of custom templates using materials autocomplete and ngTemplateOutlet to switch in which template I want to use to display the results (e.g. if there are grouped results they will display differently to traditional autocomplete options).

ngTemplateOutlet seems to render the template only if there is a default option provided. It seems unable to attach the options directly to the mat-autocomplete without at least one mat-option existing. Ideally, the only options being rendered would be the ones fetched from the component (in a more complicated example, the following code is simple and still replicates the problem). Essentially, I'm looking for a workaround.

I have tried every variation I can think of in terms of ng-container and template.

For example, with a template of

<ng-template #default let-options="options">
  <mat-option *ngFor="let option of options" [value]="option">
  {{option}}
  </mat-option>
</ng-template>

This doesn't render any autocomplete options

<mat-autocomplete #auto="matAutocomplete">
    <ng-container *ngTemplateOutlet="default; context:{options: options}"></ng-container>
</mat-autocomplete>

But, with the inclusion of at least one mat-option, this renders all of them

<mat-autocomplete #auto="matAutocomplete">
    <mat-option>Test</mat-option>
    <ng-container *ngTemplateOutlet="default; context:{options: options}"></ng-container>
</mat-autocomplete>

Here's a stackblitz: https://stackblitz.com/edit/angular-bz45ae?file=app/autocomplete-simple-example.html

alistairKane
  • 23
  • 1
  • 4

1 Answers1

2

This is related to the mat-autocomplete behavior. It uses @ContentChildren to access the QueryList of MatOption's. mat-autocomplete also has an ngAfterContentInit hook where it creates a ListKeyManger, that manages the active option in a list of items, passing the QueryList of MatOptions's as a constructor argument. You can see it here.

Yes, those mat-option's are rendered, but it still won't work as normal, you can check it by adding the optionSelected listener:

<mat-autocomplete #auto="matAutoComplete" (optionSelected)="optionSelected($event)">

Assume we do a console.log inside the optionSelected method of our component - it won't be invoked when you click on any option that's rendered via ngTemplateOutlet. mat-autocomplete cannot access this list just because this list is a part of another view (everything inside ng-template is a separately compiled view definition).

You could just create a reusable component where you will pass an options binding and link it with a parent form via injecting the FormGroupDirective.

overthesanity
  • 1,054
  • 1
  • 8
  • 12
  • Thanks for this answer and the additional reading. Interestingly I have found that it works when I put the mat-options template inside the mat-autocomplete tags. This seems to make it work as intended, including with optionSelected. However your suggestion to create a reusable component is good also. – alistairKane Jul 25 '19 at 14:40