Angular 12 - Select dropdown option based on the value in template

Viewed 15388

I am working on a POC in which i am facing a challenge. Please help me solving it.

Situation

  1. I have list of categories which needs to be displayed in a select dropdown
  2. I have a portal object which contains one of the categories in the select dropdown

Challenge: I need to select the option which matches the category of the portal while populating the select dropdown

Category select dropdown population and prtl-category binding is,

<!-- Portal Category -->
    <div class="row prtl_field_margin">
        <div class="col-4 prtl_form_field_right">
            <label for="pCategory" class="control-label prtl_form_label">Category</label>
        </div>
        <div class="col-4">
            <select class="form-control" id="pCategory" name="pCategory" [(ngModel)]="prtl.category">
                <option value="" disabled>Choose a Category</option>
                <option *ngFor="let c of categories" [ngValue]="c">{{ c.name }}</option>
            </select>
        </div>
        <div class="col-4"></div>
    </div>

prtl object data in component ts file is,

{
  "name": "Stock Market",
  "id": 10,
  "category": {
    "id": "1",
    "name": "Cricket"
  }
}

Category array data in component ts file is,

[
  {
    "id": "1",
    "name": "Cricket"
  },
  {
    "id": "2",
    "name": "Football"
  }
]

Result needed: Categories dropdown should contain all the categories in the array. However, the category which matches to the category of prtl object should be selected. As in, the cricket category should be selected after dropdown is populated, but the user is free to change it to football.

3 Answers

Previous solution doesn't work (at least, not worked in my test).

I did my try and you can test that it works in here

EXTRACT:

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

interface CategoryInterface {
  id: string;
  name: string;
}

@Component({
  selector: "app-root",
  templateUrl: "./app.component.html",
  styleUrls: ["./app.component.css"]
})
export class AppComponent implements OnInit {
  title = "Juan Berzosa select of an object value of a HTML select component solution";

  prtl = {
    name: "Stock Market",
    id: 10,
    category: {
      id: "2",
      name: "Football"
    }
  };

  categories: CategoryInterface[];

  selectedCategory: CategoryInterface;

  constructor() {
    this.categories = [
      {
        id: "1",
        name: "Cricket"
      },
      {
        id: "2",
        name: "Football"
      }
    ];

    const selectedCategoryArray = this.categories.filter(
      (itemCategory) =>
        this.prtl.category.id === itemCategory.id &&
        this.prtl.category.name === itemCategory.name
    );

    this.selectedCategory = selectedCategoryArray[0];
  }
}

HTML:

<div>
  <h1>
    Welcome to {{ title }}!
  </h1>
</div>
<!-- Portal Category -->
<div class="row prtl_field_margin">
  <div class="col-4 prtl_form_field_right">
    <label for="pCategory" class="control-label prtl_form_label"
      >Category</label
    >
  </div>
  <div class="col-4">
    <select
      class="form-control"
      id="pCategory"
      name="pCategory"
      [(ngModel)]="selectedCategory"
    >
      <option value="" disabled>Choose a Category</option>
      <option *ngFor="let c of categories" [ngValue]="c">{{ c.name }}</option>
    </select>
  </div>
  <div class="col-4"></div>
</div>

P.S: As @arun-s said, don't forget to add the FormsModule in app.module.ts:

...
import { FormsModule } from "@angular/forms";
...
@NgModule({
  declarations: [AppComponent],
  imports: [BrowserModule,
  FormsModule],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule {}

First of all , when you are using ngModel, make sure to import FormsModule to your application module file if not done.

You can use below component to achieve your output :

app.component.ts

import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
 title = 'my-app';


  prtl = {
    "name": "Stock Market",
    "id": 10,
    "category": {
       "id": "2",
       "name": "Football"
    }
  };

  categories = [
    {
     "id": "1",
     "name": "Cricket"
    },
    {
     "id": "2",
     "name": "Football"
    }
  ];
  categoryToSelect = this.prtl.category;
}

app.component.html

<div class="row prtl_field_margin">
<div class="col-4 prtl_form_field_right">
    <label for="pCategory" class="control-label prtl_form_label">Category</label>
</div>
<div class="col-4">
    <select class="form-control" id="pCategory" name="pCategory" [(ngModel)]="categoryToSelect.id">
        <option value="" disabled>Choose a Category</option>
        <option *ngFor="let c of categories" [ngValue]="c.id">{{ c.name }}</option>
    </select>
</div>
<div class="col-4"></div>

In the above code, i have made your selection Football as the default one . Also , when giving an ngValue to a select dropdown, give a unique value as the identifier and not the whole object itself.

Use unique identifiers as values for dropdown options, which in your case is category ids. Now pass the category id from the portal object to ngModel directive to bind the value to dropdown. You can use ngModelChange directive to listen for category id value changes and update the same in portal object. You can find the example below.

Example: component.html

<select
  id="category"
  [ngModel]="prtlObj.category.id"
  (ngModelChange)="updateObj($event)"
>
  <option
    *ngFor="let item of categories"
    [value]="item.id"
  >{{ item.name }}</option>
</select>

component.ts

updateObj(id: string) {
  const category = this.categories.find(x => x.id === Number.parseInt(id));
  this.prtlObj = {
    ...this.prtlObj,
    category
  };
}

Full working example can be found here.

Related