Dynamically create ion-options from a directive


#1

I have a number of ion-select/option components that looks something like this (code abbreviated):

    <form [formGroup]="form" (ngSubmit)="save($event)">
            <ion-select formControlName="MyList">
                <ion-option *ngFor="let entry of lists.Options$.getAttributeDef( 'MyAwesomeList' ).domain.domainFunctions.getTableValues()"
                             [value]="entry" >{{entry}}</ion-option>
            </ion-select>
    </form>

It works fine but it’s ugly. What I’d like to do is hide the code in a directive and just pass in the name of the list. Something like this:

    <form [formGroup]="form" (ngSubmit)="save($event)">
            <ion-select formControlName="MyList" listDirective="MyAwesomeList">
            </ion-select>
    </form>

I’ve created a directive but I don’t know how/where to add the options to the ion-select element. Do I add then to elementRef.nativeElement? At what point in the directive life-cycle should I add them? (e.g. the constructor or ngOnChanges.)


#2

I don’t like this approach very much, because it subverts AoT compilation and splits up what is logically a single class into two different parts, but if you want to persist with it, look into injecting an instance of Renderer2.

What I would do instead is to make this a custom component who receives MyAwesomeList as a bound property. It can then in its onChanges handler do all the function calling to populate an array property, and you can let ngFor iterate across that. That way you aren’t having those two function calls on every change detection cycle.


#3

Thanks for the response! Just to make sure I understand your recommendation: you suggest creating my own list component instead of using ion-select. So it would look something like:

<form [formGroup]="form" (ngSubmit)="save($event)">
        <my-select formControlName="MyList" listName="MyAwesomeList">
        </my-select>
</form>

Would creating my own component interfere with Ionic’s automatic processing of ion-select? I have a limited understanding (so far) of Ionic’s overall structure but I think I’d prefer using a stock Ionic control. I also intend my my solution to be used by developers in other apps and I don’t want create different Ionic controls. On the other hand I know that if I’m mucking with nativeElement I potentially have a brittle solution.


#4

<my-select>'s template would look something like this:

<ion-select [(ngModel)]="selection">
<ion-option *ngFor="let choice of choices" [value]="choice">{{choice}}</ion-option>
</ion-select>

So as far as Ionic is concerned, it would still see the <ion-select> and I think it would work just as it does now. I’m not certain what happens when you bind reactive FormControls. It might just work, but if it doesn’t, I know you definitely can do it with [(ngModel)] (because I’ve done it). It would be used like:

<my-select [(ngModel)]="whatever" [list]="whateverList" ngDefaultControl></my-select>

Inside MySelect's controller, you would have:

@Input() ngModel = "";
@Output() ngModelChange = new EventEmitter<string>();
@Input() list: string | undefined;
choices: string[] = [];

ngOnChanges(changes: SimpleChanges) {
  if (changes.hasOwnProperty("list")) {
    this.choices = magicGoesHere(changes.list);
  }
}

The combination of those top two properties plus the ngDefaultControl makes two-way binding work on MySelect like it does on any other control.