From 567005fb06cb11bb5e7e3bdac38bfd4a3615d4d6 Mon Sep 17 00:00:00 2001 From: Ophir Katz Date: Tue, 18 Apr 2023 20:06:11 +0300 Subject: [PATCH 1/6] added emitInitialValue --- projects/ngx-sub-form/src/lib/create-form.ts | 23 ++++++++++++++++++- .../src/lib/ngx-sub-form.types.ts | 1 + .../assassin-droid.component.ts | 7 +++--- .../droid-listing/droid-product.component.ts | 1 + .../listing-form/listing-form.component.ts | 2 +- src/app/main/listing/listing.component.ts | 1 + 6 files changed, 30 insertions(+), 5 deletions(-) diff --git a/projects/ngx-sub-form/src/lib/create-form.ts b/projects/ngx-sub-form/src/lib/create-form.ts index 90097533..51690cf6 100644 --- a/projects/ngx-sub-form/src/lib/create-form.ts +++ b/projects/ngx-sub-form/src/lib/create-form.ts @@ -153,7 +153,10 @@ export function createForm( const broadcastValueToParent$: Observable = transformedValue$.pipe( switchMap(transformedValue => { if (!isRoot(options)) { - return formGroup.valueChanges.pipe(delay(0)); + return formGroup.valueChanges.pipe( + delay(0), + tap(v => console.log('broadcast', v)), + ); } else { const formValues$ = options.manualSave$ ? options.manualSave$.pipe( @@ -194,6 +197,12 @@ export function createForm( ), ); + const emitInitialValueOnInit$: Observable = + // don't emit initial value by default + !isNullOrUndefined(options.emitInitialValueOnInit) || options.emitInitialValueOnInit + ? lifecyleHooks.afterViewInit + : EMPTY; + // components often need to know what the current value of the FormControl that it is representing is, usually for // display purposes in the template. This value is the composition of the value written from the parent, and the // transformed current value that was most recently written to the parent @@ -217,6 +226,18 @@ export function createForm( broadcastValueToParent$: registerOnChange$.pipe( switchMap(onChange => broadcastValueToParent$.pipe(tap(value => onChange(value)))), ), + emitInitialValueOnInit$: emitInitialValueOnInit$.pipe( + tap(_ => console.log('emitInitialValueOnInit', 'emitted')), + switchMap(() => registerOnChange$), + tap(_ => console.log('emitInitialValueOnInit', 'registerOnChange')), + switchMap(onChange => + broadcastValueToParent$.pipe( + tap(value => console.log('emitInitialValueOnInit', 'broadcastValueToParent', value)), + tap(value => onChange(value)), + ), + ), + tap(_ => console.log('emitInitialValueOnInit', 'broadcast')), + ), applyUpstreamUpdateOnLocalForm$: transformedValue$.pipe( tap(value => { handleFormArrays(formArrays, value, createFormArrayControl); diff --git a/projects/ngx-sub-form/src/lib/ngx-sub-form.types.ts b/projects/ngx-sub-form/src/lib/ngx-sub-form.types.ts index ed51a4c2..bdcee52a 100644 --- a/projects/ngx-sub-form/src/lib/ngx-sub-form.types.ts +++ b/projects/ngx-sub-form/src/lib/ngx-sub-form.types.ts @@ -78,6 +78,7 @@ export type NgxSubFormOptions< formControls: Controls; formGroupOptions?: FormGroupOptions; emitNullOnDestroy?: boolean; + emitInitialValueOnInit?: boolean; componentHooks?: ComponentHooks; // emit on this observable to mark the control as touched touched$?: Observable; diff --git a/src/app/main/listing/listing-form/droid-listing/assassin-droid/assassin-droid.component.ts b/src/app/main/listing/listing-form/droid-listing/assassin-droid/assassin-droid.component.ts index 60473c9a..72a5a542 100644 --- a/src/app/main/listing/listing-form/droid-listing/assassin-droid/assassin-droid.component.ts +++ b/src/app/main/listing/listing-form/droid-listing/assassin-droid/assassin-droid.component.ts @@ -25,10 +25,11 @@ export class AssassinDroidComponent { public form = createForm(this, { formType: FormType.SUB, formControls: { - color: new UntypedFormControl(null, { validators: [Validators.required] }), - name: new UntypedFormControl(null, { validators: [Validators.required] }), + color: new UntypedFormControl('#000000', { validators: [Validators.required] }), + name: new UntypedFormControl('r2d2', { validators: [Validators.required] }), droidType: new UntypedFormControl(DroidType.ASSASSIN, { validators: [Validators.required] }), - weapons: new UntypedFormControl([], { validators: [Validators.required] }), + weapons: new UntypedFormControl(['Axe'], { validators: [Validators.required] }), }, + emitInitialValueOnInit: true, }); } diff --git a/src/app/main/listing/listing-form/droid-listing/droid-product.component.ts b/src/app/main/listing/listing-form/droid-listing/droid-product.component.ts index 7f31f1f9..85277c54 100644 --- a/src/app/main/listing/listing-form/droid-listing/droid-product.component.ts +++ b/src/app/main/listing/listing-form/droid-listing/droid-product.component.ts @@ -38,6 +38,7 @@ export class DroidProductComponent { assassinDroid: new UntypedFormControl(null), droidType: new UntypedFormControl(null, { validators: [Validators.required] }), }, + emitInitialValueOnInit: true, toFormGroup: (obj: OneDroid): OneDroidForm => { return { protocolDroid: obj.droidType === DroidType.PROTOCOL ? obj : null, diff --git a/src/app/main/listing/listing-form/listing-form.component.ts b/src/app/main/listing/listing-form/listing-form.component.ts index c7803873..9d5d33e0 100644 --- a/src/app/main/listing/listing-form/listing-form.component.ts +++ b/src/app/main/listing/listing-form/listing-form.component.ts @@ -48,7 +48,7 @@ export class ListingFormComponent { manualSave$: this.manualSave$$, formControls: { vehicleProduct: new UntypedFormControl(null), - droidProduct: new UntypedFormControl(null), + droidProduct: new UntypedFormControl(null, Validators.required), listingType: new UntypedFormControl(null, Validators.required), id: new UntypedFormControl(null, Validators.required), title: new UntypedFormControl(null, Validators.required), diff --git a/src/app/main/listing/listing.component.ts b/src/app/main/listing/listing.component.ts index ed949949..ade92158 100644 --- a/src/app/main/listing/listing.component.ts +++ b/src/app/main/listing/listing.component.ts @@ -46,6 +46,7 @@ export class ListingComponent { } public upsertListing(listing: OneListing): void { + console.log('upsertListing', listing); this.listingService.upsertListing(listing); } } From 3289515aa23a6fb07d56c5568418ccfc58100ce5 Mon Sep 17 00:00:00 2001 From: Ophir Katz Date: Thu, 20 Apr 2023 11:40:30 +0300 Subject: [PATCH 2/6] Added emitInitialValueOnInit feature, works on example --- projects/ngx-sub-form/src/lib/create-form.ts | 32 +++++--------------- 1 file changed, 7 insertions(+), 25 deletions(-) diff --git a/projects/ngx-sub-form/src/lib/create-form.ts b/projects/ngx-sub-form/src/lib/create-form.ts index 51690cf6..9587707f 100644 --- a/projects/ngx-sub-form/src/lib/create-form.ts +++ b/projects/ngx-sub-form/src/lib/create-form.ts @@ -152,18 +152,18 @@ export function createForm( const broadcastValueToParent$: Observable = transformedValue$.pipe( switchMap(transformedValue => { + const valueChanges = formGroup.valueChanges.pipe( + options.emitInitialValueOnInit ? startWith(transformedValue) : tap(), + ); if (!isRoot(options)) { - return formGroup.valueChanges.pipe( - delay(0), - tap(v => console.log('broadcast', v)), - ); + return valueChanges.pipe(delay(0)); } else { const formValues$ = options.manualSave$ ? options.manualSave$.pipe( - withLatestFrom(formGroup.valueChanges), + withLatestFrom(valueChanges), map(([_, formValue]) => formValue), ) - : formGroup.valueChanges; + : valueChanges; // it might be surprising to see formGroup validity being checked twice // here, however this is intentional. The delay(0) allows any sub form @@ -183,7 +183,7 @@ export function createForm( return options.outputFilterPredicate(transformedValue, formValue); } - return !isEqual(transformedValue, formValue); + return options.emitInitialValueOnInit ?? !isEqual(transformedValue, formValue); }), options.handleEmissionRate ?? identity, ); @@ -197,12 +197,6 @@ export function createForm( ), ); - const emitInitialValueOnInit$: Observable = - // don't emit initial value by default - !isNullOrUndefined(options.emitInitialValueOnInit) || options.emitInitialValueOnInit - ? lifecyleHooks.afterViewInit - : EMPTY; - // components often need to know what the current value of the FormControl that it is representing is, usually for // display purposes in the template. This value is the composition of the value written from the parent, and the // transformed current value that was most recently written to the parent @@ -226,18 +220,6 @@ export function createForm( broadcastValueToParent$: registerOnChange$.pipe( switchMap(onChange => broadcastValueToParent$.pipe(tap(value => onChange(value)))), ), - emitInitialValueOnInit$: emitInitialValueOnInit$.pipe( - tap(_ => console.log('emitInitialValueOnInit', 'emitted')), - switchMap(() => registerOnChange$), - tap(_ => console.log('emitInitialValueOnInit', 'registerOnChange')), - switchMap(onChange => - broadcastValueToParent$.pipe( - tap(value => console.log('emitInitialValueOnInit', 'broadcastValueToParent', value)), - tap(value => onChange(value)), - ), - ), - tap(_ => console.log('emitInitialValueOnInit', 'broadcast')), - ), applyUpstreamUpdateOnLocalForm$: transformedValue$.pipe( tap(value => { handleFormArrays(formArrays, value, createFormArrayControl); From f631ce4003264cec7e11d27c6de3fb4d2816653d Mon Sep 17 00:00:00 2001 From: Ophir Katz Date: Thu, 20 Apr 2023 11:47:14 +0300 Subject: [PATCH 3/6] Removed unnecessary demo code, need to add tests --- .../assassin-droid/assassin-droid.component.ts | 7 +++---- .../listing-form/droid-listing/droid-product.component.ts | 1 - .../main/listing/listing-form/listing-form.component.ts | 2 +- src/app/main/listing/listing.component.ts | 1 - 4 files changed, 4 insertions(+), 7 deletions(-) diff --git a/src/app/main/listing/listing-form/droid-listing/assassin-droid/assassin-droid.component.ts b/src/app/main/listing/listing-form/droid-listing/assassin-droid/assassin-droid.component.ts index 72a5a542..60473c9a 100644 --- a/src/app/main/listing/listing-form/droid-listing/assassin-droid/assassin-droid.component.ts +++ b/src/app/main/listing/listing-form/droid-listing/assassin-droid/assassin-droid.component.ts @@ -25,11 +25,10 @@ export class AssassinDroidComponent { public form = createForm(this, { formType: FormType.SUB, formControls: { - color: new UntypedFormControl('#000000', { validators: [Validators.required] }), - name: new UntypedFormControl('r2d2', { validators: [Validators.required] }), + color: new UntypedFormControl(null, { validators: [Validators.required] }), + name: new UntypedFormControl(null, { validators: [Validators.required] }), droidType: new UntypedFormControl(DroidType.ASSASSIN, { validators: [Validators.required] }), - weapons: new UntypedFormControl(['Axe'], { validators: [Validators.required] }), + weapons: new UntypedFormControl([], { validators: [Validators.required] }), }, - emitInitialValueOnInit: true, }); } diff --git a/src/app/main/listing/listing-form/droid-listing/droid-product.component.ts b/src/app/main/listing/listing-form/droid-listing/droid-product.component.ts index 85277c54..7f31f1f9 100644 --- a/src/app/main/listing/listing-form/droid-listing/droid-product.component.ts +++ b/src/app/main/listing/listing-form/droid-listing/droid-product.component.ts @@ -38,7 +38,6 @@ export class DroidProductComponent { assassinDroid: new UntypedFormControl(null), droidType: new UntypedFormControl(null, { validators: [Validators.required] }), }, - emitInitialValueOnInit: true, toFormGroup: (obj: OneDroid): OneDroidForm => { return { protocolDroid: obj.droidType === DroidType.PROTOCOL ? obj : null, diff --git a/src/app/main/listing/listing-form/listing-form.component.ts b/src/app/main/listing/listing-form/listing-form.component.ts index 9d5d33e0..c7803873 100644 --- a/src/app/main/listing/listing-form/listing-form.component.ts +++ b/src/app/main/listing/listing-form/listing-form.component.ts @@ -48,7 +48,7 @@ export class ListingFormComponent { manualSave$: this.manualSave$$, formControls: { vehicleProduct: new UntypedFormControl(null), - droidProduct: new UntypedFormControl(null, Validators.required), + droidProduct: new UntypedFormControl(null), listingType: new UntypedFormControl(null, Validators.required), id: new UntypedFormControl(null, Validators.required), title: new UntypedFormControl(null, Validators.required), diff --git a/src/app/main/listing/listing.component.ts b/src/app/main/listing/listing.component.ts index ade92158..ed949949 100644 --- a/src/app/main/listing/listing.component.ts +++ b/src/app/main/listing/listing.component.ts @@ -46,7 +46,6 @@ export class ListingComponent { } public upsertListing(listing: OneListing): void { - console.log('upsertListing', listing); this.listingService.upsertListing(listing); } } From 0539c3e9ae87da9d6b3d57598e01af5586386d98 Mon Sep 17 00:00:00 2001 From: Ophir Katz Date: Fri, 21 Apr 2023 17:26:39 +0300 Subject: [PATCH 4/6] Updated README file to include documentation on emitInitialValueOnInit flag --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index b7cc5f55..de22f3ac 100644 --- a/README.md +++ b/README.md @@ -70,6 +70,7 @@ This function takes as parameter a configuration object and returns an object re | `manualSave$` | `Observable` | Optional | ✅ | ❌ | By default a root form will automatically broadcast all the form updates (through the `output$`) as soon as there's a change. If you wish to "save" the form only when you click on a save button for example, you can create a subject on your side and pass it here. Whenever you call `next` on your subject, assuming the form is valid, it'll broadcast te form value to the parent (through the `output$`) | | `outputFilterPredicate` | `(currentInputValue: FormInterface, outputValue: FormInterface) => boolean` | Optional | ✅ | ❌ | The default behaviour is to compare the current transformed value of `input$` with the current value of the form _(deep check)_, and if these are equal, the value won't be passed to `output$` in order to prevent the broadcast | | `handleEmissionRate` | `(obs$: Observable) => Observable` | Optional | ✅ | ❌ | If you want to control how frequently the form emits on the `output$`, you can customise the emission rate with this. Example: `handleEmissionRate: formValue$ => formValue$.pipe(debounceTime(300))` | +| `emitInitialValueOnInit`| `boolean` | Optional | ✅ | ✅ | Controls whether the form (root or sub) will emit the default values provided with the `FormControls` if they are valid. The default behavior is not to emit the default values | # Principles From 4b97f08aa16bbf9768b848f9506871ef8b4ec45f Mon Sep 17 00:00:00 2001 From: Ophir Katz Date: Fri, 21 Apr 2023 17:28:04 +0300 Subject: [PATCH 5/6] ran prettier linter --- README.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index de22f3ac..c628495a 100644 --- a/README.md +++ b/README.md @@ -61,16 +61,16 @@ This function takes as parameter a configuration object and returns an object re -| Key | Type | Optional or required | Root form | Sub form | What is it for? | -| ----------------------- | --------------------------------------------------------------------------- | -------------------- | --------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `formType` | `FormType` | Required | ✅ | ✅ | Defines the type of the form. Can either be `FormType.ROOT` or `FormType.SUB` | -| `disabled$` | `Observable` | Required | ✅ | ❌ | When this observable emits `true`, the whole form (including the root form and all the sub forms) will be disabled | -| `input$` | `Observable` | Required | ✅ | ❌ | A root form is a component in between the parent passing raw data and the form itself. This property is an observable that you must provide which will be used behind the scenes to update for you the form values | -| `output$` | `Subject` | Required | ✅ | ❌ | A root form is a component in between the parent passing raw data and the form itself. This property is an observable that you must provide which will be used behind the scenes to broadcast the form value to the parent when it changes | -| `manualSave$` | `Observable` | Optional | ✅ | ❌ | By default a root form will automatically broadcast all the form updates (through the `output$`) as soon as there's a change. If you wish to "save" the form only when you click on a save button for example, you can create a subject on your side and pass it here. Whenever you call `next` on your subject, assuming the form is valid, it'll broadcast te form value to the parent (through the `output$`) | -| `outputFilterPredicate` | `(currentInputValue: FormInterface, outputValue: FormInterface) => boolean` | Optional | ✅ | ❌ | The default behaviour is to compare the current transformed value of `input$` with the current value of the form _(deep check)_, and if these are equal, the value won't be passed to `output$` in order to prevent the broadcast | -| `handleEmissionRate` | `(obs$: Observable) => Observable` | Optional | ✅ | ❌ | If you want to control how frequently the form emits on the `output$`, you can customise the emission rate with this. Example: `handleEmissionRate: formValue$ => formValue$.pipe(debounceTime(300))` | -| `emitInitialValueOnInit`| `boolean` | Optional | ✅ | ✅ | Controls whether the form (root or sub) will emit the default values provided with the `FormControls` if they are valid. The default behavior is not to emit the default values | +| Key | Type | Optional or required | Root form | Sub form | What is it for? | +| ------------------------ | --------------------------------------------------------------------------- | -------------------- | --------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `formType` | `FormType` | Required | ✅ | ✅ | Defines the type of the form. Can either be `FormType.ROOT` or `FormType.SUB` | +| `disabled$` | `Observable` | Required | ✅ | ❌ | When this observable emits `true`, the whole form (including the root form and all the sub forms) will be disabled | +| `input$` | `Observable` | Required | ✅ | ❌ | A root form is a component in between the parent passing raw data and the form itself. This property is an observable that you must provide which will be used behind the scenes to update for you the form values | +| `output$` | `Subject` | Required | ✅ | ❌ | A root form is a component in between the parent passing raw data and the form itself. This property is an observable that you must provide which will be used behind the scenes to broadcast the form value to the parent when it changes | +| `manualSave$` | `Observable` | Optional | ✅ | ❌ | By default a root form will automatically broadcast all the form updates (through the `output$`) as soon as there's a change. If you wish to "save" the form only when you click on a save button for example, you can create a subject on your side and pass it here. Whenever you call `next` on your subject, assuming the form is valid, it'll broadcast te form value to the parent (through the `output$`) | +| `outputFilterPredicate` | `(currentInputValue: FormInterface, outputValue: FormInterface) => boolean` | Optional | ✅ | ❌ | The default behaviour is to compare the current transformed value of `input$` with the current value of the form _(deep check)_, and if these are equal, the value won't be passed to `output$` in order to prevent the broadcast | +| `handleEmissionRate` | `(obs$: Observable) => Observable` | Optional | ✅ | ❌ | If you want to control how frequently the form emits on the `output$`, you can customise the emission rate with this. Example: `handleEmissionRate: formValue$ => formValue$.pipe(debounceTime(300))` | +| `emitInitialValueOnInit` | `boolean` | Optional | ✅ | ✅ | Controls whether the form (root or sub) will emit the default values provided with the `FormControls` if they are valid. The default behavior is not to emit the default values | # Principles From d451c51fe136a876f6290762a270a54aba3f6019 Mon Sep 17 00:00:00 2001 From: Ophir Katz Date: Thu, 27 Apr 2023 16:53:48 +0300 Subject: [PATCH 6/6] testing broadcastDefaultValue --- .vscode/launch.json | 15 +++++ projects/ngx-sub-form/src/lib/create-form.ts | 59 +++++++++++++++++-- .../src/lib/ngx-sub-form.types.ts | 2 +- .../assassin-droid.component.ts | 7 ++- .../listing-form/listing-form.component.ts | 2 +- src/app/main/listing/listing.component.html | 1 + src/app/main/listing/listing.component.ts | 3 + 7 files changed, 78 insertions(+), 11 deletions(-) create mode 100644 .vscode/launch.json diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 00000000..1348ba4a --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,15 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "chrome", + "request": "launch", + "name": "Launch Chrome against localhost", + "url": "http://localhost:4200", + "webRoot": "${workspaceFolder}" + } + ] +} \ No newline at end of file diff --git a/projects/ngx-sub-form/src/lib/create-form.ts b/projects/ngx-sub-form/src/lib/create-form.ts index 9587707f..ff568974 100644 --- a/projects/ngx-sub-form/src/lib/create-form.ts +++ b/projects/ngx-sub-form/src/lib/create-form.ts @@ -6,6 +6,7 @@ import { combineLatest, concat, EMPTY, identity, merge, Observable, of, timer } import { delay, filter, + first, map, mapTo, shareReplay, @@ -150,20 +151,62 @@ export function createForm( shareReplay({ refCount: true, bufferSize: 1 }), ); + const broadcastDefaultValueToParent$: Observable = transformedValue$.pipe( + first(), + filter(() => options.emitDefaultValue ?? false), + switchMap(transformedValue => { + const defaultValue$ = of(transformedValue); + if (!isRoot(options)) { + return defaultValue$.pipe(delay(0)); + } else { + // it might be surprising to see formGroup validity being checked twice + // here, however this is intentional. The delay(0) allows any sub form + // components to populate values into the form, and it is possible for + // the form to be invalid after this process. In which case we suppress + // outputting an invalid value, and wait for the user to make the value + // become valid. + return defaultValue$.pipe( + filter(() => formGroup.valid), + delay(0), + filter(formValue => { + if (formGroup.invalid) { + return false; + } + + if (options.outputFilterPredicate) { + return options.outputFilterPredicate(transformedValue, formValue); + } + + return true; + }), + options.handleEmissionRate ?? identity, + ); + } + }), + map(value => + options.fromFormGroup + ? options.fromFormGroup(value) + : // if it's not a remap component, the ControlInterface === the FormInterface + (value as any as ControlInterface), + ), + ); + const broadcastValueToParent$: Observable = transformedValue$.pipe( switchMap(transformedValue => { - const valueChanges = formGroup.valueChanges.pipe( - options.emitInitialValueOnInit ? startWith(transformedValue) : tap(), - ); + const valueChanges = formGroup.valueChanges; if (!isRoot(options)) { - return valueChanges.pipe(delay(0)); + return valueChanges.pipe( + delay(0), + tap(v => console.log('not root', v)), + ); } else { const formValues$ = options.manualSave$ ? options.manualSave$.pipe( withLatestFrom(valueChanges), + tap(console.log), map(([_, formValue]) => formValue), ) - : valueChanges; + : valueChanges.pipe(tap(console.log)); // it might be surprising to see formGroup validity being checked twice // here, however this is intentional. The delay(0) allows any sub form @@ -172,6 +215,7 @@ export function createForm( // outputting an invalid value, and wait for the user to make the value // become valid. return formValues$.pipe( + tap(v => console.log('formValues$', v)), filter(() => formGroup.valid), delay(0), filter(formValue => { @@ -183,7 +227,7 @@ export function createForm( return options.outputFilterPredicate(transformedValue, formValue); } - return options.emitInitialValueOnInit ?? !isEqual(transformedValue, formValue); + return !isEqual(transformedValue, formValue); }), options.handleEmissionRate ?? identity, ); @@ -220,6 +264,9 @@ export function createForm( broadcastValueToParent$: registerOnChange$.pipe( switchMap(onChange => broadcastValueToParent$.pipe(tap(value => onChange(value)))), ), + broadcastDefaultValueToParent$: registerOnChange$.pipe( + switchMap(onChange => broadcastDefaultValueToParent$.pipe(tap(value => onChange(value)))), + ), applyUpstreamUpdateOnLocalForm$: transformedValue$.pipe( tap(value => { handleFormArrays(formArrays, value, createFormArrayControl); diff --git a/projects/ngx-sub-form/src/lib/ngx-sub-form.types.ts b/projects/ngx-sub-form/src/lib/ngx-sub-form.types.ts index bdcee52a..52c55214 100644 --- a/projects/ngx-sub-form/src/lib/ngx-sub-form.types.ts +++ b/projects/ngx-sub-form/src/lib/ngx-sub-form.types.ts @@ -78,7 +78,7 @@ export type NgxSubFormOptions< formControls: Controls; formGroupOptions?: FormGroupOptions; emitNullOnDestroy?: boolean; - emitInitialValueOnInit?: boolean; + emitDefaultValue?: boolean; componentHooks?: ComponentHooks; // emit on this observable to mark the control as touched touched$?: Observable; diff --git a/src/app/main/listing/listing-form/droid-listing/assassin-droid/assassin-droid.component.ts b/src/app/main/listing/listing-form/droid-listing/assassin-droid/assassin-droid.component.ts index 60473c9a..919a867a 100644 --- a/src/app/main/listing/listing-form/droid-listing/assassin-droid/assassin-droid.component.ts +++ b/src/app/main/listing/listing-form/droid-listing/assassin-droid/assassin-droid.component.ts @@ -24,11 +24,12 @@ export class AssassinDroidComponent { public form = createForm(this, { formType: FormType.SUB, + emitDefaultValue: true, formControls: { - color: new UntypedFormControl(null, { validators: [Validators.required] }), - name: new UntypedFormControl(null, { validators: [Validators.required] }), + color: new UntypedFormControl('#111111', { validators: [Validators.required] }), + name: new UntypedFormControl('hello', { validators: [Validators.required] }), droidType: new UntypedFormControl(DroidType.ASSASSIN, { validators: [Validators.required] }), - weapons: new UntypedFormControl([], { validators: [Validators.required] }), + weapons: new UntypedFormControl(['Axe'], { validators: [Validators.required] }), }, }); } diff --git a/src/app/main/listing/listing-form/listing-form.component.ts b/src/app/main/listing/listing-form/listing-form.component.ts index c7803873..9d5d33e0 100644 --- a/src/app/main/listing/listing-form/listing-form.component.ts +++ b/src/app/main/listing/listing-form/listing-form.component.ts @@ -48,7 +48,7 @@ export class ListingFormComponent { manualSave$: this.manualSave$$, formControls: { vehicleProduct: new UntypedFormControl(null), - droidProduct: new UntypedFormControl(null), + droidProduct: new UntypedFormControl(null, Validators.required), listingType: new UntypedFormControl(null, Validators.required), id: new UntypedFormControl(null, Validators.required), title: new UntypedFormControl(null, Validators.required), diff --git a/src/app/main/listing/listing.component.html b/src/app/main/listing/listing.component.html index 286594d2..a300aae3 100644 --- a/src/app/main/listing/listing.component.html +++ b/src/app/main/listing/listing.component.html @@ -1,5 +1,6 @@ Readonly +{{ listingOutput | json }}