Skip to content

Commit

Permalink
feat: fix: #585 #531
Browse files Browse the repository at this point in the history
fix: #585 #531

fix: #585, fix #531
  • Loading branch information
foxhound87 committed Mar 26, 2023
1 parent f5ce963 commit e59727c
Show file tree
Hide file tree
Showing 15 changed files with 101 additions and 48 deletions.
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
# 5.5.0 (next)

- Updated add()/del()/update() actions to handle `changed` field prop.
(not triggering anymore `onChange` when using add(), use `onAdd`/`onDel` hooks instead).
- Updated `changed` computed prop behavior for nested fields and Event Hooks triggering.
- Events Hooks now are triggered also from actions if not used Event Handlers.

fix: #585 #531

# 5.4.1 (next)

fix: #371 #399 #408
Expand Down
47 changes: 29 additions & 18 deletions src/Base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import {
parseCheckArray,
parseCheckOutput,
pathToFieldsTree,
defaultClearValue,
} from "./parser";
import { FieldPropsEnum } from "./models/FieldProps";
import { OptionsEnum } from "./models/OptionsModel";
Expand Down Expand Up @@ -135,7 +136,7 @@ export default class Base implements BaseInterface {

get changed(): number {
return !_.isNil(this.path) && this.hasNestedFields
? this.reduce((acc: number, field: FieldInterface) => (acc + field.changed), 0)
? (this.reduce((acc: number, field: FieldInterface) => (acc + field.changed), 0) + this.$changed)
: this.$changed;
}

Expand Down Expand Up @@ -165,7 +166,7 @@ export default class Base implements BaseInterface {
onClear = (...args: any): any =>
this.execHandler(FieldPropsEnum.onClear, args, (e: Event) => {
e.preventDefault();
(this as any).clear(true);
(this as any).clear(true, false);
});

/**
Expand All @@ -174,7 +175,7 @@ export default class Base implements BaseInterface {
onReset = (...args: any): any =>
this.execHandler(FieldPropsEnum.onReset, args, (e: Event) => {
e.preventDefault();
(this as any).reset(true);
(this as any).reset(true, false);
});

/**
Expand All @@ -183,7 +184,7 @@ export default class Base implements BaseInterface {
onSubmit = (...args: any): any =>
this.execHandler(FieldPropsEnum.onSubmit, args, (e: Event, o = {}) => {
e.preventDefault();
this.submit(o);
this.submit(o, false);
});

/**
Expand All @@ -192,7 +193,7 @@ export default class Base implements BaseInterface {
onAdd = (...args: any): any =>
this.execHandler(FieldPropsEnum.onAdd, args, (e: Event, val: any) => {
e.preventDefault();
this.add($isEvent(val) ? null : val);
this.add($isEvent(val) ? null : val, false);
});

/**
Expand All @@ -201,7 +202,7 @@ export default class Base implements BaseInterface {
onDel = (...args: any): any =>
this.execHandler(FieldPropsEnum.onDel, args, (e: Event, path: string) => {
e.preventDefault();
this.del($isEvent(path) ? this.path : path);
this.del($isEvent(path) ? this.path : path, false);
});

/******************************************************************
Expand Down Expand Up @@ -305,7 +306,8 @@ export default class Base implements BaseInterface {
/**
Submit
*/
submit(o: any = {}): Promise<any> {
submit(o: any = {}, execHook: boolean = true): Promise<any> {
execHook && this.execHook(FieldPropsEnum.onSubmit, o);
this.$submitting = true;
this.$submitted += 1;

Expand Down Expand Up @@ -395,14 +397,19 @@ export default class Base implements BaseInterface {

if (!_.isNil($field) && !_.isUndefined(field)) {
if (_.isArray($field.values())) {
let n: number = _.max(_.map(field.fields, (f, i) => Number(i))) ?? -1;
const n: number = _.max(_.map(field.fields, (f, i) => Number(i))) ?? -1;
_.each(getObservableMapValues($field.fields), ($f) => {
if (Number($f.name) > n) $field.fields.delete($f.name);
if (Number($f.name) > n) {
$field.$changed ++;
$field.state.form.$changed ++;
$field.fields.delete($f.name);
}
});
}
if (field?.fields) {
const fallback = this.state.options.get(OptionsEnum.fallback);
if (!fallback && $field.fields.size === 0 && this.state.struct().findIndex(s => s.startsWith($field.path.replace(/\.\d+\./, '[].') + '[]')) < 0) {
const x = this.state.struct().findIndex(s => s.startsWith($field.path.replace(/\.\d+\./, '[].') + '[]'));
if (!fallback && $field.fields.size === 0 && x < 0) {
$field.value = parseInput($field.$input, {
separated: _.get(raw, $path),
});
Expand All @@ -421,6 +428,8 @@ export default class Base implements BaseInterface {
// get full path when using update() with select() - FIX: #179
const $newFieldPath = _.trimStart([this.path, $path].join("."), ".");
// init field into the container field
$container.$changed ++;
$container.state.form.$changed ++;
$container.initField($key, $newFieldPath, field, true);
} else if (recursion) {
if (_.has(field, "fields") && !_.isNil(field.fields)) {
Expand Down Expand Up @@ -529,7 +538,7 @@ export default class Base implements BaseInterface {
if (deep && this.hasNestedFields) this.deepSet(prop, data, "", true);
else _.set(this, `$${prop}`, data);

if (prop === 'value') {
if (prop === FieldPropsEnum.value) {
this.$changed ++;
this.state.form.$changed ++;
}
Expand All @@ -556,7 +565,7 @@ export default class Base implements BaseInterface {
const isStrict = this.state.options.get(OptionsEnum.strictUpdate, this);

if (_.isNil(data)) {
this.each((field: any) => field.clear(true));
this.each((field: any) => field.$value = defaultClearValue({ value: field.$value }));
return;
}

Expand All @@ -583,7 +592,7 @@ export default class Base implements BaseInterface {
/**
Add Field
*/
add(obj: any): any {
add(obj: any, execEvent: boolean = true): any {
if (isArrayOfObjects(obj)) {
_.each(obj, (values) =>
this.update({
Expand All @@ -593,7 +602,8 @@ export default class Base implements BaseInterface {

this.$changed ++;
this.state.form.$changed ++;
return this.execHook(FieldPropsEnum.onChange);
execEvent && this.execHook(FieldPropsEnum.onAdd);
return;
}

let key;
Expand All @@ -607,14 +617,14 @@ export default class Base implements BaseInterface {

this.$changed ++;
this.state.form.$changed ++;
this.execHook(FieldPropsEnum.onChange);
execEvent && this.execHook(FieldPropsEnum.onAdd);
return field;
}

/**
Del Field
*/
del($path: string | null = null) {
del($path: string | null = null, execEvent: boolean = true) {
const isStrict = this.state.options.get(OptionsEnum.strictDelete, this);
const path = parsePath($path ?? this.path);
const fullpath = _.trim([this.path, path].join("."), ".");
Expand All @@ -627,14 +637,15 @@ export default class Base implements BaseInterface {
throwError(fullpath, null, msg);
}

this.$changed ++;
this.state.form.$changed ++;
container.$changed ++;
container.state.form.$changed ++;

if (this.state.options.get(OptionsEnum.softDelete, this)) {
return this.select(fullpath).set("deleted", true);
}

container.each((field) => field.debouncedValidation.cancel());
execEvent && this.execHook(FieldPropsEnum.onDel);
return container.fields.delete(last);
}

Expand Down
19 changes: 12 additions & 7 deletions src/Field.ts
Original file line number Diff line number Diff line change
Expand Up @@ -358,11 +358,13 @@ export default class Field extends Base implements FieldInterface {
}

get isDirty(): boolean {
return !_.isNil(this.initial) && !_.isEqual(this.initial, this.value);
const value = this.changed ? this.value : this.initial;
return !_.isNil(this.initial) && !_.isEqual(this.initial, value);
}

get isPristine(): boolean {
return !_.isNil(this.initial) && _.isEqual(this.initial, this.value);
const value = this.changed ? this.value : this.initial;
return !_.isNil(this.initial) && _.isEqual(this.initial, value);
}

get isEmpty(): boolean {
Expand Down Expand Up @@ -427,7 +429,8 @@ export default class Field extends Base implements FieldInterface {
? this.onDrop(...args)
: this.execHandler(FieldPropsEnum.onChange, args, this.sync);

onToggle = (...args: any) => this.execHandler(FieldPropsEnum.onToggle, args, this.sync);
onToggle = (...args: any) =>
this.execHandler(FieldPropsEnum.onToggle, args, this.sync);

onBlur = (...args: any) =>
this.execHandler(
Expand Down Expand Up @@ -653,7 +656,8 @@ export default class Field extends Base implements FieldInterface {
if (deep) this.each((field: any) => field.resetValidation());
}

clear(deep: boolean = true): void {
clear(deep: boolean = true, execHook: boolean = true): void {
execHook && this.execHook(FieldPropsEnum.onClear);
this.$clearing = true;
this.$touched = false;
this.$blurred = false;
Expand All @@ -669,7 +673,8 @@ export default class Field extends Base implements FieldInterface {
}) : this.resetValidation(deep);
}

reset(deep: boolean = true): void {
reset(deep: boolean = true, execHook: boolean = true): void {
execHook && this.execHook(FieldPropsEnum.onReset);
this.$resetting = true;
this.$touched = false;
this.$blurred = false;
Expand All @@ -689,7 +694,6 @@ export default class Field extends Base implements FieldInterface {
}

focus(): void {
// eslint-disable-next-line
this.state.form.each((field: any) => (field.autoFocus = false));
this.autoFocus = true;
}
Expand Down Expand Up @@ -775,7 +779,8 @@ export default class Field extends Base implements FieldInterface {
throw new Error("The update() method accepts only plain objects.");
}
const fallback = this.state.options.get(OptionsEnum.fallback);
if (!fallback && this.fields.size === 0 && this.state.struct().findIndex(s => s.startsWith(this.path.replace(/\.\d+\./, '[].') + '[]')) < 0) {
const x = this.state.struct().findIndex(s => s.startsWith(this.path.replace(/\.\d+\./, '[].') + '[]'));
if (!fallback && this.fields.size === 0 && x < 0) {
this.value = parseInput(this.$input, {
separated: fields,
});
Expand Down
10 changes: 6 additions & 4 deletions src/Form.ts
Original file line number Diff line number Diff line change
Expand Up @@ -199,18 +199,20 @@ export default class Form extends Base implements FormInterface {
/**
Clear Form Fields
*/
clear(): void {
clear(deep: boolean = true, execHook: boolean = true): void {
execHook && this.execHook(FieldPropsEnum.onClear);
this.$touched = false;
this.$changed = 0;
this.each((field: FieldInterface) => field.clear(true));
this.each((field: FieldInterface) => field.clear(deep));
}

/**
Reset Form Fields
*/
reset(): void {
reset(deep: boolean = true, execHook: boolean = true): void {
execHook && this.execHook(FieldPropsEnum.onReset);
this.$touched = false;
this.$changed = 0;
this.each((field: FieldInterface) => field.reset(true));
this.each((field: FieldInterface) => field.reset(deep));
}
}
2 changes: 1 addition & 1 deletion src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ const $try = (...args: any) => {
args.map(
(
val: any // eslint-disable-line
) => found === null && !_.isNil(val) && (found = val)
) => found === null && !_.isUndefined(val) && (found = val)
);

return found;
Expand Down
2 changes: 1 addition & 1 deletion tests/computed/flat.isDirty.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export default ($) => {
it('$G isDirty should be false', () => expect($.$G.isDirty).to.be.false);
it('$H isDirty should be false', () => expect($.$H.isDirty).to.be.false);
it('$I isDirty should be false', () => expect($.$I.isDirty).to.be.false);
it('$L isDirty should be true', () => expect($.$L.isDirty).to.be.true);
it('$L isDirty should be false', () => expect($.$L.isDirty).to.be.false); //
it('$M isDirty should be true', () => expect($.$M.isDirty).to.be.true);
it('$N isDirty should be false', () => expect($.$N.isDirty).to.be.false);
});
Expand Down
2 changes: 1 addition & 1 deletion tests/computed/flat.isPristine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export default ($) => {
it('$G isPristine should be true', () => expect($.$G.isPristine).to.be.true);
it('$H isPristine should be true', () => expect($.$H.isPristine).to.be.true);
it('$I isPristine should be true', () => expect($.$I.isPristine).to.be.true);
it('$L isPristine should be false', () => expect($.$L.isPristine).to.be.false);
it('$L isPristine should be true', () => expect($.$L.isPristine).to.be.true); //
it('$M isPristine should be false', () => expect($.$M.isPristine).to.be.false);
it('$N isPristine should be true', () => expect($.$N.isPristine).to.be.true);
});
Expand Down
8 changes: 4 additions & 4 deletions tests/data/_.fixes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@ import $454 from "./forms/fixes/form.454";
import $518 from "./forms/fixes/form.518";
import $376 from "./forms/fixes/form.376";

// import $585 from "./forms/fixes/form.585";
// import $531 from "./forms/fixes/form.531";
import $585 from "./forms/fixes/form.585";
import $531 from "./forms/fixes/form.531";

export default {
$A,
Expand Down Expand Up @@ -78,6 +78,6 @@ export default {
$518,
$376,

// $585,
// $531,
$585,
$531,
};
12 changes: 11 additions & 1 deletion tests/data/forms/fixes/form.e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,16 @@ const values = {
places: ["NY", "NJ"],
};

const hooks = {
places: {
onClear(fieldset) {
it('Fixes-E $(places).clear() should call onClear() hook on fieldset', () => {
expect(fieldset.values()).to.deep.equal([]);
})
}
}
}

class NewForm extends Form {
hooks() {
return {
Expand All @@ -39,6 +49,6 @@ class NewForm extends Form {
}
}

export default new NewForm({ fields, values, extra }, { options: {
export default new NewForm({ fields, values, extra, hooks }, { options: {
removeNullishValuesInArrays: true,
}, name: "Fixes-E" });
4 changes: 4 additions & 0 deletions tests/data/forms/fixes/form.t.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,8 @@ class NewForm extends Form {
expect(form.$("arrayChangeAdd").isDirty).to.be.true);
it(`"arrayChangeAdd" isPristine should be false`, () =>
expect(form.$("arrayChangeAdd").isPristine).to.be.false);
it(`"arrayChangeAdd" changed should not be 0`, () =>
expect(form.$("arrayChangeAdd").changed).not.to.be.equal(0));
});

describe("Check props state after change:", () => {
Expand All @@ -135,6 +137,8 @@ class NewForm extends Form {
expect(form.$("arrayChangeDel").isDirty).to.be.true);
it(`"arrayChangeDel" isPristine should be false`, () =>
expect(form.$("arrayChangeDel").isPristine).to.be.false);
it(`"arrayChangeDel" changed should should not be 0`, () =>
expect(form.$("arrayChangeDel").changed).not.to.be.equal(0));
});

describe("Check props state after change:", () => {
Expand Down
10 changes: 10 additions & 0 deletions tests/data/forms/nested/form.r.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,11 @@ const checkFieldset = (fieldset) =>
expect(fieldset).to.have.property("path")));

const submit = {
onSubmit(fieldset) {
it('$R.submit() should call onSubmit callback', () => {
expect(fieldset.submitted).to.equal(1);
})
},
onSuccess(fieldset) {
checkFieldset(fieldset);
},
Expand All @@ -81,5 +86,10 @@ export default new Form(
{
name: "Nested-R",
plugins,
hooks: {
onInit(form) {
form.$('club').submit();
}
}
}
);
Loading

0 comments on commit e59727c

Please sign in to comment.