1

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.

2
  • Instead of [ngValue]=option.value try [ngValue]=option. This will pass the whole object to the event. Commented Apr 10, 2023 at 23:30
  • If I do this, then the ngModel needs to be an object, but it has to be single property. I mighty work with setter and ngModelChange and use internal selectedValue property instead Commented Apr 11, 2023 at 6:18

2 Answers 2

1

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];
  }

}
Sign up to request clarification or add additional context in comments.

2 Comments

Perhaps also use <ng-content> for the options
Well I need the ngModel/selectedValue to bind the value property, not the whole option object, since it is then used outside to set a property in another object. And the ngModel/selectedValue needs to be set via @Input
0

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>

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.