admin管理员组

文章数量:1406713

I have an Angular dropdown component (CustomDropdownComponent) that receives a list of options and a selected value from its parent component via @Input(). The parent also listens for selection changes using @Output().

However, I intentionally did not update the selectedValue in the parent when handling the (selectionChange) event to see if the dropdown would revert to the default selection. Surprisingly, the dropdown still updates to the newly selected option, even though the parent state remains unchanged.

My Questions:

  • Why does the dropdown update the selected option even though the parent does not store the new value?
  • Is this behavior due to Angular's change detection or how property bindings work?
  • How can I ensure that the dropdown only displays what the parent provides and does not retain a selection unless explicitly stored in the parent?

Here is a simplified version of my implementation:

Parent Component (appponent.ts)

export class AppComponent {
  options = [
    { value: '1', text: 'Option 1' },
    { value: '2', text: 'Option 2' },
    { value: '3', text: 'Option 3' }
  ];

  selectedValue = ''; // Intentionally not updating this when selection changes

  handleOnChange(event: any) {
    console.log(event.target.value); // Logging the value but not saving it
  }
}

Parent Component Template (appponent.html)

<app-custom-dropdown 
  [options]="options" 
  [selected]="selectedValue" 
  (selectionChange)="handleOnChange($event)">
</app-custom-dropdown>

Child Component (custom-dropdownponent.ts)

@Component({
  selector: 'app-custom-dropdown',
  templateUrl: './custom-dropdownponent.html',
  styleUrls: ['./custom-dropdownponent.scss']
})
export class CustomDropdownComponent {
  @Input() options: { value: string; text: string }[] = [];
  @Input() selected: string = '';

  @Output() selectionChange: EventEmitter<any> = new EventEmitter<any>();

  onChange(event: any) {
    this.selectionChange.emit(event); // Emitting value but parent does not save it
  }
}

Child Component Template (custom-dropdownponent.html)

<select (change)="onChange($event)" [value]="selected">
  <option value="" hidden>default</option>
  <option *ngFor="let option of options" [value]="option.value">
    {{ option.text }}
  </option>
</select>

I have an Angular dropdown component (CustomDropdownComponent) that receives a list of options and a selected value from its parent component via @Input(). The parent also listens for selection changes using @Output().

However, I intentionally did not update the selectedValue in the parent when handling the (selectionChange) event to see if the dropdown would revert to the default selection. Surprisingly, the dropdown still updates to the newly selected option, even though the parent state remains unchanged.

My Questions:

  • Why does the dropdown update the selected option even though the parent does not store the new value?
  • Is this behavior due to Angular's change detection or how property bindings work?
  • How can I ensure that the dropdown only displays what the parent provides and does not retain a selection unless explicitly stored in the parent?

Here is a simplified version of my implementation:

Parent Component (appponent.ts)

export class AppComponent {
  options = [
    { value: '1', text: 'Option 1' },
    { value: '2', text: 'Option 2' },
    { value: '3', text: 'Option 3' }
  ];

  selectedValue = ''; // Intentionally not updating this when selection changes

  handleOnChange(event: any) {
    console.log(event.target.value); // Logging the value but not saving it
  }
}

Parent Component Template (appponent.html)

<app-custom-dropdown 
  [options]="options" 
  [selected]="selectedValue" 
  (selectionChange)="handleOnChange($event)">
</app-custom-dropdown>

Child Component (custom-dropdownponent.ts)

@Component({
  selector: 'app-custom-dropdown',
  templateUrl: './custom-dropdownponent.html',
  styleUrls: ['./custom-dropdownponent.scss']
})
export class CustomDropdownComponent {
  @Input() options: { value: string; text: string }[] = [];
  @Input() selected: string = '';

  @Output() selectionChange: EventEmitter<any> = new EventEmitter<any>();

  onChange(event: any) {
    this.selectionChange.emit(event); // Emitting value but parent does not save it
  }
}

Child Component Template (custom-dropdownponent.html)

<select (change)="onChange($event)" [value]="selected">
  <option value="" hidden>default</option>
  <option *ngFor="let option of options" [value]="option.value">
    {{ option.text }}
  </option>
</select>
Share edited Mar 4 at 13:34 Chait 1,3733 gold badges22 silver badges37 bronze badges asked Mar 4 at 12:06 David KüngDavid Küng 294 bronze badges 1
  • What you are describing is the expected <select> behaviour. It visually updates its selected option based on the user’s choice, even if the value binding does not explicitly update. – AnwarMEQOR Commented Mar 4 at 13:03
Add a comment  | 

2 Answers 2

Reset to default 0

My reasoning is this:

You have a property binding [value]="selected" this means that only when the value selected has an actual value change (1 -> 2) will change detection run for the select and update the DOM, but in your scenario, you never update selected value either in the child nor the parent, so there is no need for the view to update when the input value does not change.

If you check this below code, you will see, the HTML updated when I programatically set selectedValue on the parent to 3, but not on subsequent changes. Because the value goes from ('' --on first attempt--> 3 --on subsequent changes--> 3(remains 3)) So the HTML never updates and keeps the user changes.

If you want to achieve a sort of revert, you have to do it programmatically.

import { Component, EventEmitter, Input, Output } from '@angular/core';
import { bootstrapApplication } from '@angular/platform-browser';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';

@Component({
  selector: 'app-custom-dropdown',
  imports: [CommonModule, FormsModule],
  template: `
  <select [value]="selected" (change)="onChange($event)">
    <option value="" hidden>default</option>
    <option *ngFor="let option of options" [value]="option.value">
      {{ option.text }}
    </option>
  </select>
  `,
})
export class CustomDropdownComponent {
  @Input() options: { value: string; text: string }[] = [];
  @Input() selected: string = '';

  @Output() selectionChange: EventEmitter<any> = new EventEmitter<any>();

  onChange(event: any) {
    this.selectionChange.emit(event); // Emitting value but parent does not save it
  }

  ngDoCheck() {
    console.log('CustomDropdownComponent check');
  }
}

@Component({
  selector: 'app-root',
  imports: [CustomDropdownComponent],
  template: `
    <app-custom-dropdown 
      [options]="options" 
      [selected]="selectedValue" 
      (selectionChange)="handleOnChange($event)">
    </app-custom-dropdown>
  `,
})
export class App {
  options = [
    { value: '1', text: 'Option 1' },
    { value: '2', text: 'Option 2' },
    { value: '3', text: 'Option 3' },
  ];

  ngDoCheck() {
    console.log('App check');
  }

  selectedValue = ''; // Intentionally not updating this when selection changes

  handleOnChange(event: any) {
    this.selectedValue = '3';
    console.log(event.target.value); // Logging the value but not saving it
  }
}

bootstrapApplication(App);

Stackblitz Demo

Summary of the Issue & Solution

The reason my dropdown still displayed the selected option—even when I didn't store the selected value in the parent—was due to Angular's change detection optimizations. Since the selectedValue in the parent never actually changed, Angular did not trigger a UI update, and the browser's native behavior retained the selected value.

To force Angular to reset the dropdown back to the default option, I had to explicitly introduce a temporary value change before resetting it back to ''.

Working Solution

handleOnChange(event: any) {
  console.log(event.target.value);

  // Temporarily set a different value to force change detection
  this.selectedValue = 'temp-value';

  // Use setTimeout to reset it in the next JavaScript cycle
  setTimeout(() => {
    this.selectedValue = ''; // Revert to default selection
  });
}

Using setTimeout() ensures that Angular detects both changes separately, allowing the dropdown to reset properly.

本文标签: