karlitos
karlitos

Reputation: 1656

Style a HTML select according to the selected option in Angular

I would like to to style a select element in an Angular component differently, when a specific option (with an inactive attribute) is selected.

My component look like this:

class ComponentWithSelect {
  @Input() ngModel: unknown = null;
  @Input() options: { value: unknown, label: string, diasbled: boolean }[]

  inactiveSelected: boolean = false;
  ...

}

and the corresponding template look like this:

<select
  [ngModel]="ngModel"
  [class.inactive-select]="inactiveSelected">
  <option *ngFor="let option of options"
    [ngValue]="option.value"
    [disabled]="option.disabled">
    {{option.label}}
  </option>
</select>

I would like to apply the inactive-select class when an option with the inactive attribute is selected OR when a ngModel is passed to the component which corresponds to an option with an inactive attribute.

I tried to use the (ngModelChange) to set the inactiveSelected property but $event parameter passed to a corresponding method (onModelChange) does contain the value of the selected option, not the inactive attribute.

I could go with (change) and access the event.target.options.selectedIndex property but neither of those two approaches works, when the ngModel is set from outside or its initial value is so, that it corresponds to a option with an inactive property

EDIT:

I tried the suggested approach with using the whole option object for ngValue

class ComponentWithSelect {

  @Input()
  set ngModel(model: unknown) {
    this.selectedValue = this.options.find(option => option.value === model) || null;
  }
  @Input() options: { value: unknown, label: string, disabled: boolean }[] = [];
  @Output() ngModelChange = new EventEmitter<unknown>();

  selectedValue: { value: unknown, label: string, disabled: boolean } = null;

  onModelChange(option: { value: unknown, label: string, disabled: boolean }) {
    this.ngModel = option.value;
    this.ngModelChange.emit(this.ngModel);
  }

and the template

<select
  [(ngModel)]="selectedValue"
  [class.inactive-select]="selectedValue.disabled">
  <option *ngFor="let option of options"
    [ngValue]="option">
    {{option.label}}
  </option>
</select>

The right option is now selected properly when a corresponding ngModel is passed to my component, but when I try to manually change the select, for the for the first time nothing is selected, all consequent changes are working fine.

Upvotes: 1

Views: 2336

Answers (2)

karlitos
karlitos

Reputation: 1656

I got it working, I had to add getter as well

class ComponentWithSelect {

  @Input()
  set ngModel(model: unknown) {
    this._ngModel = model;
    this.selectedValue = this.options.find(option => option.value === model) || null;
  }
  get (): unknown {
    return this._ngModel;
  }
  @Input() options: { value: unknown, label: string, disabled: boolean }[] = [];
  @Output() ngModelChange = new EventEmitter<unknown>();

  private _ngModel: unknown;
  selectedValue: { value: unknown, label: string, disabled: boolean } = null;

  onModelChange(option: { value: unknown, label: string, disabled: boolean }) {
    this._ngModel = option.value;
    this.ngModelChange.emit(this._ngModel);
  }

and the template:

<select
  [(ngModel)]="selectedValue"
  [class.inactive-select]="selectedValue.disabled">
  <option *ngFor="let option of options"
    [ngValue]="option">
    {{option.label}}
  </option>
</select>

Upvotes: 0

A.B.
A.B.

Reputation: 2470

There are 2 things you should notice:

  • as Maggini pointed out your ngValue binding is wrong
  • use [(ngModel)] instead of [ngModel]

Besides you do not need a secondary boolean property for tracking.
Last note, inital bound object must be one from the options array.

*** .html file ***

<select
  [(ngModel)]="selectedValue"
  [ngClass]="{'inactive-select': selectedValue.inactive}"
  >
  <option *ngFor="let option of options"
    [ngValue]="option">
    {{option.label}}
  </option>
</select>

*** .ts file ***

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

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

  selectedValue: any;

  options: { value: unknown, label: string, inactive: boolean } [] = [
    {
      value: 1,
      label: "1",
      inactive: false,
    },
    {
      value: 2,
      label: "2",
      inactive: true,
    },    
  ]

  ngOnInit(): void {
    this.selectedValue = this.options[0];
  }

}

Upvotes: 1

Related Questions