Skip to content

Commit

Permalink
refactored to use ArrayFieldItem
Browse files Browse the repository at this point in the history
  • Loading branch information
joepuzzo committed May 4, 2020
1 parent 8876057 commit a8cdb21
Show file tree
Hide file tree
Showing 14 changed files with 607 additions and 234 deletions.
47 changes: 31 additions & 16 deletions src/Context.js
@@ -1,28 +1,43 @@
import React from 'react';

const FormRegisterContext = React.createContext({
register: () => { },
deregister: () => { },
setValue: () => { },
setTouched: () => { },
setError: () => { },
update: () => { },
getField: () => { },
expectRemoval: () => { },
register: () => {},
deregister: () => {},
setValue: () => {},
setTouched: () => {},
setError: () => {},
update: () => {},
getField: () => {},
expectRemoval: () => {}
});

const FormStateContext = React.createContext({});
const FormApiContext = React.createContext({
getFullField: () => { },
getValues: () => { },
getFullField: () => {},
getValues: () => {},
getOptions: () => ({}),
setInitialValue: () => { },
getInitialValue: () => { },
getDerrivedValue: () => { },
getSavedValue: () => { },
removeSavedState: () => { }
setInitialValue: () => {},
getInitialValue: () => {},
getDerrivedValue: () => {},
getSavedValue: () => {},
removeSavedState: () => {}
});
const GroupContext = React.createContext();
const SelectContext = React.createContext();

export { FormRegisterContext, FormStateContext, FormApiContext, GroupContext, SelectContext };
const ArrayFieldStateContext = React.createContext();
const ArrayFieldApiContext = React.createContext();
const ArrayFieldItemApiContext = React.createContext();
const ArrayFieldItemStateContext = React.createContext();

export {
FormRegisterContext,
FormStateContext,
FormApiContext,
GroupContext,
SelectContext,
ArrayFieldApiContext,
ArrayFieldStateContext,
ArrayFieldItemApiContext,
ArrayFieldItemStateContext
};
91 changes: 50 additions & 41 deletions src/FormController.js
Expand Up @@ -4,23 +4,21 @@ import Debug from './debug';
import { validateYupSchema } from './utils';
const debug = Debug('informed:Controller' + '\t');

const noop = () => { };
const noop = () => {};
class FormController extends EventEmitter {

constructor(options = {}) {

// Dont forget to call super! :)
super();

this.options = options;

// Map will store all fields by id
// Map will store all fields by id
// Key => uuid
// Val => fieldObj
// Why? so the form can control the fields!
this.fieldsById = new Map();

// Map will store all fields by name
// Map will store all fields by name
// Key => fieldName - example: "foo.bar[3].baz"
// Val => fieldObj
// Why? so the form can control the fields!
Expand All @@ -32,7 +30,7 @@ class FormController extends EventEmitter {
// Map to store fields being removed
this.expectedRemovals = {};

// Map of saved values
// Map of saved values
this.savedValues = {};

// Initialize the controller state
Expand Down Expand Up @@ -144,7 +142,7 @@ class FormController extends EventEmitter {
setCurrent: this.setCurrent
};

this.on('value', (field) => {
this.on('value', field => {
// The forms values have changed so we want to clear form level error
delete this.state.error;
this.notify(field);
Expand Down Expand Up @@ -196,7 +194,6 @@ class FormController extends EventEmitter {
}

next(nextComponent) {

// Validate the entire form
this.validate();

Expand All @@ -218,7 +215,7 @@ class FormController extends EventEmitter {

/**
* Generate the external form state that will be exposed to the users
*
*
* @returns Form State
*/
getFormState() {
Expand Down Expand Up @@ -267,14 +264,14 @@ class FormController extends EventEmitter {
// of their changes, we need to literally itterate through all registered fields
// and build the values object.
const values = {};
this.fieldsById.forEach((field) => {
this.fieldsById.forEach(field => {
if (!field.shadow) {
ObjectMap.set(values, field.field, field.fieldApi.getValue());
}
});

// Cleanup phase to get rid of irrelevant fields
this.fieldsById.forEach((field) => {
this.fieldsById.forEach(field => {
if (!field.fieldApi.relevant(values)) {
ObjectMap.delete(values, field.field);
}
Expand All @@ -290,14 +287,18 @@ class FormController extends EventEmitter {
// of their changes, we need to literally itterate through all registered fields
// and build the touched object.
const touched = {};
this.fieldsById.forEach((field) => {
this.fieldsById.forEach(field => {
if (!field.shadow && field.fieldApi.relevant(values)) {
ObjectMap.set(touched, field.field, field.fieldApi.getTouched());
}
});
// Shadow values override unless undefined
this.fieldsById.forEach((field) => {
if (field.shadow && field.fieldApi.getError() != undefined && field.fieldApi.relevant(values)) {
this.fieldsById.forEach(field => {
if (
field.shadow &&
field.fieldApi.getError() != undefined &&
field.fieldApi.relevant(values)
) {
ObjectMap.set(touched, field.field, field.fieldApi.getTouched());
}
});
Expand All @@ -312,16 +313,20 @@ class FormController extends EventEmitter {
// of their changes, we need to literally itterate through all registered fields
// and build the errors object.
const errors = {};
this.fieldsById.forEach((field) => {
this.fieldsById.forEach(field => {
// EXAMPLE special cases
// siblings && siblings[1] && favorite.color && favorite
if (!field.shadow && field.fieldApi.relevant(values)) {
ObjectMap.set(errors, field.field, field.fieldApi.getError());
}
});
// Shadow values override unless undefined
this.fieldsById.forEach((field) => {
if (field.shadow && field.fieldApi.getError() != undefined && field.fieldApi.relevant(values)) {
this.fieldsById.forEach(field => {
if (
field.shadow &&
field.fieldApi.getError() != undefined &&
field.fieldApi.relevant(values)
) {
ObjectMap.set(errors, field.field, field.fieldApi.getError());
}
});
Expand Down Expand Up @@ -363,7 +368,7 @@ class FormController extends EventEmitter {
return field;
}

// Notify other fields
// Notify other fields
notify(field) {
// Get the notifier
const notifier = this.getField(field);
Expand Down Expand Up @@ -399,7 +404,9 @@ class FormController extends EventEmitter {

screenValid() {
// Return false if any of the fields on the screen are invalid
const error = Object.entries(this.onScreen).some(([name, field]) => field.fieldApi.getError());
const error = Object.entries(this.onScreen).some(([name, field]) =>
field.fieldApi.getError()
);
return !error;
}

Expand All @@ -424,13 +431,8 @@ class FormController extends EventEmitter {
// of their changes, we need to literally itterate through all registered fields
// and reset them. Not a big deal but very important to remember that you cant simply
// reset this controllers state!
this.fieldsById.forEach((field) => {
this.fieldsById.forEach(field => {
field.fieldApi.reset();
// Initialize the values if it needs to be
const initialValue = ObjectMap.get(this.options.initialValues, field.field);
if (initialValue !== undefined) {
this.setValue(field.field, initialValue, { initial: true });
}
});

this.emit('change');
Expand All @@ -442,7 +444,7 @@ class FormController extends EventEmitter {
// of their changes, we need to literally itterate through all registered fields
// and set them. Not a big deal but very important to remember that you cant simply
// set this controllers state!
this.fieldsById.forEach((field) => {
this.fieldsById.forEach(field => {
// Initialize the values if it needs to be
const value = ObjectMap.get(values, field.field);
if (value !== undefined) {
Expand All @@ -465,8 +467,8 @@ class FormController extends EventEmitter {
// of their changes, we need to literally itterate through all registered fields
// and set them. Not a big deal but very important to remember that you cant simply
// set this controllers state!
this.fieldsById.forEach((field) => {
// Check to see if there is an error to set
this.fieldsById.forEach(field => {
// Check to see if there is an error to set
// Note: we use has becuause value may be there but undefined
if (ObjectMap.has(errors, field.field)) {
const error = ObjectMap.get(errors, field.field);
Expand All @@ -480,7 +482,7 @@ class FormController extends EventEmitter {
}

// Itterate through and call validate on every field
this.fieldsById.forEach((field) => {
this.fieldsById.forEach(field => {
field.fieldApi.validate(values);
field.fieldApi.setTouched(true);
});
Expand All @@ -498,16 +500,15 @@ class FormController extends EventEmitter {
// of their changes, we need to literally itterate through all registered fields
// and set them. Not a big deal but very important to remember that you cant simply
// set this controllers state!
this.fieldsById.forEach((field) => {
// Check to see if there is an error to set
this.fieldsById.forEach(field => {
// Check to see if there is an error to set
// Note: we use has becuause value may be there but undefined
if (ObjectMap.has(errors, field.field)) {
const error = ObjectMap.get(errors, field.field);
// If there is an error then set it
this.setError(field.field, error);
}
});

}
}

Expand All @@ -520,7 +521,6 @@ class FormController extends EventEmitter {
}

submitForm(e) {

// Incriment number of submit attempts
this.state.submits = this.state.submits + 1;

Expand All @@ -532,7 +532,7 @@ class FormController extends EventEmitter {
// Validate the form
this.validate();

// Emit a change
// Emit a change
this.emit('change');

// Check validity and perform submission if valid
Expand All @@ -558,10 +558,13 @@ class FormController extends EventEmitter {

// Always register the field
this.fieldsById.set(id, field);
this.fieldsByName.set(name, field );
this.fieldsByName.set(name, field);

// Example foo.bar.baz[3].baz >>>> foo.bar.baz[3]
const magicValue = name.slice(0, name.lastIndexOf('[') != -1 ? name.lastIndexOf(']') + 1 : name.length);
const magicValue = name.slice(
0,
name.lastIndexOf('[') != -1 ? name.lastIndexOf(']') + 1 : name.length
);

// Always clear out expected removals when a reregistering array field comes in
debug('clearing expected', magicValue);
Expand All @@ -587,16 +590,19 @@ class FormController extends EventEmitter {
delete this.onScreen[id];

// Example foo.bar.baz[3].baz >>>> foo.bar.baz[3]
const magicValue = name.slice(0, name.lastIndexOf('[') != -1 ? name.lastIndexOf(']') + 1 : name.length);
const magicValue = name.slice(
0,
name.lastIndexOf('[') != -1 ? name.lastIndexOf(']') + 1 : name.length
);

// If the fields state is to be kept then save the value
// Exception where its expected to be removed!
// Exception where its expected to be removed!
if (field.keepState && !this.expectedRemovals[magicValue]) {
debug(`Saving field ${name}'s value`, field.fieldApi.getFieldState());
ObjectMap.set(this.savedValues, name, field.fieldApi.getFieldState());
}

// Remove if its an expected removal OR we dont have keep state
// Remove if its an expected removal OR we dont have keep state
if (this.expectedRemovals[magicValue] || !field.keepState) {
// Remove the field completley
debug('Removing field', name);
Expand All @@ -615,11 +621,14 @@ class FormController extends EventEmitter {
update(id, field) {
const { field: name } = field;
debug('Update', id, name, field.fieldState.value);
const prevName = this.fieldsById.get(id).field;
this.fieldsById.set(id, field);
this.fieldsByName.set(name, field);
this.emit('change');
// Only emit change if field name changed
if(prevName !== name){
this.emit('change');
}
}

}

export default FormController;

0 comments on commit a8cdb21

Please sign in to comment.