htmlangularoptgroup

use single ngFor loop to display two distinct optgroup in select tag for angular 4


I have a dummy Array of Objects as follows:

this.historyOfWords = [
      {
        'property': 'property1',
        'type': 'dataprop',
        'value': 'value1'

      },
      {
        'property': 'Ref1',
        'type': 'objprop',
        'value': 'Prop of Ref1'
      }
    ];

I am looking to sort the above mentioned information into <optgroup> for the <select> tag

My current implementation is as follows:

<select size="10">
      <optgroup label="Properties"> <!-- all type of data that is not `objprop`-->
        <option *ngFor="let eachVal of historyOfWords">
          <div *ngIf="eachVal.type!='objprop'"> <!-- test type here -->
          {{eachVal.property}}</div>
        </option>
      </optgroup>
      <optgroup label="References to Properties">
        <option *ngFor="let eachVal of historyOfWords"> <!-- need to run the  loop again-->
          <div *ngIf="eachVal.type==='objprop'">{{eachVal.property}}</div>
        </option>
      </optgroup>
    </select>

I tried the logic if else logic in angular similarly

<select size="10">
      <optgroup label="Properties"> <!-- all type of data that is not `objprop`-->
        <option *ngFor="let eachVal of historyOfWords">
          <div *ngIf="eachVal.type=='dataprop'; else ref;"> <!-- test type here -->
          {{eachVal.property}}</div>
        </option>
      </optgroup>
      <optgroup label="References to Properties">
        <ng-template #ref> <-- ELSE show this -->
        <option *ngFor="let eachVal of historyOfWords"> <!-- need to run the  loop again-->
          <div>{{eachVal.property}}</div> <!-- need to put some logic here again -->
        </option>
      </optgroup>
    </select>

The problem is eachVal is outside the scope of the first optgroup and hence not available so I need to loop over again.

What is an optimal way to display two distinct values under optgroup without looping more times.


Solution

  • Filter the data ahead of time. It's always a good idea to keep as much of the logic as possible in the component class, and not in the template.

    import { Component } from '@angular/core';
    
    @Component({
      selector: 'my-app',
      template: `
      <select>
          <optgroup label="Properties"> 
            <option *ngFor="let eachVal of notObjProp">
              <div> {{eachVal.property}}</div>
            </option>
          </optgroup>
          <optgroup label="References to Properties">
            <option *ngFor="let eachVal of objProp"> 
              <div>{{eachVal.property}}</div>
            </option>
          </optgroup>
        </select>
      `,
      styleUrls: ['./app.component.css']
    })
    export class AppComponent {
      data = [
        {
          'property': 'property1',
          'type': 'dataprop',
          'value': 'value1'
    
        },
        {
          'property': 'Ref1',
          'type': 'objprop',
          'value': 'Prop of Ref1'
        }
      ];
    
      objProp = this.data.filter(({ type }) => type === 'objprop');
      notObjProp = this.data.filter(({ type }) => type !== 'objprop');
    }
    

    Live demo