jurassix / react-validation-mixin

Simple validation mixin (HoC) for React.
MIT License
282 stars 38 forks source link

Implement "getValidatorData" to return data #75

Closed Rich-amoebaWare closed 8 years ago

Rich-amoebaWare commented 8 years ago

I just dl'd, installed, and tried to make a quick test...but I get:

Implement ("getValidatorData" to return data) message in the console.

Not sure why it can't find my getValidatorData() function, but it is not called when I call onSubmit() even though it is implemented and responds/returns data when I call it from within component.

import React, {Component, PropTypes} from 'react';
import ui from 'redux-ui';
import Dialog from 'material-ui/Dialog';
import FlatButton from 'material-ui/FlatButton';
import TextField from 'material-ui/TextField';
import {formatClientToolbarTitle} from '../utils';
import Joi from 'joi-browser';
import validation from 'react-validation-mixin';
/** The joi-validation-strategy requires joi. There is a joi
    * and a joi-browser build. The joi package requires net and dns
    * modules which are not available in browser resulting in error when
    * packing module with webpack. But the joi-validation-strategy
    * includes joi.
    *   We had to modify the node-modules/joi-validation-strategy.js
    * and change
    * var _joi = require('joi');
    * to
    *   var _joi = require('joi-browser');
    * see: https://github.com/jeffbski/joi-browser
    */
import strategy from 'joi-validation-strategy';

@ui({
        state: {
            buttons: {
                cancel: {id: "cancel", label: "Cancel"},
                submit: {id: "submit", label: "Submit"},
            }
        }
})
class ClientDialog extends Component {
    static propTypes = {
        ui: PropTypes.object.isRequired,
        updateUI: PropTypes.func.isRequired,
        validate: PropTypes.func.isRequired
    }

    constructor(props) {
        super(props);
        this.validatorTypes = {
            fname: Joi.string().alphanum().min(1).max(50).required().label('First Name'),
            lname: Joi.string().alphanum().min(1).max(50).required().label('Last Name'),
        };
        this.getValidatorData = this.getValidatorData.bind(this);
    }

    getValidatorData = () => {
        console.log("getData: ", this.props.ui.dialog.values);
        //return Object.assign({},this.props.ui.dialog.values);
    }

    onButtonClick = (button) => {
        const {ui, updateUI} = this.props;
        switch (button) {
            case ui.buttons.cancel.id:
                updateUI({dialog: {...ui.dialog, open: false}});
                break;
            case ui.buttons.submit.id:
                this.onSubmit();
        }
    }

    updateValue = (field, val) => {
        const {ui, updateUI} = this.props;
        updateUI({dialog: {...ui.dialog, values: {...ui.dialog.values, [field]: val }}});
    }

    onSubmit = () => {
        //event.preventDefault();
        const onValidate = (error) => {
            if (error) {
                console.log("error: ", error);
            }
            else {
                console.log("no errors");
            }
        };
        this.props.validate(onValidate);
    }

    render() {
        console.log(this.props);
        const {ui} = this.props;
        const{buttons} = ui;
        const {values} = ui.dialog;

        const title = values ? formatClientToolbarTitle(values) : "Client";
        const actions = [
            <FlatButton
                label={buttons.cancel.label}
                primary={false}
                onTouchTap={() => ::this.onButtonClick(buttons.cancel.id)}
            />,
            <FlatButton
                label={buttons.submit.label}
                primary={true}
                disabled={false}
                onTouchTap={() =>::this.onButtonClick(buttons.submit.id)}
            />,
        ];

        return (
            <Dialog
                title={title}
                actions={actions}
                modal={true}
                open={ui.dialog.open}
                contentStyle={{width: "100%"}}
                autoScrollBodyContent={true}>
                    <TextField
                        value={values.fname}
                        hintText="First Name"
                        floatingLabelText="First Name"
                        floatingLabelFixed={true}
                        onChange={(_,value)=>::this.updateValue('fname', value)} />
                    <br />
                    <TextField
                        value={values.lname}
                        hintText="Last Name"
                        floatingLabelText="Last Name"
                        floatingLabelFixed={true}
                        onChange={(_,value)=>::this.updateValue('lname', value)} />
            </Dialog>
    );
    }
}

export default validation(strategy)(ClientDialog);
jurassix commented 8 years ago

The following two lines are equivalent and could be creating an issue for you? Choose one impl

Explicit constructor binding this:

  constructor(props) {
        this.getValidatorData = this.getValidatorData.bind(this);
  }

  getValidatorData(){/* */}

OR have babel hoist this function into the constructor, auto-binding this:

  constructor(props) {
    super(props);
      this.validatorTypes = {
          fname: Joi.string().alphanum().min(1).max(50).required().label('First Name'),
          lname: Joi.string().alphanum().min(1).max(50).required().label('Last Name'),
      };
  }

  getValidatorData = () => {/* */}

Also make sure you return data from getValidatorData(), and you should probably event.preventDefault() on submit.

Let me know if this helps. Make these changes and report back if the issue is still there.

Rich-amoebaWare commented 8 years ago

Thanks for replying!

I have tried it with the getValidatorData() function both ways to no avail. My normal method as you can see in style is to have the methods hoisted.

I also added event.preventDefault() in the onSubmit() and I get the same invariant violation.

I thought maybe the @ui decorator was interfering so I tried wrapping the component with the validation component before wrapping the wrapped component with @ui. But that also didn't work.

I get the same invariant whether I explicitly bind the functions in the constructor (which i rarely do anymore because it is so verbose): this.getValidatorData = this.getValidatorData.bind(this); this.onSubmit = this.onSubmit.bind(this);

or use my preferred syntax:

getValidatorData = () => {} onSubmit = () => {}

To make sure I had no errors in my code i changed getValidatorData to:

getValidatorData = () => { const data = {id:1, fname:'some', lname:'body'}; return (data); }

And onSubmit to:

onSubmit = () => { console.log(this.getValidatorData()); }

When I click the button, the handler calls onSubmit:

onButtonClick = (button) => {
    const {ui, updateUI} = this.props;
    switch (button) {
        case ui.buttons.cancel.id:
            updateUI({dialog: {...ui.dialog, open: false}});
            break;
        case ui.buttons.submit.id:
            this.onSubmit();
            break;
    }
}

OnSubmit is called:

onSubmit = () => {
    console.log(this.getValidatorData());
    event.preventDefault();
    const onValidate = (error) => {
        if (error) {
        }
        else {
        }
    };
    this.props.validate(onValidate);
}

I see the logged message from my console.log(this.getValidatorData())...but the validate() logs the invariant.

I also see the methods in this.props collection that lets me know the component was wrapped:

clearValidations errors getValidationMessages handleValidation isValid massUpdateUI mountUI resetUI setDefaultUI ui uiKey uiPath unmountUI updateUI validate

Everything else is working in this component. So I am at a loss what to check next.

jurassix commented 8 years ago

Oh I c. The issue here is that you are wrapping your component with @ui HOC the wrapped component doesn't have the required method getValidatorData or validatorTypes.

The component you pass to validation needs to have these above functions: export default validation(strategy)(ClientDialog);

jurassix commented 8 years ago

You might experiment with something like:

@ui({
        state: {
            buttons: {
                cancel: {id: "cancel", label: "Cancel"},
                submit: {id: "submit", label: "Submit"},
            }
        }
})
@validation(strategy)
class ClientDialog extends Component { /* */ }
jurassix commented 8 years ago

In the above example you are passing the validation method the correct Component. I'm not sure how the UI component will work after this but give it a shot.

Rich-amoebaWare commented 8 years ago

That works ! Thank you! Now i can try out the library..

@ui({ state: { buttons: { cancel: {id: "cancel", label: "Cancel"}, submit: {id: "submit", label: "Submit"}, } } }) @validation(strategy) export default class ClientDialog extends Component {

or without decorators:

let validatedClientDialog = validation(strategy)(ClientDialog); let uiValidatedClientDialog = ui({ state: { buttons: { cancel: {id: "cancel", label: "Cancel"}, submit: {id: "submit", label: "Submit"}, } } })(validatedClientDialog); export default uiValidatedClientDialog;

I ran a quick test after changing that and I get errors object when I return invalid data from getValidatorData(). Thanks for your help!

jurassix commented 8 years ago

Excellent. Hope this library helps you.