Giter Club home page Giter Club logo

informed's People

Contributors

adrianocola avatar alakhote avatar alexandrtovmach avatar behzadmehrabi avatar eltociear avatar fescreen avatar githubanuja avatar glebtv avatar jacargentina avatar jaffparker avatar jakobo avatar jaredhan418 avatar jkaravakis avatar joepuzzo avatar kevinhewson avatar lalezaris avatar larrybotha avatar mattbasta avatar mfp22 avatar mikejestes avatar nggonzalez avatar olleolleolle avatar ollyhodgson avatar pangoraw avatar patrickfatrick avatar peternoordijk avatar pleeko avatar robin-ambachtsheer avatar tgaeta avatar vfonic avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

informed's Issues

[Typescript] declaration file missing

I don't find informed definition file in the provided package and @types/informed doesn't exist yet. Could you migrate @types/react-form with the changes ?

does setValues work with arrays?

I'm not seeing it working when doing something like

this.props.formApi.setValues({ someArray: [{ url: 'test'}]})

specifically the someArray element doesnt appear when I log this.props.formState.values, however the rest of the keys exist

Need export makePathArray in npm package

Hi,

You have many great functions and classes. Why is not published in npm package?

I would be happy:

  • makePathArray
  • ObjectMap
  • context's from informed/src/Context/index.js

Its will be very helpful to implement self custom fields.

Cannot read property 'config' of undefined without proper <Field />

First of all, I am glad that you took the time to rewrite the react-form!

I found react-form concepts really useful, but because it was too bloated I was about to write my own implementation which would look EXACTLY like informed. Credits for perfect timing! ๐Ÿ˜†

Anyway, back to issue. If the <Form /> looks like this:

<Form
  getApi={(formApi) => formApi.setValue('str', 'gni')}
>
  <Text field="str" />
  <button type="submit">Submit</button>
</Form>

Calling formApi.setValue(key, value) I get this error after witch the complete tree unmounts:

Cannot read property 'config' of undefined

Its pointing to this line:
https://github.com/joepuzzo/informed/blob/9a68ff6b45555566ec5f38592a64eef0080f5cff/src/Controller/FormController.js#L87

I figured that you cannot set a value when there is no initialized <Field /> representing that key.

Is this intentional? Maybe setting values should work even without the field component? What do you think?

Best,
Denis

Dynamically adjust validateOn* props for a field

My use case is that I would like to validate an input on change, but only once the user has entered data in the field and blurred it (dirty and touched). So the flow would be:

User enters page, input hasn't been touched
User clicks input, enters invalid text, error doesn't appear until user has blurred input
User clicks away, Input is now touched and error appears
User clicks on input again, modifies entered data and makes it valid. Input error state should be cleared the moment the entered data is valid (not on blur)

As far as I can tell, the only way to achieve this is to somehow adjust the value of validateOnChange from false (before the input is touched) to true (after the input has been touched)

I tried to accomplish this by creating a chain of HOCs that wrap the asField HOC to change the validateOnChange prop before its passed in, using the withFieldState HOC at a higher level to determine whether the field has been touched yet. However, this creates some crazy recursion loop and crashes the page so I don't think I should be doing that.

Let me know if I'm missing something obvious here..

Edit
I think I managed to get it working with the HOC chain using something like this:

const withValidation = (Component) => {
  // Validate on change after the field has been interacted with the first time
  return (props) => {
    const {touched, pristine, ...rest} = props
    return <Component {...rest} validateOnBlur validateOnChange={touched && !pristine}/>
  }
}

const withInputFieldState = (Component) => {
  return withFormState((props) => {
    const {formState, ...rest} = props
    const touched = formState.touched[props.field]
    const pristine = formState.pristine[props.field]
    return <Component {...rest} touched={touched} pristine={pristine}/>
  })
}

export default withInputFieldState(withValidation(asField(Text)))

It seems fairly ugly though

Value renderer/parser

I propose to implement customizable fieldState.value -> html's input value function and e.target.value -> fieldApi.setValue function. It can be done by:

const Text = ( { fieldApi, fieldState, valueParse, valueFormat, ...props  } ) => {
  const {
    value
  } = fieldState;
  const {
    setValue,
    setTouched
  } = fieldApi;
  const {
    onChange,
    onBlur,
    forwardedRef,
    ...rest
  } = props
  return (
      <input
          {...rest}
          ref={forwardedRef}
          value={valueFormat(!value && value !== 0 ? '' : value)}
          onChange={e => {
            setValue(valueParse(e.target.value))
            if (onChange) {
              onChange(e)
            }
          }}
          onBlur={e => {
            setTouched()
            if (onBlur) {
              onBlur(e)
            }
          }}
      />
    )
};

Text.defaultProps = {
  valueFormat: value => value,
  valueParse: value => value
}

It will be very helpful to implement i.e. number input with round to given max decimal places.

Force entire form to re-validate?

For example, I have a reset password page with:
New Password
Confirm Password

And the inputs sort of need to depend on each other, so that when I change one the other one becomes invalid if they no longer match.

What I'm thinking is adding a "validate" function to the form API that will just force validation check to occur on all inputs in the form.

Access formApi from asField() ?

Building completely custom inputs with asField, is it possible to access the formApi? I would like to only check submitted (in addition to touched) before applying the error state.

[Missing dependency] @babel/runtime

I use informed in a typescript project and the compilation failed without adding @babel/runtime manually. Can you add it to the package.json ?

Not seeing initialValue applied until after interacting with the input

I'm using the Text input with an initialValue, however, the input initializes with no value (empty) and then once I interact with it (click on then, click off) the initial value is applied. Do I need to define this initialValue somewhere else than just on the element.

<Text field="address" id="form-address" initialValue="John" />

Add .prettierrc for consistent JS formatting

PrettierJs is awesome. My default Prettier config is different from your JS style.

Keen on me adding a .prettierrc that conforms to your style so that all contributors using Prettier will contribute consistent to how you format JS?

Setting values on the form later?

In the react-form I had a component like this which essentially loads data asynchronously while the form is being already displayed empty giving better UX. Would it be too problematic to have values prop in Informed as well which would override the state completely on every update?

export class FormLoader extends React.Component {
  state = {}
  async componentDidMount() {
    if (!this.props.shouldLoad) {
      return
    }
    const values = await this.props.loader()
    this.setState({ values })
  }
  render() {
    const { shouldLoad, loader, ...props } = this.props
    return <Form values={this.state.values} {...props} />
  }
}

'window is not defined' using SSR

I'm trying to migrate from react-form to informed using next.js, but it throws the following error:

window is not defined

ReferenceError: window is not defined
    at Object.<anonymous> (/myproject/node_modules/informed/dist/index.js:1:271)

The webpack config probably adds the window reference. Any way to change it to make it would work with server side rendering? Or maybe I'm missing something?

TypeError: Cannot read property 'replace' of undefined using SSR

Running version 1.3.8

Getting this error when I try to render the simple example from the README via SSR .
@jangerhofer have you may encountered something like this when testing after #13 ?

{ TypeError: Cannot read property 'replace' of undefined
    at makePathArray (node_modules/informed/dist/index.js:1:23304)
    at ObjectMap.get (node_modules/informed/dist/index.js:1:25157)
    at Object.FormController.r.getValue (node_modules/informed/dist/index.js:1:28107)
    at buildFieldState (node_modules/informed/dist/index.js:1:4729)
    at new _class (node_modules/informed/dist/index.js:1:5858)
    at processChild (node_modules/react-dom/cjs/react-dom-server.node.development.js:2095:14)
    at resolve (node_modules/react-dom/cjs/react-dom-server.node.development.js:2061:5)
    at ReactDOMServerRenderer.render (node_modules/react-dom/cjs/react-dom-server.node.development.js:2380:22)

Thanks for the great work

Nested Scopes are not handled properly

When Scopes are nested, the fields inner scope's fields become unusable and throw a console error: Cannot read property config of undefined whenever you interact with them.

Async validation

Hi Joe, do you have any intentions of adding async validation into informed?

How do you make custom inputs?

I'm using the Material UI library and want to make inputs that work with this library that look like the material ones using their components. I don't understand how to make custom inputs even after diving into the code to figure out how the ones that are shipped with it are created.

I keep getting an undefined error at replace and I don't know why

I've tried setting up my input super basic to try to figure out where this error is coming from and I can't.

import React, { Component } from 'react'
import { asField } from 'informed';

class CustomInput extends Component {
  render() {
    console.log(this.props)
    const { fieldApi, fieldState, ...props } = this.props;
    const {
      value
    } = fieldState;
    const {
      setValue,
      setTouched
    } = fieldApi;
    const {
      onChange,
      onBlur,
      forwardedRef,
      ...rest
    } = props
    return (
      <input
          {...rest}
          ref={forwardedRef}
          value={!value && value !== 0 ? '' : value}
          onChange={e => {
            setValue(e.target.value)
            if (onChange) {
              onChange(e)
            }
          }}
          onBlur={e => {
            setTouched()
            if (onBlur) {
              onBlur(e)
            }
          }}
      />
    );
  }
}

export default asField(CustomInput);

This is basically just the same component as the Text component but I'm getting a Cannot read property 'replace' of undefined error

Could use some help with this and also maybe an update to the docs on how one might actually build their own custom informed inputs

Props changed don't affect fieldState

The main purpose of a React component is that "when its internal and external state (props) change, its content must change (or not)".
But that's not the case for the inputs. For example, I use a Text component and validate if it's empty, but I don't want it to until I submit, okay so far, but I want that after submitting the form, if there was an error, the component should validate on Change, so I can do a proper validation, so I tag it with validateOnChange, but when it changes, nothing happens, just when I submit again the form, it revalidates.

I guess a simple check on componentDidUpdate reflecting changes to the fieldState would solve the issue.

As a workaround, the following code achieves (closely) the desired behavior.

<Text
    field={"name"}
    validate={validation}
    placeholder={"Name"}
    initialValue={formState.values['name']}
    validateOnMount={this.state.formSent}
    validateOnChange={this.state.formSent}
    key={`name-${this.state.formSent|0}`}
/>

Assuming you have a state flag formSent changing with the Form prop onSubmitFailure. The key prop is used to force the component to remount, which is not the perfect behavior, but solves the problem, but as it's a brand new component you should set validateOnMount so it validates right after the first submit.

EDIT: Another downside about this, is that you have to set the initialValue of the field according to the formState, so it doesn't empty whenever it remounts, and it would affect other behaviors

As of now, I'm a bit busy so I couldn't make a PR nor avaliate a change in the source code. If you could please take a look, I don't think it would be something too expensive.

Reusable of existing code: lodash/defaultTo

Hi. I know, you hate me ;)

You are use !value && value !== 0 ? '' : value expression to avoid of undefined or null test in input. But, the defaultTo(value, '') from lodash return the same result but it looks clearer.

Lodash is the big library but you can import only import defaultTo from "lodash/defaultTo";. Then JS code after minification will be contain only code from lodash/defaultTo.js, not whole lodash.

You thought about it?

Form component onChange function is triggered twice

The function is being fired twice, once with the form state, as i would expect, and a second time with the event from the input's onChange function.
image

Here is a minimal version of my form
image

Is this intended behavior? I would be unintuitive to have to try to filter out the second function call.

Why HOC only?

Hey, I must say I am bit confounded right now. I am using the react-form in a middle-sized project and it works quite well for its needs. Finding out today that it has been deprecated was surprising at least and I was at least hoping that Informed would be very similar. It's kinda sad realization it's mainly HOC based. This was the main reason why I haven't picked other solutions.

What is the logic behind such a decision? I mean you have the render prop approach for the Form state, but everything else must go through HOC. Why? Internally it's so much easier to have to render prop component being exposed as HOC. It's rather cumbersome in an opposite way.

It's not like HOC are bad, I am not saying that but why issuing such a constraint when it's easy not to? Please reconsider this decision. I am not sure what other solutions are out there, but I don't want to end up writing my own form library because this ends up another HOC only solution.

PS: I do like that you got rid of Redux, it was wildly unnecessary and harder to debug. Although there is still redux in keywords in package.json :)

The values passed to onSubmit aren't immutable

Reproduction steps:

  1. View: https://codesandbox.io/s/53y9kroy8n
  2. Enter data into the text area
  3. Click 'Submit', the submitted value should be populated
  4. Click 'Toggle visibility' twice.

Expected result:
I would expect the object this.value to not change, since only the internal state of the form that has changed, and onSubmit has only been run once.

Observed result:
this.value is cleared.

It looks like the value passed to the onSubmit prop of the form isn't a clone of the values, it's the actual values themselves. When the values are cleared internally, they have also been cleared anywhere they are used directly.

A workaround is to deep-clone the values passed to the onSubmit prop, but I would expect informed to handle that for me.

How to incorporate input type "hidden"?

In my application I user <Scope> to group multiple fields. These fields belong to a dataset that also as an ID. I need to pass this ID along with the scoped group. But unfortunately fields with type="hidden" are not parsed by informed.

Can you please explain how to handle this?

setValue(null) removes the value from the state

Hey,

when using setValue from the <Field /> perspective and setting it to null, the next state is without that value instead of having it as null.

const initialState = {
  some: 'value',
};

Then: <Field field="some" />.setValue(null), we get the following state:

const nextState = {};

In my use case null and undefined is VERY different, so I'd need the value to have the null set...

React optimization: why you use lambda expressions?

When you have:

const Text = ( { fieldApi, fieldState, ...props  } ) => {
  const {
    value
  } = fieldState;
  const {
    setValue,
    setTouched
  } = fieldApi;
  const {
    onChange,
    onBlur,
    forwardedRef,
    ...rest
  } = props
  return (
      <input
          {...rest}
          ref={forwardedRef}
          value={!value && value !== 0 ? '' : value}
          onChange={e => {
            setValue(e.target.value)
            if (onChange) {
              onChange(e)
            }
          }}
          onBlur={e => {
            setTouched()
            if (onBlur) {
              onBlur(e)
            }
          }}
      />
    )
};

export default asField(Text);

The onChange and onBlur props will be recreated always when render() will be executed. So the children component will be unnecessary rerendered. See: https://reactjs.org/docs/optimizing-performance.html#examples

I propose to refactor to:

export class BasicText extends React.PureComponent {
  onChange = (e) => {
    const {fieldApi, onChange} = this.props;
    const {setValue} = fieldApi;

    setValue(e.target.value);
    if (onChange) {
      onChange(e.target.value, e);
    }
  };

  onBlur = (e) => {
    const {fieldApi, onBlur} = this.props;
    const {setTouched} = fieldApi;

    setTouched();
    if (onBlur) {
      onBlur(e);
    }
  };

  render() {
    const {fieldApi, fieldState, onChange, onBlur, ...rest} = this.props;
    const {value} = fieldState;

    return (
      <Input
        {...rest}
        value={value}
        onChange={this.onChange}
        onBlur={this.onBlur}
      />
    );
  }
};

export const Text = asField(BasicText); // BasicText - exported too, unwrapped Text component from #25 :)

HOC for reusable codes from Text, TextArea etc.

You have:

const Text = ( { fieldApi, fieldState, ...props  } ) => {
  const {
    value
  } = fieldState;
  const {
    setValue,
    setTouched
  } = fieldApi;
  const {
    onChange,
    onBlur,
    forwardedRef,
    ...rest
  } = props
  return (
      <input
          {...rest}
          ref={forwardedRef}
          value={!value && value !== 0 ? '' : value}
          onChange={e => {
            setValue(e.target.value)
            if (onChange) {
              onChange(e)
            }
          }}
          onBlur={e => {
            setTouched()
            if (onBlur) {
              onBlur(e)
            }
          }}
      />
    )
};

export default asField(Text);

But when you refactor it to kind of:

export const asText = (Input) => (
  ( { fieldApi, fieldState, ...props  } ) => {
    const {
      value
    } = fieldState;
    const {
      setValue,
      setTouched
    } = fieldApi;
    const {
      onChange,
      onBlur,
      forwardedRef,
      ...rest
    } = props
    return (
      <Input
        {...rest}
        ref={forwardedRef}
        value={value}
        onChange={e => {
          setValue(e)
          if (onChange) {
            onChange(e)
          }
        }}
        onBlur={e => {
          setTouched()
          if (onBlur) {
            onBlur(e)
          }
        }}
      />
    )
  }
);

const DefaultText = ({value, onChange, onBlur, ...rest}) => (
  <input
    {...rest}
    value={!value && value !== 0 ? '' : value}
    onChange={e => {
      onChange(e.target.value)
    }}
    onBlur={e => {
      onBlur(e)
    }}
  />
);

export default asField(asText(DefaultText));

It will be easy to implement custom input wrapped by asField and with reuse of code. It is supplement to #25. In #25 we can implement custom input with reuse of code in layer above the binding of form state. In this issue we can implement custom input in layer below the binding of form state.

So we get these layers:

  1. asField layer.
  2. #25 custom input layer.
  3. fieldApi/fieldState binding layer.
  4. This issue custom input layer.
  5. Raw HTML element layer.

Need feature: the "changed" state

Currently you have value and touched. value is the current value (typed or achieved from initialValues). touched is set to true if input was focused and lost focus. I want to mark inputs who are changed value according to initial value. Typical application: edit form.

In react-form I can implement it in my custom input because I can achieve access to defaultValues via:

contextTypes = {
  formProps: PropTypes.object
}

And this.context.defaultValues['my_field_name'] have my initial value. So in my custom input I can compare value from defaultValues with value from field state and mark my custom input as changed.

In informed the initialValues is stored in controller but controller is used in bindToField and no propagated to Field. Moreover nor withController and not FormContext is not exported. So I have not access to controller from my custom field.

Can you implement access to initialValue from formApi/fieldApi or formState/fieldState or better: implement changed state next to touched state?

Allow stopping propagation of form submit event

Hello. Thanks for the nice library. I would like to mention a problem I'm having and two possible solutions I've come up with. I can implement either and submit a pull request if either seem acceptable. I have a form containing a modal which itself contains a form. Nested forms are disallowed by HTML so I'm using a React portal to render the modal outside the form even though it is semantically a child of the parent form. This works fine. Unfortunately, submitting the child form also submits the parent form because the submit event bubbles upwards in React and I don't have control over this.

One way to solve this is to pass the event as a second argument to the Form onSubmit callback prop. This would allow the user to call event.stopPropagation() manually.

Another way to solve this is to use the same pattern as the Form dontPreventDefault prop. A new boolean prop called something like stopPropagation that, when set to true, would automatically call event.stopPropagation() just before or after the event.preventDefault() call.

Do either of these sound reasonable?

Why no wrapped Text, TextArea, Checkbox, Radio, RadioGroup and Select is not exported?

I want to implement custom input. It may be very helpful if the no wrapped version of Text and etc. will be exported. Then I will be able to reuse no wrapped Text and implement custom input as kind of:

const CustomInput = ({ fieldApi, fieldState, customProp, ...props  }) => {
  return (
    <div className={fieldState.error ? 'error' : ''}>
    {/*...*/}
      <NoWrappedText {...props} fieldApi={fieldState} fieldState={fieldState} />
    {/*...*/}
    </div>
  )
}

export default asField(CustomInput);

Updating formState.touched on radio button

Awesome library that you've written. Just ran into a small bug.

When I'm keyboarding through the form and tab to a radio group, if I hit the space bar to select the current highlighted radio button, the formState.touched is not updated. If I use an arrow key to highlight a different radio button instead, then the touched object is correctly updated. I did notice however, that the radio group's validation is correctly ran and the formState.values object is correctly updated.

The reason I need the touched object is because I'm currently using the formState.touched to determine if the user has at least tried each form element before I enable the submit button.

Thanks for your time.

FormAPI.setValue for nested value

I am trying to set a value under a <Scope scope="parent">...</Scope>. The form values look like so:

const values = {
  parent: {
    child: 'SET THIS VALUE',
  },
};

But neither formApi.setValue('parent.child', 'NEXT VALUE') nor formApi.setValue(['parent', 'child'], 'NEXT VALUE') work.

Do I really have to use formApi.setValues and merge?

Dynamic fields - bug when field props changed

During writing example for #49 I saw bug when field is not recreating but only field name is changed.

Simple example: https://dzwiedziu-nkg.github.io/test-react-informed/
Source: https://github.com/dzwiedziu-nkg/test-react-informed

Bug: field store old value when field props changed.

It happen when we use conditional renders i.e. my example. React use Virtual DOM. Before unomunt and mount new component's instances try to update only props. React create new instance or discard component only when there's no other way.

Error When Using Array for formApi.getValue()

In the previous library, I could retrieve a nested value by passing the path array to the formApi.getValue() method. However, when using it via informed, I'm getting the following error:

Uncaught TypeError: e.replace is not a function

While debugging I noticed that when you build the array from the string equivalent, prop.nested, there is no check to see if the array is already there: https://github.com/joepuzzo/informed/blob/master/src/ObjectMap/index.js#L74

It might be useful to see if this is already present to allow variously data formats for lookup.

How to integrate with a redux app

Use case

  • Having an react redux app, with some object, holding a string property path
  • Having a form for editing that objects path property
  • Having a widget for navigating folders on disk, and dispatch a redux action to select a given path.

Is that possible with the current version?

validateOnMount?

Is there a way to have the form run validation on mount? Previously could set a validateOnMount property of the form to true.

React error upon using <Select> component

Uncaught Error: Element ref was specified as a string (select) but no owner was set. This could happen for one of the following reasons:

Element ref was specified as a string (select) but no owner was set. This could happen for one of the following reasons:
1. You may be adding a ref to a functional component
2. You may be adding a ref to a component that was not created inside a component's render method
3. You have multiple copies of React loaded
See https://fb.me/react-refs-must-have-owner for more information.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.