How to implement a dependant <select> inside HTML table

Viewed 58

I have implemented a table like the one below. Two columns are editable in which we can select values from the drop-down. I implemented this using HTML select. The values in the 'Category tier 2' are dependant on the value I select in the 'Category tier 1' column.

When I select a value in the 'Category tier 1' column of a particular row, all the row values in the 'Category tier 2' column change, but I would like to change the 'Category tier 2' value only for that particular row.

table

stackblitz link.

2 Answers

You can create each row in table as component let's say my-row.component and use it as a attribute on tr. after those changes

app.component.html

<div>
  <table style="width:100%">
        <th *ngFor = "let column of headers">
            {{column}}
        </th>
        <th>
            Category tier 1
        </th>
        <th>
            Category tier 2
        </th>
        <ng-container *ngFor = "let row of rows">
        <tr [headers]="headers" [row]="row" [categories]="categories" [category_tier1]="category_tier1" app-my-row></tr>   
        </ng-container>
    </table>
</div>

app.component.ts

import { Component, VERSION } from '@angular/core';
interface Category{
    category_id: number;
    tier1: string;
    tier2: string;
}
@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: [ './app.component.css' ]
})
export class AppComponent  {
  categories:Category[]=[
    {
    "category_id": 2,
    "tier1": "ABC",
    "tier2": "A"
    },
    {
    "category_id": 3,
    "tier1": "ABC",
    "tier2": "B"
    },
    {
    "category_id": 4,
    "tier1": "ABC",
    "tier2": "C"
    },
    {
    "category_id": 5,
    "tier1": "DE",
    "tier2": "D"
    },
    {
    "category_id": 6,
    "tier1": "DE",
    "tier2": "E"
    },
    {
    "category_id": 7,
    "tier1": "FG",
    "tier2": "F"
    },
    {
    "category_id": 8,
    "tier1": "FG",
    "tier2": "G"
    }
    ];
      category_tier1:string[]=[];
      category_tier2:string[]=[];

      headers=["Firstname","Lastname","Age"];

      rows=[
        {
          "Firstname":"Jill",
          "Lastname":"Smith",
          "Age":"50"
        },
        {
          "Firstname":"Eve",
          "Lastname":"Jackson",
          "Age":"94"
        },
        {
          "Firstname":"John",
          "Lastname":"Doe",
          "Age":"80"
        }
  ];

  constructor(){
    for(let i in this.categories){
                this.category_tier1.push(this.categories[i].tier1);
              }
              this.category_tier1=Array.from(new Set(this.category_tier1));
  }

}

app.component.css

table, th {
    border: 1px solid black;
    border-collapse: collapse;
    table-layout: fixed; width: 100%
  }
  th {
    padding: 15px;
    overflow: hidden;
  }

my-row.component.html

<td *ngFor="let column of headers">
    {{row[column]}}
</td>
<td>
    <select (change)="selectTier1($event)">
                  <option *ngFor="let c of category_tier1" [value]="c">{{c}}</option>
                </select>
</td>
<td>
    <select (change)="selectTier2($event)">
                <option *ngFor="let c of category_tier2" [value]="c">{{c}}</option>
              </select>
</td>

my-row.component.ts

import { Component, OnInit, Input } from '@angular/core';

@Component({
  selector: '[app-my-row]',
  templateUrl: './my-row.component.html',
  styleUrls: ['./my-row.component.css']
})
export class MyRowComponent implements OnInit {
  @Input() public headers;
  @Input() public row;
  @Input() public categories;
  @Input() public category_tier1;
  public category_tier2;
  constructor() { }

  ngOnInit() {
  }
  selectTier1(event){
    this.category_tier2=[];
    for(let c in this.categories){
      if(this.categories[c].tier1===event.target.value){
        this.category_tier2.push(this.categories[c].tier2);
      }
    }
  }
  selectTier2(event){

  }
}

my-row.component.css

 td {
    padding: 5px;
    overflow: hidden;
  }

select {
    border: 0;
    height: 60%;
    width: 100%;
}

td {
    border: 1px solid black;
    border-collapse: collapse;
    table-layout: fixed; width: 100%
  }

By using above approach each row will get their individual tier 2 array.

Working Stackblitz :- https://stackblitz.com/edit/angular-ivy-dcjc1y

Change 3 places in your code:

  • Define tier 2 categories as array of array instead of string:

      categories_tier2:string[][] = []; // replace category_tier2:string[] = []
    
  • In HTML, modify code to render tier2 select, and pass row index to tier1 select event handler:

    <tr *ngFor = "let row of rows; index as i">  // Use row index here
        <td  *ngFor = "let column of headers">
          {{row[column]}}
        </td>
        <td>
            <select (change)="selectTier1($event,i)"> // Pass row index to handler
              <option *ngFor="let c of category_tier1" [value]="c">{{c}}</option>
            </select>
        </td>
        <td>
          <select (change)="selectTier2($event)">  // Adjust to changed array
            <option *ngFor="let c of categories_tier2[i]" [value]="c">{{c}}</option>
          </select>
        </td>
      </tr> 
    

Then in the tier1 select handler, use the row index to render the correct tier2 list:

    selectTier1(event, i){
        this.categories_tier2[i]=[];
        for (let c of this.categories){
           if (c.tier1===event.target.value) {
             this.categories_tier2[i].push(c.tier2);
           }
        }
     }
Related