-
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
markAllAsTouched() not propegated to subforms #155
Comments
Was thinking about this issue, but also more generally that there are various others arising from not being able to have access to the parent They all stem from the limited interface of the
Goal:
Note: |
Results of further investigation for how to hook into calls on the parent, sample
I'm aware that especially the second approach would require significant changes in how the lib works today. Right now this is meant merely to spark a discussion of what you guys think of this. |
So I took a swing at trying to see how exzessive it would be todo this:
I was slightly worried that the injection of the derived directive would get messed up, but no need it worked like a charm!
Below is the directive, there is an equivalent const controlNameBinding: any = {
provide: NgControl,
useExisting: forwardRef(() => SubFormNameDirective)
};
@Directive({ selector: '[subFormName]', providers: [controlNameBinding] })
export class SubFormNameDirective extends FormControlName implements OnInit {
@Input('subFormName') name!: string;
// this is copied from angular FormControlName directive
// https://github.com/angular/angular/blob/master/packages/forms/src/directives/reactive_directives/form_control_name.ts
constructor(
@Optional() @Host() @SkipSelf() parent: ControlContainer,
@Optional() @Self() @Inject(NG_VALIDATORS) validators: Array<Validator | ValidatorFn>,
@Optional() @Self() @Inject(NG_ASYNC_VALIDATORS) asyncValidators: Array<AsyncValidator | AsyncValidatorFn>,
@Optional() @Self() @Inject(NG_VALUE_ACCESSOR) valueAccessors: ControlValueAccessor[]
) {
super(parent, validators, asyncValidators, valueAccessors, null);
}
ngOnInit(): void {
const self = (this as unknown) as FormControlName;
if (self.valueAccessor instanceof NgxSubFormComponent) {
const subForm = self.valueAccessor;
const control = self.control;
const markAsTouched = control.markAsTouched.bind(
control
) as AbstractControl['markAsTouched'];
control.markAsTouched = (
opts: Parameters<AbstractControl['markAsTouched']>[0]
) => {
markAsTouched(opts);
subForm.formGroup.markAllAsTouched();
};
const markAsUntouched = control.markAsUntouched.bind(
control
) as AbstractControl['markAsUntouched'];
control.markAsUntouched = (
opts: Parameters<AbstractControl['markAsUntouched']>[0]
) => {
markAsUntouched(opts);
subForm.formGroup.markAsUntouched();
};
}
}
} What do you guys think? |
Hi @ntziolis , First of all congrats for the great job you guys are doing with ngx-sub-form. I started using it a few days ago, with some very complex forms where we were struggling to handle validation. Using ngx-sub-form was pretty straight forward and did solved the enormous boilerplate we had due to our deep component tree on those forms. But we soon noticed this problem with markAllAsTouched(): We use Angular Material, and although all fields were being correctly validated, it was not being reflected in the interface (since they were not being marked as touched on form submit). That was when we found this thread. We implemented your SubFormNameDirective, and noticed a few points that I would like to share with you:
Please let me know if there is any thing we can do to help with those issues, or if we are missing something. Thanks, guys! |
@RicardoArdito First off, me and my team really are just heavy users of ngx-sub-form as well, credits for the lib should go to the creators ;) In regards to your comments:
In general the Initially we thought we can get around this by using custom directives but that is not the case as we quickly ran into issues as you encountered as well. We need both a better way to intercept as well as propagate events / calls to the form controls on the sub form. Here are some requirements we have for a solutions:
With that in mind we are thinking this could be achieved by:
Right now I believe such solution would not require custom directives, but merely the use of What do you think? |
Hey guys, small update. We've had to focus on our app in the last few weeks but we will try to get back on ngx-sub-form a bit next week. Thanks for keeping up great discussions in the meantime, we'll have a look asap :) ! PS: Thanks for the kind words Ricardo, feels free to share more about your experience here: #112 ! |
We have the same use case as @RicardoArdito. I prefer my submit buttons to never be disabled and show errors only when the user tries to submit the form instead. But I can't as there is no way to access child controls from root form for now. @ntziolis, I follow you on the requirements. About the solution I don't know much about the code base but it seems right at a glance. |
Hey, we have the same problem. Our solution is simple marker directive for sub forms components. @Directive({
selector: '[rbSubForm]',
})
export class RbSubFormDirective implements OnInit, OnDestroy {
private destroy = new Subject<void>();
constructor(private ngControl: NgControl, private rbForm: RbFormDirective) {}
ngOnInit(): void {
this.rbForm
.onSubmit()
.pipe(takeUntil(this.destroy))
.subscribe(() => {
const valueAccessor = this.ngControl
.valueAccessor as NgxSubFormComponent<unknown>;
valueAccessor.formGroup.markAllAsTouched();
});
}
ngOnDestroy(): void {
this.destroy.next();
this.destroy.complete();
}
} Then we use it as follows: <rb-address-form
rbSubForm
id="permanent-address"
[formControlName]="formControlNames.permanentAddress"
></rb-address-form> Yes it is manual solution, but it serves us well. |
@MiroslavKral This (additional directive) was our initial approach as well and then we simply moved the logic into the However it does not work when wanting todo other things like reset the form (both data as well as touched state). While directive approach does make sure all formControls on all subForms are initially marked untouched, this gets reverted by the way the |
The more we we make use of the sub-form library it becomes clear that we need access to the actual form controls on a sub form to enable full control from the outside if needed (while we actively trying to avoid it where possible) I’m currently playing with creating a class derived from form group based on the requirements outlined above. Since this approach would allow to use formGroup of the sub form directly we wouldn’t need the sub-form-component class to handle things like on touched anymore. Trying to figure out if a change in the lib is required to make this work or if it’s possible to simply ignore what the lib does in regards to touched. Will report back here soon. |
Can we all please give a 👍 to the following issue: angular/angular#27315 ? If that was solved upstream we could implement this feature really easily ❤️ |
please share RbFormDirective
|
Hi everyone, today I was dealing with this problem and trying to use your ideas as inspiration I created the following gist that let me workaround the markAsTouched/markAllAsTouched problem. NgxSubForm markAllAsTouched fix All of your feedback is very preciated and I hope this could help someone of you. |
could you please provide a usage example
because it already has a directive with the same name [formGroup] |
Hi @elvispdosreis I hope you will be fine. I created this example to demonstrate how works the proposed workaround. I think It could be easily extended and generalized. NgxSubForm markAllAsTouched fix example Tell me if I can help with another example or case 🤟 |
Hi, I added minor changes to the example to add ngSubmit usage. |
Thanks is that correct?
|
Hi, It's ok, but I was reading again my code and I applied small refactorizations to improve the code readability. Basically I'm looking for a NgxSubForm instance inside any ControlValueAccessor (asuming this is in the root) of the FormGroup/FormControl. |
Hello 👋 I'm glad we've got some work around explained above above but as explained here, this would really need to be fixed upstream so I've marked this issue as I'll close this issue as there's nothing we can do more than offer work arounds, which has been done :) Thanks everyone for the brainstorming around the work arounds! |
I need help, I'm using your fix but it only marks the first form |
Repro:
markAllAsTouched()
Expected:
Actual:
Note:
touched
is not an event that gets propagated from parent to child (only the other way around registerOnTouched)The intent of this issue is to try and come up with a best practice to handle this and if possible to extend the library to assist in these cases.
The text was updated successfully, but these errors were encountered: