2016-03-26 376 views
2

我已创建的表单验证与结构这样阵营JS:

var Signin = React.createClass({ 

    render: function() { 

     return (
       <Form> 
        <Input type="text" name="email" labelName="Email" rules="isEmail" error:"Email not valid" /> 
        <Input type="password" name="password" labelName="Password" rules="isLength:6" error:"Passowrd not valid"/> 
       </Form> 
     ); 
    } 
}); 

,因为,例如,“电子邮件”输入将被在应用程序的不同部分中使用可重复使用的部件,我会避免添加每次都有相同的属性(名称,类型,标签名称,规则和错误)。所以,我想创建这样的

var InputEmail = React.createClass({ 

    render: function() { 

     return (
      <Input type="text" name="email" labelName="Email" rules="isEmail" error="Email not valid"/> 
     ) 
    } 
}); 

var InputPassword = React.createClass({ 

    render: function() { 

     return (
      <Input type="password" name="password" labelName="Password" rules="isLength:6" error="Passwordnot valid"/> 
     ) 
    } 
}); 

所以签到组件时,应

var Signin = React.createClass({ 

    render: function() { 

     return (
       <Form> 
        <InputEmail /> 
        <InputPassword /> 
       </Form> 
     ); 
    } 
}); 

但这种方式,我得到两个错误:

  1. 我找不到了在Form中输入props.name,因为 没有在InputEmail中;

  2. 在输入的渲染功能

    状态是空

我怎么可以创建一个reausable /继承组件?我同时使用构图模式和

我将自己完全的代码混入失败:表单

var Form = React.createClass({ 

    getInitialState: function() { 
     return { 
      isValid : false, 
      isSubmitting: false 
     } 
    }, 

    componentWillMount: function(){ 
     this.model = {}; 
     this.inputs = {}; 
     this.registerInputs(this.props.children); 

    }, 

    registerInputs: function(children){ 


     React.Children.forEach(children, function (child) { 


      if (child.props.name) { 


       child.props.attachToForm = this.attachToForm; 

       child.props.detachFromForm = this.detachFromForm; 

       child.props.validate = this.validate; 
      } 
      if (child.props.children) { 
       this.registerInputs(child.props.children); 
      } 
     }.bind(this)); 
    }, 


    attachToForm: function (component) { 
     this.inputs[component.props.name] = component; 
     this.model[component.props.name] = component.state.value; 
     this.validate(component); 
    }, 

    detachFromForm: function (component) { 
     delete this.inputs[component.props.name]; 
     delete this.model[component.props.name]; 
    }, 

    validate: function (component) { 

     var isValid = true; 
       // validation code 
     component.setState({ 
      isValid: isValid, 

     }, this.validateForm); 

    }, 


    validateForm: function() { 
     var formIsValid = true; 

     var inputs = this.inputs; 
     Object.keys(inputs).forEach(function (name) { 
      if (!inputs[name].state.isValid) { 
       formIsValid = false; 
      } 
     }); 

     this.setState({ 
      isValid: formIsValid 
     }); 
    }, 

    updateModel: function (component) { 
     Object.keys(this.inputs).forEach(function (name) { 
      this.model[name] = this.inputs[name].state.value; 
     }.bind(this)); 
    }, 

    submit: function (event) { 
     event.preventDefault(); 

     this.setState({ 
      isSubmitting : true 
     }); 

     this.updateModel(); 
     console.log(this.model); 
    }, 

    render: function() { 

     return (

      <form className="ui form" onSubmit={this.submit}> 
       {this.props.children} 
       <button className="ui button" type="submit" disabled={this.state.isSubmitting}>Accedi</button> 
      </form> 
     ); 
    } 
}); 

输入

var Input = React.createClass({ 

    getInitialState: function(){ 
     return { 
      value : this.props.value || "", 
      isValid: true 
     } 
    }, 

    setValue: function (event) { 

     this.setState({ 
      value: event.target.value 
     }, function() { 

      this.props.validate(this); 

     }.bind(this)); 
    }, 

    componentWillMount: function() { 

     if (this.props.required) { 
      this.props.validations = this.props.validations ? this.props.validations + ',' : ''; 
      this.props.validations += 'isLength:1'; 
     } 

       // ERROR: TypeError: this.props.attachToForm is not a function 
     this.props.attachToForm(this); 
    }, 

    componentWillUnmount: function() { 
     this.props.detachFromForm(this); 
    }, 


    render: function() { 

     var className = "field"; 

     if(this.props.className){ 
      className += " " + this.props.className; 
     } 

     if(this.props.required){ 
      className += " required"; 
     } 

     var Label; 
     if(this.props.labelName){ 
      Label = (<label htmlFor={this.props.name}>{this.props.labelName}</label>); 
     } 

     var Error; 
     if(!this.state.isValid){ 
      Error = (<div className="ui">{this.props.error || this.props.name + " not valid"}</div>); 
     }; 


     return (
      <div className={className}> 
       {Label} 
       <input type={this.props.type || "text"} id={this.props.name} name={this.props.name} onChange={this.setValue} value={this.state.value} /> 
       {Error} 
      </div> 
     ); 

    } 
}); 

有了这个工作

ReactDOM.render(
    <Form> 
     <Input type="text" name="email" labelName="Email" rules="isEmail" error:"Email not valid"/> 
    </Form>, 
    document.getElementById('app') 
); 

这样我获得: “TypeError:this.props.attachToForm不是函数 this.props.attachToForm(this);“

ReactDOM.render(
    <Form> 
     <InputEmail/> 
    </Form>, 
    document.getElementById('app') 
); 

PS:我尝试对的jsfiddle添加此代码,但我得到 “类型错误:无法界定财产 ”attachToForm“:对象是不可扩展的”

jsfiddle

+0

这里的一个建议是不使用单独的组件来封装可重用的行为。您可以改为创建一个代表您想要重复使用的道具的对象,然后将它们传播到需要它们的输入中。var inputEmailProps = {},' – azium

+0

对于试图帮助你解决你使用的其他库问题;看起来你正在使用React-bootstrap,对吧?请务必注意,由于这些API是 – markthethomas

+0

@markthethomas问题的一部分,我正在使用语义UI和验证器的模块进行表单验证 – Webman

回答

0

有2与您的设置有关的主要问题:

  1. 您的<Form>的设置方式是,表格的孩子需要有道具,否则不起作用。
  2. <InputEmail>包装不完整。它需要将所有道具传递给<Input>,包括传递的表单函数。

广告1:固定的形式,以确保验证方法添加
你的错误是因为你<Form>的孩子需要有props.name的原因。然后它将这些表单的功能(包括attachToForm)添加到子表单中。这在方法registerInputs()中完成。

在最初的变体中,<Input>组件具有道具,所以一切顺利。

在适应变形,包装<InputEmail>不再有道具,所以attachToForm()等功能不会添加到道具,你会得到错误当<Input>试图调用该函数。

最简单的办法解决这一问题:新增至少1支托在渲染功能,并在registerInputs()检查这一点,如:

ReactDOM.render(
    <Form> 
     <InputEmail initialValue={'[email protected]'}/> 
    </Form>, 
    document.getElementById('app') 
); 

而且在registerInputs(),更改行:

if (child.props.name) { 

到:

if (child.props.initialValue) { 

2.扩展<InputEmail>包装向下传递功能,以及这样做
最简单的办法是增加{...this.props},像这样:

var InputEmail = React.createClass({ 

    render: function() { 

     return (
      <Input {...this.props} 
       type="text" name="email" labelName="Email" rules="isEmail" error="Email not valid"/> 
     ) 
    } 
}); 

这样一来,通过<Form>向下传递到<InputEmail>组件的功能(以及任何其他道具)将传递到<Input>组件。

PS:registerInputs()内检查child.props.children的代码不能按预期工作:在调用该代码时,<InputEmail>组件没有子代。就像名字所暗示的那样,它会检查作为道具传递下来的孩子。通过的唯一道具是initialValue

由于一些小问题,我建议做2个变化:

  • registerInputs(),你直接修改道具。这通常不是一个好主意。更好的办法是制作一个道具副本,并将你的表单方法添加到副本中。您可以使用React.Children.map来执行此操作。请参阅official docs here
  • 而不是硬编码的<Input>组件的name="email"等,里面<InputEmail>,更好的是把这些默认值的道具默认值,使用propTypes,作为解释here in official docs
  • +0

    谢谢。你写了“有2个主要......”,其他小问题会是什么? – Webman

    +0

    更新的答案也包含针对次要问题的建议。 – wintvelt