msanford
msanford

Reputation: 12237

Dynamic <select><option> with conditional <optgroup> as ng-container not working

I have created a small dropdown component which takes the following interface as <option>s:

interface DropdownSelectChoice {
    label: string;
    key: string;
}

The component to accepts a one- or two-dimensional array of choices

@Input() choices: DropdownSelectChoice[] | DropdownSelectChoice[][];

and if a two-dimensional array of choices is passed, to create unlabelled <optgroup>s of each set of choices:

<span [class.disabled]="disabled">
    <select (ngModelChange)="choose($event)"
            [disabled]="disabled"
            [(ngModel)]="defaultChoice">

        <ng-container *ngIf="choices[0].constructor === Array">
            <optgroup *ngFor="let group of choices" [label]="null">
                <option *ngFor="let choice of group"
                        [value]="choice.key">
                    {{ choice.label }}
                </option>
            </optgroup>
        </ng-container>

        <ng-container *ngIf="choices[0].constructor !== Array">
            <option *ngFor="let choice of choices"
                    [value]="choice.key">
                {{ choice.label }}
            </option>
        </ng-container>

    </select>
</span>

If I pass it a one-dimensional array, it works as expected:

choices: DropdownSelectChoice[] = [
    {
        label: "One",
        key: "1"
    },
    {
        label: "Two",
        key: "2"
    }
];

If I pass it a two-dimensional array:

choicesGroup: DropdownSelectChoice[][] = [
    [

        {
            label: "One",
            key: "1"
        },
        {
            label: "Two",
            key: "2"
        }
    ],
    [
        {
            label: "Three",
            key: "3"
        },
        {
            label: "Four",
            key: "4"
        }
    ]
];

I get choicesGroup.length blank <option>s with no <optgroup>.

Setting a break-point in the drop down component's initialiser, I confirm that this.choices[0].constructor === Array is true when passing choicesGroup, but the template seems always to be evaluating the !== Array template path.

What silly thing have I omitted that I am not noticing?

Upvotes: 3

Views: 2304

Answers (2)

msanford
msanford

Reputation: 12237

For the sake of completeness -- informed by Günter's accepted answer, these are my final changes:

private grouped: boolean;

ngOnInit() {
    this.grouped = this.choices[0].constructor === Array;
}

And in the template, I changed the ng-container's conditions to:

<ng-container *ngIf="grouped">...</ng-container>
<ng-container *ngIf="!grouped">...</ng-container>

Upvotes: 0

G&#252;nter Z&#246;chbauer
G&#252;nter Z&#246;chbauer

Reputation: 657821

Types can't be used this way in templates.

Try

<ng-container *ngIf="choices[0].isArray">
<ng-container *ngIf="!choices[0].isArray">

if you still want to use constructor === Array you need to create a method and do the comparison there.

<ng-container *ngIf="isArray(choices[0])">
isArray(obj) {
  return obj.constructor === Array;
}

Upvotes: 3

Related Questions