You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
import{Directive,Input,Output}from'@angular/core';import{BehaviorSubject}from'rxjs';exportconstcreateModel=()=>{conststate$=newBehaviorSubject(true);constprocessing$=newBehaviorSubject(false);consttoggle=async(newState=!state$.getValue()): Promise<void>=>{console.log('Beginning toggle, newState = '+newState);// Note that uncommenting the following line is a workaround for this issue:// await Promise.resolve();processing$.next(true);state$.next(newState);awaitnewPromise((resolve)=>setTimeout(resolve,1000));processing$.next(false);console.log('Ending toggle, newState = '+newState);};return{
state$,
processing$,
toggle,};};
@Directive({selector: '[myDirective]',exportAs: 'myDirective',})exportclassMyDirective{privatemodel=createModel();
@Input('myDirective')setstate(value: boolean){this.model.toggle(value);}
@Output('myDirectiveChange')stateChange=this.model.state$;processing=this.model.processing$;toggle(){returnthis.model.toggle();}}
When clicking on the "Toggle" button in the component, everything works as expected:
The first two lines state and d.state have the same boolean value (the opposite of the previous value), and the third line has processing = true
After 1s, the third line switches back to processing = false
When clicking on the "Toggle by binding" button, the same toggle method from the model is triggered, so the same behavior as before would be expected. However, Angular only refreshes the first line (state) and does not update the d.state and processing lines, even though they changed. After 1s, the second line (d.state) is updated with the correct value and the third line did not change at all (now that the value of processing is back to false).
Please provide a link to a minimal reproduction of the bug
This is working as expected, except for #45612 where no error is thrown because this is an OnPush component.
The state changes that occur from within the directive after its input binding changes happen after the change detector has already updated the DOM with the values, given that those values occur in the view before assigning the directive input.
This is a typical ExpressionHasChangedAfterItHasBeenChecked error, which would surface if the component is not OnPush. It's generally bad practice to emit into an output when assigning an input, as doing so breaks unidirectional dataflow. This could be masked by making the EventEmitter asynchronous (by passing true as first constructor argument) but that will trigger another round of change detection.
Closing as this is working as expected and #45612 is tracking the issue for OnPush components.
Which @angular/* package(s) are the source of the bug?
core
Is this a regression?
No
Description
Here is a component:
It uses the following directive:
When clicking on the "Toggle" button in the component, everything works as expected:
state
andd.state
have the same boolean value (the opposite of the previous value), and the third line hasprocessing = true
processing = false
When clicking on the "Toggle by binding" button, the same
toggle
method from the model is triggered, so the same behavior as before would be expected. However, Angular only refreshes the first line (state
) and does not update thed.state
andprocessing
lines, even though they changed. After 1s, the second line (d.state
) is updated with the correct value and the third line did not change at all (now that the value ofprocessing
is back tofalse
).Please provide a link to a minimal reproduction of the bug
https://stackblitz.com/edit/angular-ivy-liytcm?file=src/app/app.component.ts
Please provide the exception or error you saw
Please provide the environment you discovered this bug in (run
ng version
)Anything else?
No response
The text was updated successfully, but these errors were encountered: