cloudnc / ngx-sub-form Goto Github PK
View Code? Open in Web Editor NEWUtility library for breaking down an Angular form into multiple components
Home Page: https://cloudnc.github.io/ngx-sub-form
License: MIT License
Utility library for breaking down an Angular form into multiple components
Home Page: https://cloudnc.github.io/ngx-sub-form
License: MIT License
When dealing with FormArray
, I believe a common use case is to click a button to add an empty entry in the array and later on from the UI fill it up.
Currently the definition is:
protected createFormArrayControl(
key: ArrayPropertyKey<StackedNonCuttingCylinders.AsObject> | undefined,
value: ArrayPropertyValue<StackedNonCuttingCylinders.AsObject>,
): FormControl
and value
doesn't accept an object with all the required keys but with null
values.
I think it should.
Anyone second that?
If formControls
is not define we end up with that error.
We should instead have a proper error for it.
In the above I have put some comments, remarks when global searching "@author" (case insensitive)
when we use:
protected getFormControls(): Controls<Spaceship> {
return {
name: new FormControl(),
builtInYear: new FormControl(),
group1: new FormGroup({
config: new FormControl(),
hehe: new FormControl('hehe'),
})
}
}
things start to fall apart, as the Stackblitz shows.
Also how do we type out such complex structures? We would need new generic abstractions for formGroup and formArray GITHUB.
As I have tried it with FormArray it failed completally, but i guess @maxime1992 is already working on something with FormArray.
Hi everyone 👋!
Out of curiosity, we'd love to heard from people using ngx-sub-form
whether you're using it for small side projects or larger projects at work 👍.
If you feel like sharing with us know, feel free to use the following template (or not):
- I/we use `ngx-sub-form` [for personal projects]/[at work (COMPANY_NAME), with at least X people using the library]
- I/we are using `ngx-sub-form` on prod for X projects that are used by more than X customers every [days/months/years]
- Searching for `from 'ngx-sub-form'` in the whole code base gives me X hits
- So far my/our experience with `ngx-sub-form` has been [awful/good/great/awesome]
- If I could vote for an epic feature it would be:
- Anything else you want to share with us:
(feel free to tweak the above as you please and answer only what can/want)
Thanks for taking the time to share 🙏!
When using NgxSubFormRemapComponent
and thus overriding
transformToFormGroup
transformFromFormGroup
I've noticed formGroup.value
isn't transformed correctly during the registerOnChange
phase.
Here
// this is required to correctly initialize the form value
this.onChange(this.formGroup.value);
it should be
// this is required to correctly initialize the form value
this.onChange(this.transformFromFormGroup(this.formGroup.value));
If we don't do this, the transformed value is emitted only on the first change.
Even with this workaround, it seems that lazy loading a component doesn't work. At least that happens when using the NG-ZORRO Tab's lazy loading mechanism.
this.subscription = this.formGroup.valueChanges
.pipe(
// this is required otherwise an `ExpressionChangedAfterItHasBeenCheckedError` will happen
// this is due to the fact that parent component will define a given state for the form that might
// be changed once the children are being initialized
delay(0),
ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: 'ng-pristine: true'. Current value: 'ng-pristine: false'.
Usage example:
<div [formGroup]="formGroup">
<input [formControlName]="formControlNames.name">
<div [formArrayName]="formControlNames.families">
<nz-tabset nzTabPosition="top" nzSize="small" nzType="card">
<nz-tab *ngFor="let c of families.controls; index as i" [nzTitle]="c.value.name">
<ng-template nz-tab> <!-- This -->
<app-family [formControlName]="i"></app-family>
</ng-template>
</nz-tab>
</nz-tabset>
</div>
</div>
I'm trying to understand, but with no luck 'til now.
Followup from #105 as that was more of a question but found a bug. (there's a stackblitz there that can be used to see the bug)
It appears that it's not possible to use [ngValue]
but [value]
works.
Docs: https://angular.io/api/forms/SelectControlValueAccessor
Not very clear but apparently [ngValue]
would be for objects while [value]
should be for strings.
Unsure why value is currently working.
Not 100% sure it's worth spending any time on this would still be good to have a clarification.
If anyone has any idea BTW, please let me know!
Is it possible to use ngx-sub-form
with template driven forms?
This means using ngModel, ngModelGroup + directive validation?
When using a formArray
it is not possible to use [formArrayName]="formControlNames.property"
It seems that this key is concatenating the property name as many time as there's values in the array
Encountered exception Error: Cannot find control with name: 'property,property'
I'd like to be able to handle default values.
I'd expect transformToFormGroup
to be run when the component is created but if the value it receives is null, nothing happens and we cannot set default values.
ERROR in node_modules/ngx-sub-form/lib/ngx-root-form.component.d.ts(18,40): error TS2707: Generic type 'NgxRootFormComponent<ControlInterface, FormInterface>' requires between 1 and 2 type arguments.
I'm using very strict typescript settings:
"noImplicitAny": true,
"noImplicitReturns": true,
"noImplicitThis": true,
"noUnusedParameters": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"strictPropertyInitialization": true,
I think that a very common thing with form is subscribing to the form value and also start that observable with the current form value.
Example of doing that with this lib:
this.formGroup.valueChanges
.pipe(
startWith(this.formGroupValues),
tap(() => this.doSomething())
);
Annoying part is that in doing so, we have to manage the subscription and the cleanup when the component's being destroyed.
I wonder if it would be worth having a method taking care of the whole observable/async part so that to react to a form change we would only need to do something like that:
@MyComponent({...})
class MyComponent extends NgxSubForm<FormInterface> {
protected getFormControls(): Controls<FormInterface> {
return {...};
}
protected onFormUpdate({ formValue, isInitialValue }: FormChange<FormInterface>) {
// do something with formValue, also possible to make a condition if you don't want
// to react to the initial value
}
}
@zakhenry what do you think?
In the following example:
export class RotorsFormComponent
extends NgxAutomaticRootFormComponent<Letter[], RotorsForm>
implements NgxFormWithArrayControls<RotorsForm> {
@DataInput()
@Input('rotors')
dataInput: Letter[] | null | undefined;
We've got an error:
ERROR in apps/enigma/src/app/rotors/rotors-form/rotors-form.component.ts(26,4): error TS2345: Argument of type 'RotorsFormComponent' is not assignable to parameter of type 'NgxRootFormComponent<any, any>'.
Types of property 'dataInput$' are incompatible.
Type 'BehaviorSubject<Letter[] | null | undefined>' is not assignable to type 'BehaviorSubject<Required<any> | null | undefined>'.
Type 'Required<any> | null | undefined' is not assignable to type 'Letter[] | null | undefined'.
Type 'Required<any>' is missing the following properties from type 'Letter[]': length, pop, push, concat, and 26 more.
This occurs as soon as we use an array on a top level form which is using the DataInput
decorator.
$ npm install ngx-sub-form
.....
$ ng serve
....
ERROR in ./node_modules/ngx-sub-form/fesm5/ngx-sub-form.js
Module not found: Error: Can't resolve 'lodash-es' in '/home/matt/WebstormProjects/food4fitness/node_modules/ngx-sub-form/fesm5'
ERROR in ./node_modules/ngx-sub-form/fesm5/ngx-sub-form.js
Module not found: Error: Can't resolve 'lodash-es/isEqual' in '/home/matt/WebstormProjects/food4fitness/node_modules/ngx-sub-form/fesm5'
$ npm install lodash-es
...
all is well
It looks like top level form (using one of the RootForm*) are not checking for validity before broadcasting the value:
If we set a global validator on the formGroup of a top level form, in the case that it's invalid it will still be emitted.
This is a tricky issue to repro and will probably only repro through a test to get the timing right.
But I just hit a case that drove me insane where my form was considered valid, while the value that was passed clearly wasn't.
@zakhenry and I identified the potential issue and it might be coming from the fact that we do:
this.subscription = this.formGroup.valueChanges
.pipe(
// ...
delay(0),
map(changes => {
this.onChange(changes)
}),
// ...
)
But apparently that value coming from the form might not be mutated like I thought it'd and thus, the validity check would succeed on the RootFormComponent because the form might now be valid while sending the previous value.
Is there a way for transformToFormGroup
in NgxSubFormRemapComponent
to return a promise or an observable to do async calls?
If not, can this be a feature request?
First of all, nice work! Really.
I was having a look at your Droids/Vehicles example, and a question emerged.
What do I do if I need to handle a FormArray
?
An example to clarify: what if I want the user to be able to pick multiple colors for a Spaceship? I'll have to deal with multiple (1...N
) color picker. This is one thing missing from the README.
Hi
It was just to suggest you to explicitly write the compatible version of Angular somewhere where it's easy to see it like the README ?
NB: Howerver today, we can see it in the Changelog file and the release tab in github :)
I'm thinking about something like:
Angular <=7: V2.7.1
Angular >=8: Latest
Thanks and nice job for the lib guys !
Related to Angular issues with ControlValueAccessor
and OnPush
components:
Currently, setting a component to use the OnPush
change detection strategy is not safe with ngx-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 the ControlValueAccessor
, 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:
<div [formGroup]="formGroup" [attr.data-ngx-sub-form-issue-93]="formGroup.valueChanges | async">
<-- ... -->
</div>
The ngOnDestroy
method
public ngOnDestroy(): void {
this.fg = null;
if (this.subscription) {
this.subscription.unsubscribe();
}
if (this.onChange) {
this.onChange(null);
}
}
currently sets the original FormControl
value to null
.
I'd say it's not really the responsibility of the library to set that value to null
, thus
if (this.onChange) {
this.onChange(null);
}
should be removed.
When the form is created, if emitInitialValueOnInit
is true
(defaults to true
...) then we call the onChange
callback:
ngx-sub-form/projects/ngx-sub-form/src/lib/ngx-sub-form.component.ts
Lines 199 to 201 in be938ff
This has for effect to set the current value as dirty
, here's an example on our demo app (at startup, on a given robot):
In most cases, this is just a bug and we should have the value set as pristine
.
BUT, there's one case where it shouldn't be pristine: If the sub component extends NgxSubFormRemapComponent
and one of the properties is somehow computed. It doesn't mean that this is impure but let say you pass an object with 3 values:
When you pass the initial values a: 1, b: 2, c: null
you'd expect to get back straight away a: 1, b: 2, c: 3
.
In which case, the control should be set as dirty because we may want to save the new value.
QUESTION:
Do we really want to handle that case?
If yes, then we should fix the wrong behavior by making a deep comparison.
If not, then we shouldn't broadcast the first value at all.
I know there is getFormGroupControlOptions
but this is validation for the entire formGroup right? (as indicated by (formGroup) => {}
) - FormGroup - level.
How can I use validation on a FormControl level?
Here in my stackblitz im using the same validatorFN validatorWithDeps
in a normal angular way and inside getFormControls() the ngx-sub-form way. How can I yield the same results? For ngx-sub-form its undefined. (because of scoping ...)
Steps to reproduce:
I've been rewriting an old form that grown mad in one of our app, it worked weel for 3 sub-forms but there is one I can't get working.
Angular tells me formControlName must be used with a parent formGroup directive. You'll want to add a formGroup directive and pass it an existing FormGroup instance (you can create one in your class)
, which I'm pretty sure the library's supposed to do automatically.
I've looked so many times for the difference between this subform and the others and I can't spot a difference.
By the way I'm on Angular: 8.2.7 if it makes any difference
<form [formGroup]="formGroup">
<app-exemption-context-form [formControlName]="formControlNames.context"></app-exemption-context-form>
<app-exemption-type-form [formControlName]="formControlNames.type"></app-exemption-type-form>
<app-exemption-justification-form [formControlName]="formControlNames.justification"></app-exemption-justification-form>
</form>
export class ExemptionFormComponent extends NgxAutomaticRootFormComponent<ExemptionFormData> implements OnInit {
@DataInput()
@Input()
dataInput: ExemptionFormData;
@Output()
dataOutput: EventEmitter<ExemptionFormData> = new EventEmitter();
protected getFormControls(): Controls<ExemptionFormData> {
return {
context: new FormControl(),
type: new FormControl(),
justification: new FormControl(),
};
}
ngOnInit() {
// By the way this is another issue that I'll have to fill,
// I couldn't set form's value without this
this.dataInput$.subscribe(d => this.formGroup.setValue(d));
this.formGroup.updateValueAndValidity();
}
}
<fieldset>
<legend>Type de dérogation</legend>
<mat-form-field appearance="outline">
<mat-select [formControlName]="formControlNames.type" placeholder="Type de dérogation" required>
<mat-option *ngFor="let type of exemptionTypes" [value]="type">
{{ type.name }} - {{ type.description }}
</mat-option>
</mat-select>
<mat-hint>...</mat-hint>
</mat-form-field>
<mat-form-field appearance="outline">
<mat-select placeholder="Équipe"
[compareWith]="idComparison"
[formControlName]="formControlNames.chargedTeam">
<mat-option *ngFor="let team of teams" [value]="team">
{{ team.name }}
</mat-option>
</mat-select>
<mat-hint *ngIf="teams.length > 1">...</mat-hint>
</mat-form-field>
</fieldset>
@Component({
selector: 'app-exemption-type-form',
template: `...`,
styles: [],
providers: subformComponentProviders(ExemptionTypeFormComponent)
})
export class ExemptionTypeFormComponent extends NgxSubFormComponent<ExemptionFormType> {
public exemptionTypes: ExemptionType[] = [];
public teams: Team[] = [];
protected getFormControls(): Controls<ExemptionFormType> {
return {
chargedTeam: new FormControl(null, { validators: [Validators.required] }),
type: new FormControl(null, { validators: [Validators.required] })
};
}
idComparison(o1: WithID, o2: WithID): boolean {
return o1.id === o2.id;
}
}
Thanks in advance for the help, and your work on the library 🙂
Would be nice to be able to quickly generate a form component with ng g
.
lets say I have to reuse a sub component OVER and OVER in my app. In this case a field with phoneNr and phoneNrConfirm
protected getFormControls(): Controls<Phone> {
return {
phone: new FormControl(null, { validators: [Validators.required] }),
phoneConfirm: new FormControl(null, { validators: [Validators.required] }),
};
}
it has INTERNAL validation (like password confirm)
public getFormGroupControlOptions(): FormGroupOptions<Phone> {
return {
validators: [
formGroup => {
if (formGroup.value.phone !== formGroup.value.phoneConfirm) {
return {
phoneMustMatch: true,
};
}
return null;
},
],
};
}
BUT there will also be EXTERNAL VALIDATION like
<p>london Number +44 123456</p>
<app-phone-form [formControlName]="formControlNames.phone" pattern="\+44[0-9]+"></app-phone-form>
<p>italian Number +39 123456</p>
<app-phone-form [formControlName]="formControlNames.phone2" pattern="\+39[0-9]+"></app-phone-form>
(of course a Validator.pattern(...)
) could be used in app-personal-details-form
. This would lead to the same problems.
string
and not an object like `phone: '123', phoneConfirm: '123'.phone: '123
is important. I only need phoneConfirm for UX and Validation.{
gender: 'Male',
phone: '123',
};
instead of
{
gender: 'Male',
phone: {
phone: '123',
phoneConfirm: '123'
}
};
(there is no valuable information in a Confirmation Field)
It would be nice to have a cherryPick()
method that will handle this issue. It will flat out the data object from subform into a primitive. Therefore the subforms value can be validated
with angulars default tools on a higher level.
In my real use case I have a {password + passwordConfirm + passwordStrengthIndicator} subcomponent and different password requirements that I would like to handle on a higher level.
As a workaround I can of course add a input to my app-phone-form
that will handle the EXTERNAL VALIDATION ... but maybe I can solve this differently.
@zakhenry I've had a thought working on a form this morning.
I think it'd be nice if the parent of the top level form component could just pass a boolean to a disabled
or readonly
property without having to manually create the input all the time.
Would be really handy for readonly forms.
What do you think?
https://stackblitz.com/edit/ngx-sub-form-form-update-field-test
I have a radio button hooked up to show/hide 2 different subforms. What I'd like to do is save the data of each subform so that it's not lost when the subform gets destroyed by switching to a different subform. To do this I need an event from the radio button, but it isn't appearing in onFormUpdate(). I have to use the valueChanges Observable on the control itself.
Thanks for the awesome library :-)
PR https://github.com/cloudnc/ngx-sub-form/pull/32/files added those 2 flags but documentation has been forgotten and we should fix that :)
It's been mentioned here #21 (comment)
I have to make some kind of "checkbox-group" for a project coded with IONIC 4 for a mobile app. And i will love use that very nice lib !
This widget will need to work exactly like a "select" with the "multivalues" option enabled. The idea behind the question is that we prefer the look and feel of a "checkbox list" than a "select" dropdown on a phone.
In fact i would love to have a component like "radiogroup" or "select" that directly display a checkbox group.
To do that, is it better to create this widget as a sub-form of my main form for each "checkbox-group" or to create a specific "widget" just for that ?
What is the best method to do that with ngx-sub-form ?
Explaining in a another issue how to build the library just made me realised that it should be part of the documentation for people who wants to participate to the project.
Hi,
Is it possible to reset formGroupValues and their corresponding subform to null?
I tried this:
this.formGroup.patchValue({ Quarter: null, CustomFrom: null, CustomTo: null, });
So the formGroupValues will change, but the formControl of the values don't.
After a quick look to this ngx-sub-form.component.ts the problem seems to be in line 202.
Is there a special way to patch some form-fields to NULL (I mean NULL, not an falsy value like it is described in the ngx-sub-form.component.ts)?
When dealing with polymorphic data and using the remap functions it'd be nice to have a oneOf
validator. It'd take as argument keys of the formGroup
so it'd also be type safe.
And it could look like the following:
public getFormGroupControlOptions(): FormGroupOptions<YourPolymorphicForm> {
return {
validators: [NgxSubFormValidators.OneOf('prop1', 'prop2', 'prop3')],
};
}
I dont see the benefit of using
[formControlName]="formControlNames.myControlName"
in comparison to just:
formControlName="myControlName"
Could you enlighten me please :~D ?
[ ] Regression (behavior that used to work and stopped working in a new release)
[ X] Bug report
[ ] Feature request
[ ] Documentation issue or request
[ ] Support request => Please do not submit support request here, instead see
Creating a form structure as below, with sub form "cat" having a subform for "appendages" similar to the OneListing[] approach taken in github demo.
{
"id": 100408,
"name": "My cat with one long arm with 6 fingers",
"type": "CAT",
"cat": {
"appendages": [
{
"fingers/digits": 6, //required
"length": 40127
}
],
"name": "HOMER"
},
I.e. the following components exist
export interface AnimalForm {
id: number;
name: string;
type: 'cat' | 'dog',
cat: CatForm
}
export class AnimalRootFormComponent extends NgxRootFormComponent<Animal, AnimalForm> {
export class CatSubFormComponent extends NgxSubFormRemapComponent<CatForm, CatForm> {
export class AppendagesFormComponent extends NgxSubFormRemapComponent<Appendage[], AppendagesForm> implements OnChanges {
export class AppendageFormComponent extends NgxSubFormRemapComponent<Appendage, Appendage> implements OnInit, OnDestroy {
The following sub forms are valid
appendages: Valid
appendage: Valid
cat: Invalid
cat.formGroup.controls.appendage.valid: Invalid
cat.formGroup.controls.appendage.errors = {
appendages: []
}
Disabling and enabling the form should result in a valid form.
Angular version: ~8.0.0
Still an issue
Browser:
- [ X] Chrome (desktop) version XX
- [ ] Chrome (Android) version XX
- [ ] Chrome (iOS) version XX
- [ ] Firefox version XX
- [ ] Safari (desktop) version XX
- [ ] Safari (iOS) version XX
- [ ] IE version XX
- [ ] Edge version XX
For Tooling issues:
- Node version: 10.9.0
- Platform: windows
I have a button to save the form outside the form component, I want to enable it only when the form is valid. This is easy if you have a reference to the formgroup: [disabled]="!form.valid"
If you'd expose this property of the form with a getter I can get this functionality back.
public get valid(): boolean {
return this.formGroup.valid;
}
If there is only one NgxRootFormComponent the formGroup is initially pristine, but after adding a NgxSubFormComponent the formGroup.pristine returns false. Check my stackblitz for an example. After deleting the app-spaceship-config-form sub-form formGroup.pristine returns true, as expected. When the sub-form is present and after pre filling the form, formGroup.pristine changes from false to true.
Tl;dr formGroup.pristine should be true even if there is a sub-form.
So, I'm not sure if this is an issue with ngx-sub-form
or my understanding of how to accomplish this. I want to be able to set the initial value of a select or radio button of an ngx-sub-form
's NgxAutomaticRootFormControl
or NgxRootFormControl
. I tried using the technique described on the blog https://dev.to/maxime1992/building-scalable-robust-and-type-safe-forms-with-angular-3nf9 where it mentions under spaceship-container.component.ts
to use an rxjs
Subject
and call it's .next()
method. This does work, but then if I change a value of the select/radio and then try to go back to the initial choice, with the auto method I get no output and with the manual method I get the previous value instead. It happens whether the value is a primitive type (like string) or an object or array.
I've tried to simplify showing this off in the following StackBlitz:
https://stackblitz.com/edit/ngx-sub-form-initial-next
The 4 forms are to show the different scenarios I figured would experience this problem, 2 with NgxAutomaticRootFormControl
and 2 with NgxRootFormControl
, each having a version for an array object as value and a string as value.
In app.component.ts
, I have a variable called doInitialNext
. If it is false
, no initial value is set and even going back to the null value of each select still emits the correct output. But when set to true
(as the example is set to), the value sent from ngAfterViewInit
is not emitted either automatically or manually when it is selected again later.
One other thing to note is that overriding emitInitialValueOnInit
to true
in any of the form components has no effect on this.
If this isn't a bug in ngx-sub-form
, then I'd like to know what I'm doing wrong (and perhaps the blog post should be edited to not include that). I've dug a tad into the code, though, and it seems that NgxRootFormComponent
's dataInputUpdated
is never called and the check in its onRegisterOnChangeHook
against this.dataInput$.value
is part of the problem. Perhaps I'm wrong, though.
There is currently onFormUpdate()
hook and formGroup.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.
Hi,
Is there a way to have NgxSubFormComponent<string>
that have only one FromControl and returns its value as a string rather than an object.
I want to warp some form fields like autocomplete in its own component and have a simple interface for it.
Is this possible or should I implement my own ControlValueAccessor
?
I noticed this.formGroup.value
is being used in ngx-sub-form.component.ts
rather than this.formGroup.getRawValue()
.
this.formGroup.value
will omit any disabled controls.
Demo - https://stackblitz.com/edit/ngx-sub-form-stepper-form-demo-s5ajvo *
Note how with
secondUnique: new FormControl({value:'1', disabled: true}),
that this is missing from the values.
*copy of original https://stackblitz.com/edit/ngx-sub-form-stepper-form-demo
I would be interested if this is intended behaviour? Note this hasn't come from my own use cases.
Sorry I wanted to open a new question since this is not about template driven forms this time.
I'm almost convinced to use you lib in my production. Looks very promising.
I have a stackoverflow question will a sub-form also work in that case? ... where I have multiple FormGroups in a FormArray with some validation and stuff. So the sub-form will be somewhere in a mat-step.
And lastly: Here on another SO Question ^_^. I want to make the Subform invalid/ Subform inputs when the Custom Input Component Validation "fails" (is invalid).
I know the parent form will be invalid if subform is, but is the opposite also possible with your library.
This means subform inputs will be INVALID when the parent element is.
Thank you for the time you spend already answering me!
You're using the ngOnInit
hook in the parent class, and ngOnDestroy
further up. If I too want to use them then that will overwrite yours. In this case I have to call super.ngOnInit()
/super.ngOnDestroy()
explicitly to make it work. Please emphasize this in the README.
public ngOnInit(): void {
super.ngOnInit();
}
public ngOnDestroy(): void {
super.ngOnDestroy();
}
Hello all <3,
here I forked a Stackblitz of yours.
When we click on the RED reset-button. The root-Forms values are being cleared but the values in the subforms still remain.
How can we reset()
the whole thing?
Should the root level form and all subforms be in total sync? two-way-bindings?
Should a new method propagateReset()
be introduced? That resets itself and all sub-forms down the tree.
In the readme, if you change
<div
class="crew-member"
formArrayName="crewMembers"
*ngFor="let crewMember of formGroupControls.crewMembers.controls; let i = index"
>
to
<div
class="crew-member"
[formArrayName]="formControlNames.crewMembers"
*ngFor="let crewMember of formGroupControls.crewMembers.controls; let i = index"
>
An error is thrown
I ran into a situation where I was updating the form value but the value wasn't appearing in the sub-form Turned out, I was updating the same object reference as ngx-sub-form had in it's formGroup.value. Therefore in the dataInput pipe, the call to isEqual was returning true and the subform wasn't being updated.
My workaround is to update the value with JSON.parse(JSON.stringify(obj)).
It seems like a bit of a hack...
So the question is, can you please make an example in the documentation of how to deal with updating individual fields of a model?
Would this be a valuable feature?
Starting point:
export class ValueComponent extends NgxSubFormComponent<Value> {
private value: Value;
protected formControls: Controls<Value> = {
id: new FormControl(),
name: new FormControl(),
value: new FormControl(),
multiplicity: new FormControl(),
type: new FormControl()
};
writeValue(value: Value): void {
this.value = value;
super.writeValue(value);
}
}
Problem: I don't need to manage all these fields using a FormGroup
.
I only need value
to be synchronized automatically.
The other static values will be taken via this.value
writeValue(value: Value): void {
this.value = value;
super.writeValue(value);
}
Result:
export class ValueComponent extends NgxSubFormComponent<Pick<Value, 'value'>> {
private value: Value;
protected formControls: Controls<Pick<Value, 'value'>> = {
value: new FormControl()
};
...
You can see I've used Pick
to choose a subset of the available fields.
Currently an error is thrown here
public writeValue(obj: any): void {
if (obj) {
if (!!this.formGroup) {
this.handleFormArrayControls(obj);
this.formGroup.setValue(this.transformToFormGroup(obj), { <--- Here
emitEvent: false,
});
because, obviously, the setValue
acts only on
{
value: FormControl
}
while obj
is
{
id: ...,
name: ...,
value: ...,
multiplicity: ...,
type: ...
}
The local FormGroup
doesn't have all the other FormControl
s for the properties of obj
.
There are typing related compilation errors with Ivy. With the most strict typescript settings:
"noImplicitAny": true,
"noImplicitReturns": true,
"noImplicitThis": true,
"noUnusedParameters": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"strictPropertyInitialization": true,
ERROR in src/.../forms/contact-form.component.html:1:7 - error TS2322: Type 'TypedFormGroup<ContactForm>' is not assignable to type 'FormGroup | undefined'.
Type 'TypedFormGroup<ContactForm>' is missing the following properties from type 'FormGroup': _parent, _asyncValidationSubscription, _updateAncestors, _setInitialStatus, and 4 more.
1 <form [formGroup]="formGroup">
src/.../forms/contact-form.component.html:31:12 - error TS2322: Type 'AbstractControl' is not assignable to type 'FormControl'.
31 <input [formControl]="formGroupControls.email" type="text" name="email" placeholder="Email" />
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~0m
Trying to get the top level form disabled in the following case (where currentStateRotors
is an array):
<app-rotors-form
*ngIf="(currentStateRotors$ | async) as currentStateRotors"
[rotors]="currentStateRotors"
[disabled]="true"
></app-rotors-form>
The disabled property is not taken into account.
It should loop over the array and disable all the controls from the array.
I wrote some E2E tests to catch errors in the demo app, and they were working locally but not on CI.
Turns out the difference is that on CI it's testing the app in prod mode with build optimizer turned ON.
I suspect the error is coming from the fact that our formGroup
property (within the lib) is considered pure by the build optimizer, while it's not. As a result, some sub forms are not correctly populated with data.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.