Describe the bug
Value Accessors for ino-checkbox handle the same events twice. Hence, checkedChange
-handler sin Angular are triggered twice. This leads to problems, if we want to implement toggle logic without explicitly check the emitted state.
This bug has a high potential to affect other components like radio, switch, ... as well (I did not test it tho).
To Reproduce
Integrate the @inovex.de/elements-angular
package on v2 and add the InoElementsModule
as import in the AppModule
of an Angular application (aká setup Angular for Elements).
Hence, define a component as:
import { Component } from "@angular/core";
@Component({
selector: "app-root",
templateUrl: "./app.component.html",
styleUrls: ["./app.component.css"]
})
export class AppComponent {
title = "CodeSandbox";
events = [];
checkedChangeHandler($event) {
this.events.push("Event triggered");
}
}
with the response template:
<!--The content below is only a placeholder and can be replaced.-->
<div>
<h1>
Welcome to {{ title }}!
</h1>
This is a test sandbox:
<ino-checkbox (checkedChange)="checkedChangeHandler($event)"></ino-checkbox>
<ul>
<li *ngFor="let event of events">{{event}}</li>
</ul>
</div>
Finally, check and uncheck the checkbox. As you can see, the event is thrown twice.
=> See the following codesandbox: https://codesandbox.io/s/eloquent-allen-fppgd
Expected behavior
EventHandlers are only triggered once.
Additional context
My insights are:
The event (checkedChange
) is only thrown once from ino-checkbox
, but handled twice within the BooleanControlValueAccessor
In the https://github.com/inovex/elements/blob/master/packages/elements-angular/elements/src/directives/control-value-accesors/value-accessor.directive.ts we have the following code:
handleChangeEvent(value: any) {
if (value !== this.lastValue) {
this.lastValue = value;
this.onChange(value);
this.writeValue(value);
}
}
Since this code is called twice for every checkbox state change, this leads to the following behavior:
- Call: Value is different from lastValue => writes the value to the native input
- Call: Value equals last value => do nothing.
However, the checkedChange
-Event is still handled by the defined checkedChange
-handler.
(No) Possible Solution
It is not possible, to simply change the abvoe code to:
handleChangeEvent(value: any) {
if (value !== this.lastValue) {
this.lastValue = value;
this.onChange(value);
this.writeValue(value);
}
else {
// ===> event.stopImmediatePropagation()
}
}
to avoid other handlers from being called, since Angular does not support this (angular/angular#9587).
Further, this "solution" doesn't answer the question of why the event is handled twice in the first place.
One possible path of investigation may be to use the native change
and to check the difference of our implementation to ionic https://github.com/ionic-team/ionic-framework/tree/master/angular/src/directives/control-value-accessors