Tenho um componente dropdown Angular ( CustomDropdownComponent
) que recebe uma lista de opções e um valor selecionado de seu componente pai via @Input()
. O pai também escuta as alterações de seleção usando @Output()
.
No entanto, intencionalmente não atualizei o selectedValue
no pai ao manipular o selectionChange
evento ( ) para ver se o menu suspenso reverteria para a seleção padrão. Surpreendentemente, o menu suspenso ainda atualiza para a opção recém-selecionada, mesmo que o estado pai permaneça inalterado.
Minhas perguntas:
- Por que o menu suspenso atualiza a opção selecionada mesmo que o pai não armazene o novo valor?
- Esse comportamento se deve à detecção de alterações do Angular ou ao funcionamento das vinculações de propriedades?
- Como posso garantir que o menu suspenso exiba apenas o que o pai fornece e não retenha uma seleção, a menos que seja explicitamente armazenada no pai?
Aqui está uma versão simplificada da minha implementação:
Componente pai ( app.component.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
}
}
Modelo de componente pai ( app.component.html
)
<app-custom-dropdown
[options]="options"
[selected]="selectedValue"
(selectionChange)="handleOnChange($event)">
</app-custom-dropdown>
Componente filho ( custom-dropdown.component.ts )
@Component({
selector: 'app-custom-dropdown',
templateUrl: './custom-dropdown.component.html',
styleUrls: ['./custom-dropdown.component.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
}
}
Modelo de componente filho ( custom-dropdown.component.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>
Meu raciocínio é este:
Você tem uma vinculação de propriedade,
[value]="selected"
o que significa que somente quando o valorselected
tiver uma alteração de valor real (1
->2
) a detecção de alterações será executada para a seleção e atualizará o DOM, mas no seu cenário, você nunca atualizaselected
o valor no filho nem no pai, então não há necessidade de a visualização atualizar quando o valor de entrada não muda.Se você verificar o código abaixo, verá que o HTML foi atualizado quando eu programei
selectedValue
o pai para3
, mas não em alterações subsequentes. Porque o valor vai de (''
--on first attempt-->3
--on subsequent changes-->3
(remains 3)) Então o HTML nunca é atualizado e mantém as alterações do usuário.Se você quiser obter algum tipo de reversão, terá que fazê-lo programaticamente.
Demonstração do Stackblitz
Resumo do problema e solução
O motivo pelo qual meu menu suspenso ainda exibia a opção selecionada — mesmo quando eu não armazenava o valor selecionado no pai — era devido às otimizações de detecção de alterações do Angular. Como o
selectedValue
no pai nunca mudou de fato, o Angular não acionou uma atualização de UI, e o comportamento nativo do navegador manteve o valor selecionado.Para forçar o Angular a redefinir o menu suspenso de volta para a opção padrão, tive que introduzir explicitamente uma alteração temporária de valor antes de redefini-lo de volta para
''
.Solução de trabalho
Usar setTimeout() garante que o Angular detecte ambas as alterações separadamente, permitindo que o menu suspenso seja redefinido corretamente.