admin管理员组文章数量:1289845
I'm trying to find a better approach to handle plex angular forms. The form is really big and I need to find an approach to reduce plexity.
Here is an example of form structure:
{
"fieldA" : ...,
"fieldB" : ...,
"fieldC" : ...,
"profile": {
"username": ...,
"email": ...,
"firstName": ...,
"lastName": ...,
...
},
"settings": {
"enableEmailNotification": ...,
"notificationsEmail": ..., // required when enableEmailNotification
...
},
...
}
There are cases when validators are changed on the fly, for example when enableEmailNotification=true
, ponent will add Required
validator to notificationsEmail
Here are researched options:
Option #0 - Classic
sample on github
This approach uses one form and one ponent.
Pros:
- A lot of code, but very simple
Cons:
- All logic is in one place. For my case this ponent bees too big and hard readable or maintainable
- UI also bees big enough
Option #1 - Passing FormGroup to subponent
sample on github
This approach sends formGroup
to inner ponents as @Input()
property.
Pros:
- Reduces part of the view
Cons:
- Form creation and validation rules are still on the parent ponent
- Only view size is reduced
- Validation logic is created in root ponent, but display errors in sub-ponent
Option #2 - Creating custom ControlValueAccessor
sample on github
Based on a this article we can create custom ControlValueAccessor which will return an object for a part of a form.
Pros:
- Splits form in multiple forms. Form can be splitten in smaller independent parts.
Cons:
- Keeping JS object for a form value. Doesn't look very good
I'm trying to find a better approach to handle plex angular forms. The form is really big and I need to find an approach to reduce plexity.
Here is an example of form structure:
{
"fieldA" : ...,
"fieldB" : ...,
"fieldC" : ...,
"profile": {
"username": ...,
"email": ...,
"firstName": ...,
"lastName": ...,
...
},
"settings": {
"enableEmailNotification": ...,
"notificationsEmail": ..., // required when enableEmailNotification
...
},
...
}
There are cases when validators are changed on the fly, for example when enableEmailNotification=true
, ponent will add Required
validator to notificationsEmail
Here are researched options:
Option #0 - Classic
sample on github
This approach uses one form and one ponent.
Pros:
- A lot of code, but very simple
Cons:
- All logic is in one place. For my case this ponent bees too big and hard readable or maintainable
- UI also bees big enough
Option #1 - Passing FormGroup to subponent
sample on github
This approach sends formGroup
to inner ponents as @Input()
property.
Pros:
- Reduces part of the view
Cons:
- Form creation and validation rules are still on the parent ponent
- Only view size is reduced
- Validation logic is created in root ponent, but display errors in sub-ponent
Option #2 - Creating custom ControlValueAccessor
sample on github
Based on a this article we can create custom ControlValueAccessor which will return an object for a part of a form.
Pros:
- Splits form in multiple forms. Form can be splitten in smaller independent parts.
Cons:
- Keeping JS object for a form value. Doesn't look very good
2 Answers
Reset to default 5My strategy for large plex forms is to have a wrapper ponent and sub ponents. each sub ponent has it's own form service and the wrapper has a master form service with the sub form services injected, consider this
@Component({
selector: 'form-wrapper',
template: `
<form [formGroup]="form" (submit)="save()">
<sub-form-a></sub-form-a>
<sub-form-b></sub-form-b>
<input type="submit" value="Submit Form">
</form>
`,
providers: [MasterFormService, FormAService, FormBService]
})
export class FormWrapper {
constructor(private formService: MasterFormService) { }
save() {
// whatever save actions here
}
}
@Component({ // form b poent is basically the same
selector: 'sub-form-a',
template: `
... whatever template belongs to form a ..
`
})
export class FormAComponent {
form: FormGroup
constructor(private formService: FormAService) {
this.form = this.formService.form;
// form a specific actions
}
}
@Injectable()
export class MasterFormService {
form: FormGroup;
constructor(private fb: FormBuilder, formAService: FormAService, formBService: FormBService) {
this.form = this.fb.group({
groupA: this.formAService.form,
groupB: this.formBService.form,
});
}
}
@Injectable() // formB service is basically the same
export class FormAService {
form: FormGroup;
constructor(private fb: FormBuilder) {
this.form = this.fb.group({
.. whatever fields belong to form a ..
});
}
}
this method creates highly reusable sub forms and lets you modularize / isolate form logic and templates. I often find that sub forms typically belong in more than one place anyway, so it keeps my code very DRY. In your example in particular, you can easily reuse the settings form and profile form ponents elsewhere in your application. One or twice I've even nested this structure again for an extremely plex form.
The con is that the structure may appear plex but you get used to it quick.
Personally, for large plex forms, I'd like to keep the form logic in one ponent, so it's easy to chain the observables, but use a couple of Services as helpers. Both for initializing the form, and handling the actions (returning new form values, Validation, authorization, etc)
- The logic/tracking is in the
FormComponent
- Initialization of the Form in is in the
FormInitService
- Actions are handled in one pr multiple
FormActionService(s)
FormComponent
export class FormComponent implements OnInit {
constructor(
private formActionService: FormActionService,
private formInitService: FormInitService
) { }
ngOnInit() {
this.form = this.FormInitService.getForm();
this._trackFieldA();
this._trackProfile();
}
// Track a single field
private _trackFieldA() {
this.form.controls.fieldA.valueChanges.pipe(
distinctUntilChanged((a, b) => JSON.stringify(a) === JSON.stringify(b))
).subscribe(fieldA => {
console.log('Field A Changed');
this.formActionService.doSomething();
});
}
// Track a group
// Use ['controls'] for nested controls to skip typechecking errors
private _trackProfile() {
bineLatest(
this.form.controls.profile['controls'].username.valueChanges,
this.form.controls.profile['controls'].email.valueChanges,
).pipe(
distinctUntilChanged((a, b) => JSON.stringify(a) === JSON.stringify(b))
).subscribe(profile => {
console.log('Some profile field changed');
this.formActionService.doSomething();
});
}
}
FormInitService
export class FormInitService {
constructor(
private formBuilder: FormBuilder
) { }
public getForm(): FormGroup {
return this.formBuilder.group({
fieldA: 'Some init value',
fieldB: 'Some init value',
profile: this.formBuilder.group({
username: 'Some init value',
email: 'Some init value',
...
}),
...
});
}
}
FormActionService
export class FormActionService {
public doSomething(): any | void {
console.log('Something')
}
}
You still have quite some code in the FormComponent and template, but it's really easy to read and maintain. Splitting in multiple Components can often bee very confusing, especially when working in teams, or when some (huge) refactor needs to be done.
本文标签: javascriptHandling big reactive forms in Angular 7Stack Overflow
版权声明:本文标题:javascript - Handling big reactive forms in Angular 7 - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1741398574a2376523.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论