-
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
No hook or observable member available to get latest form value after component initialised #86
Comments
Indeed there's no way for now to be aware when the form is patched by the parent. It would be nice to have a proper hook or observable for that but the current workaround is to override the |
I have the same type of trouble with the lib: to refresh sub-components when data are updated from an external source after the initialization (from a Rest api). |
Sure thing @cbleu When using That part is completely hidden and transparent when using ngx-sub-form, but as there's no hook (yet) to be warned when the value changes (patched by the parent) you can just hook yourself onto Something along the following lines: // ...
public writeValue(obj) {
super.writeValue(obj);
// do what you want here
}
// ... Is that enough for you now @cbleu? Hopefully it'll be useful until a proper feature/hook lands =) |
@zakhenry I've encountered that one quite a few times and it makes the component really verbose. Also to clarify the usage, in my case I'm passing an ID to a sub form and that sub form does access the store to retrieve a resource. When the ID changes locally (selection local to that component) it's all good. But when the ID changes upstream (from the parent) it's not fetching the new resource (at least not without the workaround). I think it's a legitimate use case, what do you think? If you agree, can you think of a good name for:
For the hook, we already have Or we let the current one and bundle that in the next breaking change otherwise |
Yea I think having a hook is useful - prob as simple as |
I'm fine with In my example above, I want to refresh the resource whenever the value as changed from the parent or locally. With hooks only I'd have to implement the fetch of the resource in both hooks. Plus, as rxjs is the default for fetching data in most cases (ngrx, http calls, etc) I think it'd be nice to have an observable that we can subscribe to. The data flow would be much nicer IMO. If we've got only a synchronous hook it means we'd have to subscribe there 😱 What do you think of:
|
Honestly, I think that is way overkill, and having a thinner interface at the cost of downstream verbosity is preferable. |
Here's a concrete example of the boilerplate required for this to work today: interface ResourceForm {
resourceId: string;
}
@Component({
selector: 'resource-control',
templateUrl: './resource-control.component.html',
styleUrls: ['./resource-control.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
providers: subformComponentProviders(ResourceControlComponent),
})
export class ResourceControlComponent extends NgxSubFormRemapComponent<
string,
ResourceForm
> {
private resourceIdFromParent$: Subject<string | null> = new Subject();
public resourceId$: Observable<string> = merge(
this.formGroupControls.resourceId.valueChanges,
this.resourceIdFromParent$
);
public resource$: Observable<Resource | undefined> = this.resourceId$.pipe(
switchMap((resourceId: string) =>
this.resourceFacade.getResource(resourceId)
)
);
constructor(private resourceFacade: ResourceFacade) {}
public writeValue(id: string | null): void {
super.writeValue(id);
this.resourceIdFromParent$.next(id);
}
// skipped everything related to ngx-sub-form on purpose to keep
// the verbose part I'm trying to get rid of only
} The way I think it should be: interface ResourceForm {
resourceId: string;
}
@Component({
selector: 'resource-control',
templateUrl: './resource-control.component.html',
styleUrls: ['./resource-control.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
providers: subformComponentProviders(ResourceControlComponent),
})
export class ResourceControlComponent extends NgxSubFormRemapComponent<
string,
ResourceForm
> {
public resource$: Observable<Resource | undefined> = this.anyFormUpdate$.pipe(
switchMap(formValue =>
this.resourceFacade.getResource(formValue.resourceId)
)
);
constructor(private resourceFacade: ResourceFacade) {}
// skipped everything related to ngx-sub-form on purpose to keep
// the verbose part I'm trying to get rid of only
} What would you do? |
I think if that is the use case, then we implement just that. i.e. have a single new observable value that is |
I'm (nearly) fine with that. I think it'll just be odd to leave the existing hook (onFormUpdate which is for local changes) without having another one to get updates whenever the form changes in any way. So after adding that observable, we should probably either:
Otherwise it's not really consistent to have sometimes observables, sometimes synchronous hooks |
Yea, I think that is fair, it does seem that |
I think CF implementation https://github.com/cloudnc/ngx-sub-form/blob/master/projects/ngx-sub-form/src/lib/ngx-sub-form.component.ts#L369-L370 I'm still unsure whether we should deprecate it or not. |
Another example of why I'm tempted to deprecate #127 |
If you patch your form and update a few values, this hook will be called multiple times: 1 time for each updated value. Which is not ideal. Also has a bug here #127 And I've given more details here: #86 (comment)
If you patch your form and update a few values, this hook will be called multiple times: 1 time for each updated value. Which is not ideal. Also has a bug here #127 And I've given more details here: #86 (comment)
If the goal is to make sure that value changes of the
This makes the changes available in the subform without interfering with the formGroups internal events / states etc. This does not solve the issue of I'm usually not the biggest fan of this overwriting approach, but in this case I feel it might be warranted as it would provide a transparent solution without the need for additional hooks etc. |
@ntziolis the override would be an issue as if you update the internal form when the parent calls |
BTW here's a small update of the workaround as I just encountered a bug: interface ResourceForm {
resourceId: string;
}
@Component({
selector: 'resource-control',
templateUrl: './resource-control.component.html',
styleUrls: ['./resource-control.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
providers: subformComponentProviders(ResourceControlComponent),
})
export class ResourceControlComponent extends NgxSubFormRemapComponent<
string,
ResourceForm
> {
private resourceIdFromParent$: ReplaySubject<string | null> = new ReplaySubject();
public resourceId$: Observable<string> = merge(
this.formGroupControls.resourceId.valueChanges,
this.resourceIdFromParent$
).pipe(shareReplay({ bufferSize: 1, refCount: true }));
public resource$: Observable<Resource | undefined> = this.resourceId$.pipe(
switchMap((resourceId: string) =>
this.resourceFacade.getResource(resourceId)
)
).pipe(shareReplay({ bufferSize: 1, refCount: true }));
constructor(private resourceFacade: ResourceFacade) {}
public writeValue(id: string | null): void {
super.writeValue(id);
this.resourceIdFromParent$.next(id);
}
// skipped everything related to ngx-sub-form on purpose to keep
// the verbose part I'm trying to get rid of only
} |
I had a longer conversation with a colleague and after thinking about it we actually like the fact that its not transparent and that one has to take action to react to external form changes seperately from internal changes. But we would love to have some syntactic sugar :) I was thinking something in the line of:
This would only be done for the properties on the additional type, hence no per impact if one doesn't need to access external value updates whicle providing easy access (using |
Looking into implementing a sample of the above I realized its not feasible to go the route of using a third interface to reduce the keys as we do not have runtime access to the keys (its different for FormInterface as we actually an object with all keys defined Controls). Given the above I see the following options:
From the above I prefer option 2 as it would cause almost no performance impact (vs option 1), while likely covering most use cases without having to grab data after getting the signal (vs option 3). In general, since the main use case for this is consuming it within the subform in a continuous manner I vote for returning a subject or an observable rather than just exposing a hook method as all that would do is move the work of implementing a subject from |
I fully agree here. Re-reading my comment and taking into account the fact that |
@maxime1992 These two observables would work for us. in fact I like the name In regards to anyformUpdate$: |
I would suggest a third option too - |
@zakhenry's I can't think of any use case right now, did you come across one or do you have one in mind? Also, reading your comment I realise that I may not have been clear enough in my previous comments but about the |
You are right, forgot about the remapping. Then maybe something like writeFormValue$, but I feel this is way less prescriptive than writeValue$. And you are right the typing prevents users from errors. So after thinking about it some more I’d still vote for writeValue$, but only because I cannot think of a better alternative. Damn naming is hard xD |
Still 100% on board with the additional observables. However I just realized this won't fix what initially raised this issue for us:
100% we do not want to create a circular data flow. With this in mind I relalized that there are edge cases when For example:
The same is true when the sub-form is initialized with Why is this crucial for us:
Right now that sync is broken as neither:
What do you think? Should this be dealt with in a separate issue since this is not about extending the api of ngx-sub-forms? |
To address my last comment I was thinking something like this: Only created a PR to see if there are side effects as I cant run CI locally anymore (windows). |
I have used patch package to monkey patch the changes I outlined above and I can confirm that the approach above does remove the state sync issues we have seen. Default values are now properly bubbled up after being set in the sub-form. |
Discussed with @zakhenry and what we should do is:
|
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
@maxime1992 is this issue actually fixed by the rewrite? I'm not sure how to get access to that observable? I think we need to expose this separately in the |
@zakhenry yes it's been solved. You can now simply subscribe to Whereas previously if a value changed upstream because You can easily test it by subscribing to |
…ly observer the current value of the component Closes #86
🎉 This issue has been resolved in version 6.0.0-feat-rewrite.5 🎉 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
…ly observer the current value of the component Closes #86
🎉 This issue has been resolved in version 5.2.0 🎉 The release is available on: Your semantic-release bot 📦🚀 |
There is currently
onFormUpdate()
hook andformGroup.valuechanges
observable, but they are not called when the component gets the value from external after finish initialization.There are indeed workarounds exist. But getting the latest form value after form initialization is still a legit scenario that is useful for this library.
The text was updated successfully, but these errors were encountered: