Skip to content

Commit

Permalink
feat: introduced fallbackValue and retrieveNullifiedEmptyStrings
Browse files Browse the repository at this point in the history
…form options

Introduced `fallbackValue` and `retrieveNullifiedEmptyStrings` form options.
  • Loading branch information
foxhound87 committed Apr 11, 2023
1 parent 170a2d8 commit a5d151b
Show file tree
Hide file tree
Showing 11 changed files with 142 additions and 37 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
# 6.1.0 (master)
- Introduced `fallbackValue` and `retrieveNullifiedEmptyStrings` form options.
- Fix: `get()` strict mode improved
- Fix: default type file value;

# 6.0.0 (master)
- stable typescript release

Expand Down
17 changes: 11 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
![GitHub release (latest by date)](https://img.shields.io/github/v/release/foxhound87/mobx-react-form)
![npm bundle size](https://img.shields.io/bundlephobia/min/mobx-react-form)
[![Codecov Coverage](https://img.shields.io/codecov/c/github/foxhound87/mobx-react-form/master.svg)](https://codecov.io/gh/foxhound87/mobx-react-form)
[![npm](https://img.shields.io/npm/v/mobx-react-form.svg)]()
[![node](https://img.shields.io/node/v/mobx-react-form.svg)]()
[![GitHub license](https://img.shields.io/github/license/foxhound87/mobx-react-form.svg)]()
![GitHub closed pull requests](https://img.shields.io/github/issues-pr-closed/foxhound87/mobx-react-form)
Expand Down Expand Up @@ -49,8 +48,6 @@ npm install --save mobx-react-form

#### Choose and Setup a Validation Plugin

> See [Validation Plugins](https://foxhound87.github.io/mobx-react-form/docs/validation/plugins.html) for more info on supported packages.
Below we are creating a `plugins` object using the `validatorjs` package to enable `DVR` functionalities (Declarative Validation Rules).

```javascript
Expand All @@ -62,6 +59,8 @@ const plugins = {
};
```

> See [Validation Plugins](https://foxhound87.github.io/mobx-react-form/docs/validation/plugins.html) for more info on supported packages.
#### Define the Form Fields

Define the `fields` as a collection with a `rules` property for the validation.
Expand All @@ -77,15 +76,17 @@ const fields = [{
label: 'Password',
placeholder: 'Insert Password',
rules: 'required|string|between:5,25',
type: 'password',
}, {
name: 'passwordConfirm',
label: 'Password Confirmation',
placeholder: 'Confirm Password',
rules: 'required|string|same:password',
type: 'password',
}];
```

> You can also define `fields` as an `object`.
> See all available [Field Props](https://foxhound87.github.io/mobx-react-form/docs/api-reference/fields-properties.html) on the docs.
#### Define the Validation Hooks

Expand All @@ -104,6 +105,8 @@ const hooks = {
}
```

> See more on the docs about the [Validation Hooks](https://foxhound87.github.io/mobx-react-form/docs/events/validation-hooks.html)
#### Initialize the Form

Simply pass the `fields`, `plugins` and `hooks` objects to the constructor
Expand All @@ -114,6 +117,8 @@ import MobxReactForm from 'mobx-react-form';
const myForm = new MobxReactForm({ fields }, { plugins, hooks });
```

> Learn about more on the docs about the [Form Instance](https://foxhound87.github.io/mobx-react-form/docs/form/)
#### Pass the myForm to a react component

The package provide some built-in and ready to use Event Handlers:
Expand All @@ -126,7 +131,7 @@ import { observer } from 'mobx-react';

export default observer(({ myForm }) => (
<form onSubmit={myForm.onSubmit}>
<label htmlFor={myForm.$('email').id}>
<label for={myForm.$('email').id}>
{myForm.$('email').label}
</label>
<input {...myForm.$('email').bind()} />
Expand All @@ -143,7 +148,7 @@ export default observer(({ myForm }) => (
));
```

> Other Field Props are available. See the [docs](https://foxhound87.github.io/mobx-react-form/docs/api-reference/fields-properties.html) for more details.
> See more on the docs about the [Event Handlers](https://foxhound87.github.io/mobx-react-form/docs/events/event-handlers.html) and the [Event Hooks](https://foxhound87.github.io/mobx-react-form/docs/events/event-hooks.html).
###### Extending the Form class

Expand Down
36 changes: 22 additions & 14 deletions src/Base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -441,13 +441,15 @@ export default class Base implements BaseInterface {
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, {
fallbackValueOption: this.state.options.get(OptionsEnum.fallbackValue, this),
separated: _.get(raw, $path),
});
return;
}
}
if (_.isNull(field) || _.isNil(field.fields)) {
$field.value = parseInput($field.$input, {
fallbackValueOption: this.state.options.get(OptionsEnum.fallbackValue, this),
separated: field,
});
return;
Expand Down Expand Up @@ -481,35 +483,37 @@ export default class Base implements BaseInterface {
if (_.isNil(prop)) {
return this.deepGet(
[...props.computed, ...props.editable, ...props.validation],
this.fields
this.fields,
strict,
);
}

allowedProps(AllowedFieldPropsTypes.all, _.isArray(prop) ? prop : [prop]);

if (_.isString(prop)) {
if (strict && this.fields.size === 0) {
return parseCheckOutput(this, prop);
if (this.fields.size === 0) {
const retrieveNullifiedEmptyStrings = this.state.options.get(OptionsEnum.retrieveNullifiedEmptyStrings, this);
return parseCheckOutput(this, prop, strict ? retrieveNullifiedEmptyStrings : false);
}

const value = this.deepGet(prop, this.fields);
const value = this.deepGet(prop, this.fields, strict);
const removeNullishValuesInArrays = this.state.options.get(OptionsEnum.removeNullishValuesInArrays, this);

return parseCheckArray(this, value, prop, removeNullishValuesInArrays);
return parseCheckArray(this, value, prop, strict ? removeNullishValuesInArrays : false);
}

return this.deepGet(prop, this.fields);
return this.deepGet(prop, this.fields, strict);
}

/**
Get Fields Props Recursively
*/
deepGet(prop: any, fields: any): any {
deepGet(prop: any, fields: any, strict = true): any {
return _.transform(
getObservableMapValues(fields),
(obj: any, field: any) => {
const $nested = ($fields: any) => $fields.size !== 0
? this.deepGet(prop, $fields)
? this.deepGet(prop, $fields, strict)
: undefined;

Object.assign(obj, {
Expand All @@ -525,22 +529,23 @@ export default class Base implements BaseInterface {
(opt.get(OptionsEnum.softDelete, this) && prop === FieldPropsEnum.value && field.deleted));

if (field.fields.size === 0) {
delete obj[field.key]; // eslint-disable-line
delete obj[field.key];
if (removeProp) return obj;
const retrieveNullifiedEmptyStrings = this.state.options.get(OptionsEnum.retrieveNullifiedEmptyStrings, this);
return Object.assign(obj, {
[field.key]: parseCheckOutput(field, prop),
[field.key]: parseCheckOutput(field, prop, strict ? retrieveNullifiedEmptyStrings : false),
});
}

let value = this.deepGet(prop, field.fields);
let value = this.deepGet(prop, field.fields, strict);
if (prop === FieldPropsEnum.value) value = field.$output(value);

delete obj[field.key];
if (removeProp) return obj;
const removeNullishValuesInArrays = this.state.options.get(OptionsEnum.removeNullishValuesInArrays, this);

return Object.assign(obj, {
[field.key]: parseCheckArray(field, value, prop, removeNullishValuesInArrays),
[field.key]: parseCheckArray(field, value, prop, strict ? removeNullishValuesInArrays : false),
});
}

Expand Down Expand Up @@ -572,6 +577,7 @@ export default class Base implements BaseInterface {
FieldPropsEnum.default,
] as string[]).includes(prop)) {
(this as any)[prop] = parseInput((this as any).$input, {
fallbackValueOption: this.state.options.get(OptionsEnum.fallbackValue, this),
separated: data,
});
} else {
Expand Down Expand Up @@ -602,6 +608,7 @@ export default class Base implements BaseInterface {

if (_.isNil(data)) {
this.each((field: any) => field.$value = defaultValue({
fallbackValueOption: this.state.options.get(OptionsEnum.fallbackValue, this),
value: field.$value,
type: field.type,
}));
Expand Down Expand Up @@ -655,8 +662,9 @@ export default class Base implements BaseInterface {
const field = this.initField(key, $path(key), _.merge(tree[0], obj));

if(!_.has(obj, FieldPropsEnum.value) && !this.state.options.get(OptionsEnum.preserveDeletedFieldsValues, this)) {
field.$value = defaultValue({ value: field.$value, type: field.type });
field.each((field: any) => field.$value = defaultValue({ value: field.$value, type: field.type }));
const fallbackValueOption = this.state.options.get(OptionsEnum.fallbackValue, this);
field.$value = defaultValue({ fallbackValueOption, value: field.$value, type: field.type });
field.each((field: any) => field.$value = defaultValue({ fallbackValueOption, value: field.$value, type: field.type }));
}

this.$changed ++;
Expand Down
37 changes: 25 additions & 12 deletions src/Field.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,14 +51,17 @@ const setupDefaultProp = (
data: any,
props: any,
update: boolean,
{ isEmptyArray }: { isEmptyArray: boolean }
{ isEmptyArray, fallbackValueOption }:
{ isEmptyArray: boolean, fallbackValueOption: any }
) =>
parseInput(instance.$input, {
defaultValue,
nullable: true,
isEmptyArray,
type: instance.type,
unified: update
? defaultValue({
fallbackValueOption,
type: instance.type,
value: instance.value
})
Expand Down Expand Up @@ -270,7 +273,7 @@ export default class Field extends Base implements FieldInterface {
}
this.$value = newVal;
this.$changed ++;
if (!this.resetting && !this.clearing) {
if (!this.actionRunning) {
this.state.form.$changed ++;
};
}
Expand All @@ -288,11 +291,17 @@ export default class Field extends Base implements FieldInterface {
}

set initial(val) {
this.$initial = parseInput(this.$input, { separated: val });
this.$initial = parseInput(this.$input, {
fallbackValueOption: this.state.options.get(OptionsEnum.fallbackValue, this),
separated: val,
});
}

set default(val) {
this.$default = parseInput(this.$input, { separated: val });
this.$default = parseInput(this.$input, {
fallbackValueOption: this.state.options.get(OptionsEnum.fallbackValue, this),
separated: val,
});
}

get actionRunning() {
Expand Down Expand Up @@ -499,6 +508,7 @@ export default class Field extends Base implements FieldInterface {
this.key = $key;
this.path = $path;
this.id = this.state.options.get(OptionsEnum.uniqueId)?.apply(this, [this]);
const fallbackValueOption = this.state.options.get(OptionsEnum.fallbackValue, this);
const struct = this.state.struct();
const structPath = pathToStruct(this.path);
const isEmptyArray: boolean = Array.isArray(struct)
Expand All @@ -509,9 +519,6 @@ export default class Field extends Base implements FieldInterface {

const { $type, $input, $output } = $props;

// eslint-disable-next-line
// if (_.isNil($data)) $data = '';

if (_.isPlainObject($data)) {
const { type, input, output } = $data;

Expand All @@ -521,6 +528,7 @@ export default class Field extends Base implements FieldInterface {
this.$output = $try($output, output, this.$output);

this.$value = parseInput(this.$input, {
fallbackValueOption,
isEmptyArray,
type: this.type,
unified: $data.value,
Expand All @@ -529,6 +537,7 @@ export default class Field extends Base implements FieldInterface {
});

this.$initial = parseInput(this.$input, {
fallbackValueOption,
nullable: true,
isEmptyArray,
type: this.type,
Expand All @@ -538,6 +547,7 @@ export default class Field extends Base implements FieldInterface {
});

this.$default = setupDefaultProp(this, $data, $props, update, {
fallbackValueOption,
isEmptyArray,
});

Expand All @@ -552,13 +562,15 @@ export default class Field extends Base implements FieldInterface {
this.$output = $try($output, this.$output);

this.$value = parseInput(this.$input, {
fallbackValueOption,
isEmptyArray,
type: this.type,
unified: $data,
separated: $props.$value,
});

this.$initial = parseInput(this.$input, {
fallbackValueOption,
nullable: true,
isEmptyArray,
type: this.type,
Expand All @@ -568,6 +580,7 @@ export default class Field extends Base implements FieldInterface {
});

this.$default = setupDefaultProp(this, $data, $props, update, {
fallbackValueOption,
isEmptyArray,
});

Expand Down Expand Up @@ -660,7 +673,7 @@ export default class Field extends Base implements FieldInterface {
this.$resetting = false;
this.$clearing = false;
}))
if (deep) this.each((field: any) => field.resetValidation());
if (deep) this.each((field: any) => field.resetValidation(deep));
}

clear(deep: boolean = true, execHook: boolean = true): void {
Expand All @@ -671,11 +684,12 @@ export default class Field extends Base implements FieldInterface {
this.$changed = 0;
this.files = undefined;
this.$value = defaultValue({
fallbackValueOption: this.state.options.get(OptionsEnum.fallbackValue),
value: this.$value,
type: this.type,
});

if (deep) this.each((field: FieldInterface) => field.clear(true));
if (deep) this.each((field: FieldInterface) => field.clear(deep));

this.state.options.get(OptionsEnum.validateOnClear, this)
? this.validate({
Expand All @@ -695,7 +709,7 @@ export default class Field extends Base implements FieldInterface {
if (useDefaultValue) this.value = this.$default;
if (!useDefaultValue) this.value = this.$initial;

if (deep) this.each((field: FieldInterface) => field.reset(true));
if (deep) this.each((field: FieldInterface) => field.reset(deep));

this.state.options.get(OptionsEnum.validateOnReset, this)
? this.validate({
Expand Down Expand Up @@ -782,13 +796,11 @@ export default class Field extends Base implements FieldInterface {
}

initMOBXEvent(type: string): void {
// @ts-ignore
if (!_.isArray(this[`$${type}`])) return;

let fn: any;
if (type === FieldPropsEnum.observers) fn = this.observe;
if (type === FieldPropsEnum.interceptors) fn = this.intercept;
// @ts-ignore
this[`$${type}`].map((obj: any) => fn(_.omit(obj, FieldPropsEnum.path)));
}

Expand All @@ -807,6 +819,7 @@ export default class Field extends Base implements FieldInterface {
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, {
fallbackValueOption: this.state.options.get(OptionsEnum.fallbackValue, this),
separated: fields,
});
return;
Expand Down
2 changes: 2 additions & 0 deletions src/Options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export default class Options implements OptionsInterface {
options: OptionsModel = {
uniqueId,
fallback: true,
fallbackValue: "",
defaultGenericError: null,
submitThrowsError: true,
showErrorsOnInit: false,
Expand All @@ -43,6 +44,7 @@ export default class Options implements OptionsInterface {
retrieveOnlyDirtyFieldsValues: false,
retrieveOnlyEnabledFieldsValues: false,
retrieveOnlyEnabledFieldsErrors: false,
retrieveNullifiedEmptyStrings: false,
autoTrimValue: false,
autoParseNumbers: false,
removeNullishValuesInArrays: false,
Expand Down
Loading

0 comments on commit a5d151b

Please sign in to comment.