-
Notifications
You must be signed in to change notification settings - Fork 33
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Custom change detection with onPush is broken #93
Comments
@zakhenry we should list our options here before working on a PR :)
|
With Angular v9 around the corner and Ivy, we might be able to do all that without having to create a module and pass the We should be able to mark the component as dirty by passing the component's reference (this) 🔥 So we should put that on hold as not having to setup a module is quite nice and if we can keep it that way we should probably |
We've made a PR recently at work where we also had to apply the fix with the <div [formGroup]="formGroup" [attr.data-ngx-sub-form-issue-93]="formGroup.valueChanges | async">
<-- ... -->
</div> We had to do <div
[formGroup]="formGroup"
[attr.data-ngx-sub-form-issue-93-1]="formGroup.valueChanges | async"
[attr.data-ngx-sub-form-issue-93-2]="formGroup.statusChanges | async"
>
<-- ... -->
</div> Which starts to be really painful especially when repeating that on multiple forms 😅 So we came up with a directive that lets us do: <div [ngxSubForm]="formGroup">
<-- ... -->
</div> The directive is the following: @Directive({
selector: '[ngxSubForm]',
providers: [{ provide: ControlContainer, useExisting: forwardRef(() => NgxSubFormDirective) }],
})
export class NgxSubFormDirective extends FormGroupDirective implements OnInit, OnDestroy {
// following line needs the disable as the type is defined in FormGroupDirective
// and overriding it throws an error
// tslint:disable-next-line: no-non-null-assertion
@Input('cncNgxSubForm') public form: FormGroup = null!;
constructor(
@Optional() @Self() @Inject(NG_VALIDATORS) validators: any[],
@Optional() @Self() @Inject(NG_ASYNC_VALIDATORS) asyncValidators: any[],
private cd: ChangeDetectorRef,
) {
super(validators, asyncValidators);
}
public ngOnInit(): void {
if (!this.valueChanges || !this.statusChanges) {
throw new Error('valueChanges and/or statusChanges not defined');
}
merge(this.valueChanges, this.statusChanges)
.pipe(
tap(() => this.cd.markForCheck()),
takeUntilDestroyed(this),
)
.subscribe();
}
public ngOnDestroy(): void {}
} I'd be glad to publish this into our package so everyone can just use it but remember, ngx-sub-form is just built with classes that you extends. We do not have any angular module 🙌 (for now, that day may come). It'd feel wrong to introduce a module for a temporary bug fix as, as stated in the previous comment, Ivy might let us run a CD cycle from a function call directly which would make that directive useless. In the meantime, I'd say use the quick workaround We're aware it's not ideal but will start dealing with that once Ivy let us go around it 😸 |
Small update, here's the directive we use:
But this is a temporary hack until we can come up with a better solution. Now that Angular 9 has been released for quite a while we could give a go to the function to run the CD directly (just like it's done by ngrx/components package). |
This is a major architecture change which is brought without any breaking change 😄! We've split up the code base in 2: Old one and new one. The old one hasn't moved at all but is now deprecated (not removed yet!). You can keep using the old one for a bit and have a smooth/incremental update to use the new API. Few changes that you have to note with the new API: - Only works with Angular 9 or more - The app needs to have Ivy activated (this is because we use `ɵmarkDirty` internally. If it ever gets removed we'll probably have to ask to provide the `ChangeDetectorRef` but we were able to around this for now!) - We got rid of inheritance 🙌 - Form errors on a FormArray are now an object instead of an array. Previously the array contained null values on all the fields without any error. It's now an object containing only the ones with errors and you can access them using the index Please start upgrading to the new API as soon as possible as we stop supporting the old API as of today and will remove it in a near release. This closes #171 for the major architectural changes and also the following issues as a result: - closes #82 - closes #86 - closes #93 - closes #133 - closes #143 - closes #144 - closes #149 - closes #160 - closes #168
🎉 This issue has been resolved in version 5.2.0-feat-rewrite.1 🎉 The release is available on: Your semantic-release bot 📦🚀 |
This is a major architecture change which is brought without any breaking change 😄! We've split up the code base in 2: Old one and new one. The old one hasn't moved at all but is now deprecated (not removed yet!). You can keep using the old one for a bit and have a smooth/incremental update to use the new API. Few changes that you have to note with the new API: - Only works with Angular 9 or more - The app needs to have Ivy activated (this is because we use `ɵmarkDirty` internally. If it ever gets removed we'll probably have to ask to provide the `ChangeDetectorRef` but we were able to around this for now!) - We got rid of inheritance 🙌 - Form errors on a FormArray are now an object instead of an array. Previously the array contained null values on all the fields without any error. It's now an object containing only the ones with errors and you can access them using the index Please start upgrading to the new API as soon as possible as we stop supporting the old API as of today and will remove it in a near release. This closes #171 for the major architectural changes and also the following issues as a result: - closes #82 - closes #86 - closes #93 - closes #133 - closes #143 - closes #144 - closes #149 - closes #160 - closes #168
Alright after trying it previsouly then reverting the fix, I finally gave this another go today. I've added I believe that by using All the e2e tests are passing correctly again |
This is a major architecture change which is brought without any breaking change 😄! We've split up the code base in 2: Old one and new one. The old one hasn't moved at all but is now deprecated (not removed yet!). You can keep using the old one for a bit and have a smooth/incremental update to use the new API. Few changes that you have to note with the new API: - Only works with Angular 9 or more - The app needs to have Ivy activated (this is because we use `ɵmarkDirty` internally. If it ever gets removed we'll probably have to ask to provide the `ChangeDetectorRef` but we were able to around this for now!) - We got rid of inheritance 🙌 - Form errors on a FormArray are now an object instead of an array. Previously the array contained null values on all the fields without any error. It's now an object containing only the ones with errors and you can access them using the index Please start upgrading to the new API as soon as possible as we stop supporting the old API as of today and will remove it in a near release. This closes #171 for the major architectural changes and also the following issues as a result: - closes #82 - closes #86 - closes #93 - closes #133 - closes #143 - closes #144 - closes #149 - closes #160 - closes #168
🎉 This issue has been resolved in version 5.2.0 🎉 The release is available on: Your semantic-release bot 📦🚀 |
Hey good news! We may not need markDirty anymore 🤔 |
Related to Angular issues with
ControlValueAccessor
andOnPush
components:Currently, setting a component to use the
OnPush
change detection strategy is not safe withngx-sub-form
as you'd end up with a "shift". If you type "Hello" in an input and then add "A" the displayed value would be "Hello". If after that you add a B, the value displayed would be "HelloA", etc. Late by 1 change basically.When calling the
onChange
hook from theControlValueAccessor
, Angular should run a change detection but it seems that it's not the case.Workaround
Not a beautiful one but at least simple and it's still possible to use
OnPush
...In order to trigger a change detection, we can use the async pipe with a value coming from the form. So for every form that has at least a child (otherwise it's not needed), you can do the following:
[attr.data-ngx-sub-form-issue-93]="formGroup.valueChanges | async"
.For e.g., from a sub component:
The text was updated successfully, but these errors were encountered: