我正在尝试使用 React 和 Redux 创建一个多步骤注册表单。
主要组成部分如下:
import React, {PropTypes} from 'react';
import {connect} from 'react-redux';
import {bindActionCreators} from 'redux';
import * as actionCreators from '../../actions/actionCreators';
import countries from '../../data/countries';
import RegistrationFormStepOne from './registrationFormStepOne';
import RegistrationFormStepTwo from './registrationFormStepTwo';
import RegistrationFormStepThree from './registrationFormStepThree';
import RegistrationFormStepFour from './registrationFormStepFour';
class RegistrationPage extends React.Component {
constructor(props) {
super(props);
this.state = {
user: Object.assign({}, this.props.userData),
fileNames: {},
selectedFile: {},
icons: {
idCard: 'upload',
statuten: 'upload',
blankLetterhead: 'upload',
companyPhoto: 'upload'
},
step: 1,
errors: {}
};
this.setUser = this.setUser.bind(this);
this.onButtonClick = this.onButtonClick.bind(this);
this.onButtonPreviousClick = this.onButtonPreviousClick.bind(this);
this.changeCheckboxState = this.changeCheckboxState.bind(this);
this.onFileChange = this.onFileChange.bind(this);
this.routerWillLeave = this.routerWillLeave.bind(this);
}
componentDidMount() {
this.context.router.setRouteLeaveHook(this.props.route, this.routerWillLeave);
}
routerWillLeave(nextLocation) {
if (this.state.step > 1) {
this.setState({step: this.state.step - 1});
return false;
}
}
getCountries(){
return countries;
}
setUser(event) {
const field = event.target.name;
const value = event.target.value;
let user = this.state.user;
user[field] = value;
this.setState({user: user});
}
validation(){
const user = this.state.user;
const languageReg = this.props.currentLanguage.default.registrationPage;
let formIsValid = true;
let errors = {};
if(!user.companyName){
formIsValid = false;
errors.companyName = languageReg.companyNameEmpty;
}
if(!user.btwNumber){
formIsValid = false;
errors.btwNumber = languageReg.btwNumberEmpty;
}
if(!user.address){
formIsValid = false;
errors.address = languageReg.addressEmpty;
}
if(!user.country){
formIsValid = false;
errors.country = languageReg.countryEmpty;
}
if(!user.zipcode){
formIsValid = false;
errors.zipcode = languageReg.zipcodeEmpty;
}
if(!user.place){
formIsValid = false;
errors.place = languageReg.placeEmpty;
}
if(!user.firstName){
formIsValid = false;
errors.firstName = languageReg.firstnameEmpty;
}
this.setState({errors: errors});
return formIsValid;
}
onFileChange(name, event) {
event.preventDefault();
let file = event.target.value;
let filename = file.split('\\').pop(); //We get only the name of the file
let filenameWithoutExtension = filename.replace(/\.[^/.]+$/, ""); //We get the name of the file without extension
let user = this.state.user;
let fileNames = this.state.fileNames;
let selectedFile = this.state.selectedFile;
let icons = this.state.icons;
switch (name.btnName) {
case "idCard" :
fileNames[name.btnName] = filenameWithoutExtension;
//Check if file is selected
if(file){
selectedFile[name.btnName] = "fileSelected";
user["idCardFile"] = true;
icons["idCard"] = "check";
}else{
selectedFile[name.btnName] = "";
user["idCardFile"] = false;
icons["idCard"] = "upload";
}
break;
case "statuten" :
fileNames[name.btnName] = filenameWithoutExtension;
//Check if file is selected
if(file){
selectedFile[name.btnName] = "fileSelected";
user["statutenFile"] = true;
icons["statuten"] = "check";
}else{
selectedFile[name.btnName] = "";
user["statutenFile"] = false;
icons["statuten"] = "upload";
}
break;
case "blankLetterhead" :
fileNames[name.btnName] = filenameWithoutExtension;
//Check if file is selected
if(file){
selectedFile[name.btnName] = "fileSelected";
user["blankLetterheadFile"] = true;
icons["blankLetterhead"] = "check";
}else{
selectedFile[name.btnName] = "";
user["blankLetterheadFile"] = false;
icons["blankLetterhead"] = "upload";
}
break;
default:
fileNames[name.btnName] = filenameWithoutExtension;
//Check if file is selected
if(file){
selectedFile[name.btnName] = "fileSelected";
user["companyPhotoFile"] = true;
icons["companyPhoto"] = "check";
}else{
selectedFile[name.btnName] = "";
user["companyPhotoFile"] = false;
icons["companyPhoto"] = "upload";
}
}
this.setState({user: user, fileNames: fileNames, selectedFile: selectedFile, icons: icons});
}
changeCheckboxState(event) {
let chcName = event.target.name;
let user = this.state.user;
switch (chcName) {
case "chcEmailNotificationsYes":
user["emailNotifications"] = event.target.checked;
break;
case "chcEmailNotificationsNo":
user["emailNotifications"] = !event.target.checked;
break;
case "chcTerms":
if(typeof this.state.user.terms === "undefined"){
user["terms"] = false;
}else{
user["terms"] = !this.state.user.terms;
}
break;
case "chcSmsYes":
user["smsNotifications"] = event.target.checked;
break;
default:
user["smsNotifications"] = !event.target.checked;
}
this.setState({user: user});
this.props.actions.userRegistration(this.state.user);
}
onButtonClick(name, event) {
event.preventDefault();
this.props.actions.userRegistration(this.state.user);
switch (name) {
case "stepFourConfirmation":
this.setState({step: 1});
break;
case "stepTwoNext":
this.setState({step: 3});
break;
case "stepThreeFinish":
this.setState({step: 4});
break;
default:
if(this.validation()) {
this.setState({step: 2});
}
}
}
onButtonPreviousClick(){
this.setState({step: this.state.step - 1});
}
render() {
const languageReg = this.props.currentLanguage.default.registrationPage;
console.log(this.state.user);
let formStep = '';
let step = this.state.step;
switch (step) {
case 1:
formStep = (<RegistrationFormStepOne user={this.props.userData}
onChange={this.setUser}
onButtonClick={this.onButtonClick}
countries={this.getCountries(countries)}
errors={this.state.errors}
step={step}/>);
break;
case 2:
formStep = (<RegistrationFormStepTwo user={this.props.userData}
onChange={this.setUser}
onButtonClick={this.onButtonClick}
onButtonPreviousClick={this.onButtonPreviousClick}
errors={this.state.errors}/>);
break;
case 3:
formStep = (<RegistrationFormStepThree user={this.props.userData}
onFileChange={this.onFileChange}
onButtonClick={this.onButtonClick}
onButtonPreviousClick={this.onButtonPreviousClick}
errors={this.state.errors}
fileNames={this.state.fileNames}
icons={this.state.icons}
fileChosen={this.state.selectedFile}/>);
break;
default:
formStep = (<RegistrationFormStepFour user={this.props.userData}
onChange={this.setUser}
onChangeCheckboxState={this.changeCheckboxState}
onButtonClick={this.onButtonClick}
onButtonPreviousClick={this.onButtonPreviousClick}
errors={this.state.errors}/>);
}
return (
<div className="sidebar-menu-container" id="sidebar-menu-container">
<div className="sidebar-menu-push">
<div className="sidebar-menu-overlay"></div>
<div className="sidebar-menu-inner">
<div className="contact-form">
<div className="container">
<div className="row">
<div className="col-md-10 col-md-offset-1 col-md-offset-right-1">
{React.cloneElement(formStep, {currentLanguage: languageReg})}
</div>
</div>
</div>
</div>
</div>
</div>
</div>
);
}
}
RegistrationPage.contextTypes = {
router: PropTypes.object
};
function mapStateToProps(state, ownProps) {
return {
userData: state.userRegistrationReducer
};
}
function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators(actionCreators, dispatch)
};
}
export default connect(mapStateToProps, mapDispatchToProps)(RegistrationPage);
第一步组件如下
import React from 'react';
import Button from '../../common/formElements/button';
import RegistrationFormHeader from './registrationFormHeader';
import TextInput from '../../common/formElements/textInput';
import SelectInput from '../../common/formElements/selectInput';
const RegistrationFormStepOne = ({user, onChange, onButtonClick, errors, currentLanguage, countries}) => {
const language = currentLanguage;
return (
<div className="contact_form">
<form role="form" action="" method="post" id="contact_form">
<div className="row">
<RegistrationFormHeader activeTab={0} currentLanguage={language}/>
<div className="hideOnBigScreens descBox">
<div className="headerTitle">{language.businessInfoConfig}</div>
<div className="titleDesc">{language.businessBoxDesc}</div>
</div>
<div className="col-lg-12">
<h6 className="registrationFormDesc col-lg-10 col-lg-offset-1 col-lg-offset-right-2 col-xs-12">
{language.businessDesc}
</h6>
<div className="clearfix"></div>
<div className="col-sm-6">
<TextInput
type="text"
name="companyName"
label={language.companyNameLabel}
labelClass="control-label"
placeholder={language.companyNameLabel}
className="templateInput"
id="company"
onChange={onChange}
value={user.companyName}
errors={errors.companyName}
/>
</div>
<div className="col-sm-6">
<TextInput
type="text"
name="btwNumber"
label={language.vatNumberLabel}
placeholder={language.vatNumberLabel}
className="templateInput"
id="btwNumber"
onChange={onChange}
value={user.btwNumber}
errors={errors.btwNumber}
/>
</div>
<div className="col-sm-12" style={{marginBottom: 25}}>
<TextInput
type="text"
name="address"
label={language.addressLabel}
placeholder={language.address1Placeholder}
className="templateInput"
id="address"
onChange={onChange}
value={user.address}
errors={errors.address}
/>
</div>
<div className="col-sm-12" style={{marginBottom: 25}}>
<TextInput
type="text"
name="address1"
placeholder={language.address2Placeholder}
className="templateInput"
id="address"
onChange={onChange}
value={user.address1}
errors=""
/>
</div>
<div className="col-sm-12">
<TextInput
type="text"
name="address2"
placeholder={language.address3Placeholder}
className="templateInput"
id="address"
onChange={onChange}
value={user.address2}
errors=""
/>
</div>
<div className="col-sm-3">
<SelectInput name="country"
label={language.selectCountryLabel}
onChange={onChange}
options={countries}
className="templateInput selectField"
defaultOption={language.selectCountry}
value={user.country}
errors={errors.country}
/>
</div>
<div className="col-sm-3">
<TextInput
type="text"
name="zipcode"
label={language.zipcodeLabel}
placeholder={language.zipcodeLabel}
className="templateInput"
id="zipcode"
onChange={onChange}
value={user.zipcode}
errors={errors.zipcode}
/>
</div>
<div className="col-sm-6">
<TextInput
type="text"
name="place"
label={language.placeLabel}
placeholder={language.placeLabel}
className="templateInput"
id="place"
onChange={onChange}
value={user.place}
errors={errors.place}
/>
</div>
</div>
<div className="clearfix"></div>
<div className="col-lg-12" style={{marginLeft: 15, marginTop: 30}}>
<Button onClick={onButtonClick.bind(this)}
name="stepOneNext"
value={language.btnNext}
icon="arrow-circle-right"
style={{margin: '0 auto 60px'}}/>
</div>
</div>
</form>
</div>
);
};
export default RegistrationFormStepOne;
我尝试添加一些简单的验证,并且在我的主要组件中添加了验证功能,然后我检查按钮点击是否返回值是真还是假。如果它是真的,那么我将步骤状态设置为适当的值。如果我只验证第一步的表单字段,但当我尝试验证下一步的一个或多个表单字段时,它会起作用(现在我正在尝试验证第二步的第一个字段)
if(!user.firstName){
formIsValid = false;
errors.firstName = languageReg.firstnameEmpty;
}
我比
警告:TextInput 正在将不受控制的文本类型输入更改为受控制。输入元素不应从不受控制切换到受控(反之亦然)。在组件的生命周期内决定使用受控或非受控输入元素。
没有验证功能,一切都完美无缺。
有什么建议吗?
编辑
import React, {propTypes} from 'react';
import _ from 'lodash';
const TextInput = ({errors, style, name, labelClass, label, className, placeholder, id, value, onChange, type}) => {
let wrapperClass = "form-group";
if (errors) {
wrapperClass += " " + "inputHasError";
}
return (
<div className={wrapperClass} style={style}>
<label htmlFor={name} className={labelClass}>{label}</label>
<input type={type}
className={className}
placeholder={placeholder}
name={name}
id={id}
value={value}
style={{}}
onChange={onChange}
/>
<div className="errorBox">{errors}</div>
</div>
);
};
TextInput.propTypes = {
name: React.PropTypes.string.isRequired,
label: React.PropTypes.string,
onChange: React.PropTypes.func.isRequired,
type: React.PropTypes.string.isRequired,
id: React.PropTypes.string,
style: React.PropTypes.object,
placeholder: React.PropTypes.string,
className: React.PropTypes.string,
labelClass: React.PropTypes.string,
value: React.PropTypes.string,
errors: React.PropTypes.string
};
export default TextInput;
这是第二步组件:
import React from 'react';
import Button from '../../common/formElements/button';
import RegistrationFormHeader from './registrationFormHeader';
import TextInput from '../../common/formElements/textInput';
const RegistrationFormStepTwo = ({user, onChange, onButtonClick, onButtonPreviousClick, errors, currentLanguage}) => {
const language = currentLanguage;
return (
<div className="contact_form">
<form role="form" action="" method="post" id="contact_form">
<div className="row">
<RegistrationFormHeader activeTab={1} currentLanguage={language}/>
<div className="hideOnBigScreens descBox">
<div className="headerTitle">{language.personalInfoConfig}</div>
<div className="titleDesc">{language.personalBoxDesc}</div>
</div>
<div className="col-lg-12">
<h6 className="registrationFormDesc col-lg-10 col-lg-offset-1 col-lg-offset-right-2 col-xs-12">
{language.personalDesc}
</h6>
<div className="col-lg-6 col-md-6 col-sm-6 col-xs-12">
<TextInput
type="text"
name="firstName"
label={language.firsnameLabel}
placeholder={language.firsnameLabel}
className="templateInput"
id="name"
onChange={onChange}
value={user.firstName}
errors={errors.firstName}
/>
</div>
<div className="col-lg-6 col-md-6 col-sm-6 col-xs-12">
<TextInput
type="text"
name="lastName"
label={language.lastnameLabel}
placeholder={language.lastnameLabel}
className="templateInput"
id="name"
onChange={onChange}
value={user.lastName}
errors={errors.lastName}
/>
</div>
<div className="col-lg-6 col-md-6 col-sm-6 col-xs-12">
<TextInput
type="text"
name="phone"
label={language.phoneLabel}
placeholder={language.phoneLabel}
className="templateInput"
id="phone"
onChange={onChange}
value={user.phone}
errors={errors.phone}
/>
</div>
<div className="col-lg-6 col-md-6 col-sm-6 col-xs-12">
<TextInput
type="text"
name="mobilePhone"
label={language.mobileLabel}
placeholder={language.mobileLabel}
className="templateInput"
id="phone"
style={{}}
onChange={onChange}
value={user.mobilePhone}
errors={errors.mobilePhone}
/>
</div>
<div className="clearfix"></div>
<div className="col-lg-12 col-md-12 col-sm-12 col-xs-12">
<TextInput
type="text"
name="email"
id="email"
label={language.emailLabel}
placeholder={language.emailLabel}
className="templateInput"
style={{}}
onChange={onChange}
value={user.email}
errors={errors.email}
/>
</div>
<div className="col-lg-12 col-md-12 col-sm-12 col-xs-12">
<TextInput
type="text"
name="userName"
label={language.usernameLabel}
placeholder={language.usernameLabel}
className="templateInput"
id="name"
onChange={onChange}
value={user.userName}
errors={errors.userName}
/>
</div>
<div className="col-lg-6 col-md-6 col-sm-6 col-xs-12">
<TextInput
type="password"
name="password"
label={language.passwordLabel}
placeholder={language.passwordLabel}
className="templateInput"
id="password"
onChange={onChange}
value={user.password}
errors={errors.password}
/>
</div>
<div className="col-lg-6 col-md-6 col-sm-6 col-xs-12">
<TextInput
type="password"
name="confirmPassword"
label={language.passwordConfirmLabel}
placeholder={language.passwordConfirmLabel}
className="templateInput"
id="password"
onChange={onChange}
value={user.confirmPassword}
errors={errors.confirmPassword}
/>
</div>
</div>
<div className="clearfix"></div>
<div className="col-lg-6 col-xs-6" style={{marginTop: 30}}>
<Button onClick={onButtonPreviousClick}
name="btnPrevious"
value={language.btnPrevious}
icon="arrow-circle-left"
style={{marginRight: 10, float: 'right'}}/>
</div>
<div className="col-lg-6 col-xs-6" style={{marginTop: 30}}>
<Button onClick={onButtonClick} name="stepTwoNext" value={language.btnNext}
icon="arrow-circle-right" style={{marginLeft: 10, float: 'left'}}/>
</div>
</div>
</form>
</div>
);
};
export default RegistrationFormStepTwo;
最佳答案
这就是警告存在的原因:当值被指定为未定义时,React 无法知道您是否打算渲染一个具有空值的组件,或者您是否打算让组件不受控制。它是错误的来源。
在将值传递给输入之前,您可以进行 null/undefined 检查。
关于javascript - 类型文本不受控制的输入被控制警告,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38014397/
我想将html转换为纯文本。不过,我不想只删除标签,我想智能地保留尽可能多的格式。为插入换行符标签,检测段落并格式化它们等。输入非常简单,通常是格式良好的html(不是整个文档,只是一堆内容,通常没有anchor或图像)。我可以将几个正则表达式放在一起,让我达到80%,但我认为可能有一些现有的解决方案更智能。 最佳答案 首先,不要尝试为此使用正则表达式。很有可能你会想出一个脆弱/脆弱的解决方案,它会随着HTML的变化而崩溃,或者很难管理和维护。您可以使用Nokogiri快速解析HTML并提取文本:require'nokogiri'h
我试图使用yard记录一些Ruby代码,尽管我所做的正是所描述的here或here#@param[Integer]thenumberoftrials(>=0)#@param[Float]successprobabilityineachtrialdefinitialize(n,p)#initialize...end虽然我仍然得到这个奇怪的错误@paramtaghasunknownparametername:the@paramtaghasunknownparametername:success然后生成的html看起来很奇怪。我称yard为:$yarddoc-mmarkdown我做错了什么?
我正在使用active_admin,我在Rails3应用程序的应用程序中有一个目录管理,其中包含模型和页面的声明。时不时地我也有一个类,当那个类有一个常量时,就像这样:classFooBAR="bar"end然后,我在每个必须在我的Rails应用程序中重新加载一些代码的请求中收到此警告:/Users/pupeno/helloworld/app/admin/billing.rb:12:warning:alreadyinitializedconstantBAR知道发生了什么以及如何避免这些警告吗? 最佳答案 在纯Ruby中:classA
当我在Rails控制台中按向上或向左箭头时,出现此错误:irb(main):001:0>/Users/me/.rvm/gems/ruby-2.0.0-p247/gems/rb-readline-0.4.2/lib/rbreadline.rb:4269:in`blockin_rl_dispatch_subseq':invalidbytesequenceinUTF-8(ArgumentError)我使用rvm来管理我的ruby安装。我正在使用=>ruby-2.0.0-p247[x86_64]我使用bundle来管理我的gem,并且我有rb-readline(0.4.2)(人们推荐的最少
我可以得到Infinity和NaNn=9.0/0#=>Infinityn.class#=>Floatm=0/0.0#=>NaNm.class#=>Float但是当我想直接访问Infinity或NaN时:Infinity#=>uninitializedconstantInfinity(NameError)NaN#=>uninitializedconstantNaN(NameError)什么是Infinity和NaN?它们是对象、关键字还是其他东西? 最佳答案 您看到打印为Infinity和NaN的只是Float类的两个特殊实例的字符串
我不确定传递给方法的对象的类型是否正确。我可能会将一个字符串传递给一个只能处理整数的函数。某种运行时保证怎么样?我看不到比以下更好的选择:defsomeFixNumMangler(input)raise"wrongtype:integerrequired"unlessinput.class==FixNumother_stuffend有更好的选择吗? 最佳答案 使用Kernel#Integer在使用之前转换输入的方法。当无法以任何合理的方式将输入转换为整数时,它将引发ArgumentError。defmy_method(number)
我正在使用Ruby2.1.1和Rails4.1.0.rc1。当执行railsc时,它被锁定了。使用Ctrl-C停止,我得到以下错误日志:~/.rvm/gems/ruby-2.1.1/gems/spring-1.1.2/lib/spring/client/run.rb:47:in`gets':Interruptfrom~/.rvm/gems/ruby-2.1.1/gems/spring-1.1.2/lib/spring/client/run.rb:47:in`verify_server_version'from~/.rvm/gems/ruby-2.1.1/gems/spring-1.1.
最近,当我启动我的Rails服务器时,我收到了一长串警告。虽然它不影响我的应用程序,但我想知道如何解决这些警告。我的估计是imagemagick以某种方式被调用了两次?当我在警告前后检查我的git日志时。我想知道如何解决这个问题。-bcrypt-ruby(3.1.2)-better_errors(1.0.1)+bcrypt(3.1.7)+bcrypt-ruby(3.1.5)-bcrypt(>=3.1.3)+better_errors(1.1.0)bcrypt和imagemagick有关系吗?/Users/rbchris/.rbenv/versions/2.0.0-p247/lib/ru
我将我的Rails应用程序部署到OpenShift,它运行良好,但我无法在生产服务器上运行“Rails控制台”。它给了我这个错误。我该如何解决这个问题?我尝试更新rubygems,但它也给出了权限被拒绝的错误,我也无法做到。railsc错误:Warning:You'reusingRubygems1.8.24withSpring.UpgradetoatleastRubygems2.1.0andrun`gempristine--all`forbetterstartupperformance./opt/rh/ruby193/root/usr/share/rubygems/rubygems
有时我需要处理键/值数据。我不喜欢使用数组,因为它们在大小上没有限制(很容易不小心添加超过2个项目,而且您最终需要稍后验证大小)。此外,0和1的索引变成了魔数(MagicNumber),并且在传达含义方面做得很差(“当我说0时,我的意思是head...”)。散列也不合适,因为可能会不小心添加额外的条目。我写了下面的类来解决这个问题:classPairattr_accessor:head,:taildefinitialize(h,t)@head,@tail=h,tendend它工作得很好并且解决了问题,但我很想知道:Ruby标准库是否已经带有这样一个类? 最佳