Я пытаюсь получить входное значение для mongoDB, пока мой пароль хэшируется.
Но мой пароль в моей схеме находится внутри объекта (localProvider). (Это только для обучения, это то, что закодировал мой учитель)
{
username: {
type: String,
required: false,
},
email: {
type: String,
required: true,
trim: true,
unique: true,
match: /^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/,
},
localProvider: {
password: {
type: String,
required: false,
},
},
facebookProvider: {
id: { type: String, required: false },
token: { type: String, required: false },
},
published_at: { type: Date, required: false },
deleted_at: { type: Date, required: false },
},
{
toJSON: { virtuals: true },
toObject: { virtuals: true },
},
{
timestamps: { createdAt: 'created_at', updatedAt: 'updated_at' },
},
);
UserSchema.methods.slugify = function () {
this.slug = slug(this.email);
};
UserSchema.pre('validate', function (next) {
if (!this.slug) {
this.slugify();
}
return next();
});
UserSchema.pre('save', function (next) {
const user = this;
if (!user.isModified('localProvider.password')) return next();// only hash the password if it has been modified (or is new)
try {
return bcrypt.genSalt(config.auth.bcrypt.SALT_WORK_FACTOR, (errSalt, salt) => {
if (errSalt) throw errSalt;
return bcrypt.hash(user.localProvider.password, salt, (errHash, hash) => {
if (errHash) throw errHash;
user.localProvider.password = hash;
return next();
});
});
} catch (error) {
return next(error);
}
});
UserSchema.methods.comparePassword = function (candidatePassword, cb) {
const user = this;
bcrypt.compare(candidatePassword, user.localProvider.password, (err, isMatch) => {
if (err) return cb(err, null);
return cb(null, isMatch);
});
};
UserSchema.virtual('id').get(function () { return this._id; });
UserSchema.plugin(mongoosePaginate);
export default mongoose.model('User', UserSchema);
Я пробовал в register.js передавать свои входные значения с помощью onChange
[event.target.name]: event.target.value
Только эта строка кода работает только для одиночных значений вне объекта.
Как вы видели на схеме, насколько я знаю, нет способа передать значение в одно значение состояния и во вложенное значение состояния.
Итак, есть ли решение исправить код, чтобы я мог отправить также имя пользователя и адрес электронной почты в состояние и передать пароль в localProvider в 1 handleChangeEvent?
код из моего register.js
class Signup extends Component {
constructor() {
super()
this.state = {
username: '',
email: '',
redirectTo: null,
localProvider:{
password: ''
}
}
this.handleSubmit = this.handleSubmit.bind(this)
this.handleChangeEvent = this.handleChangeEvent.bind(this)
}
handleInputChange(event, value){
this.setState({
[event.target.name]: event.target.value
})
}
handleChangeEvent(el) {
let inputName = el.target.name;
let inputValue = el.target.value;
let statusCopy = Object.assign({}, this.state);
statusCopy.localProvider[inputName].value = inputValue;
this.setState(statusCopy);
}
handleSubmit(event) {
console.info('sign-up handleSubmit, username: ')
console.info(this.state.username)
event.preventDefault()
//request to server to add a new username/password
axios.post('/api/v1/users/', {
...this.state
})
.then(response => {
console.info(response)
if (!response.data.errmsg) {
console.info('successful signup')
this.props.history.push("/");
} else {
console.info('username already taken')
}
}).catch(error => {
console.info('signup error: ')
console.info(error)
})
}
render() {
const { classes } = this.props;
return (
<React.Fragment>
<CssBaseline />
<Paper className = {classes.paper}>
<Avatar className = {classes.avatar}>
<LockOutlinedIcon />
</Avatar>
<Typography component = "h1" variant = "h5">
Sign in
</Typography>
<form className = {classes.form}>
<FormControl margin = "normal" required fullWidth>
<InputLabel htmlFor = "name">Name</InputLabel>
<Input name = "username" type = "text" id = "username" autoComplete = "username" onChange = {this.handleInputChange} />
</FormControl>
<FormControl margin = "normal" required fullWidth>
<InputLabel htmlFor = "email">Email Address</InputLabel>
<Input id = "email" name = "email" autoComplete = "email" onChange = {this.handleInputChange} />
</FormControl>
<FormControl margin = "normal" required fullWidth>
<InputLabel htmlFor = "password">Password</InputLabel>
<Input type = "password" id = "password" autoComplete = "current-password" onChange = {this.handleChangeEvent} name = "password"/>
</FormControl>
<FormControlLabel
control = {<Checkbox value = "remember" color = "primary" />}
label = "Remember me"
/>
<Button
type = "submit"
fullWidth
variant = "contained"
color = "primary"
>
Sign in
</Button>
</form>
</Paper>
</React.Fragment>
)
}
}
export default withStyles(styles)(Signup);```





handleChangeEvent(el) {
let inputName = el.target.name;
let inputValue = el.target.value;
let providerCopy = Object.assign({}, this.state.localProvider); // new
let statusCopy = Object.assign({}, this.state, {localProvider: providerCopy}); //edited
statusCopy.localProvider[inputName].value = inputValue;
this.setState(statusCopy);
}
Object.assign() копирует только на один уровень в глубину, и вам нужно глубоко копировать на 2 уровня, поэтому вы можете использовать его дважды или использовать оператор распространения над объектом, который также работает.
let nextState = {...this.state, localProvider: {...this.state.localProvider, [el.target.name]: [el.target.value] } };
this.setState(nextState);
См. здесь для получения дополнительной информации об изменениях вложенных состояний.
хаха глупая ошибка! Теперь я получаю сообщение об ошибке в handleInputChange, что this.setState не является функцией. Любые подсказки, как это исправить?
Да, добавьте this.handleInputChange = this.handleInputChange.bind(this); внутри вашего конструктора
А, это потому, что вы никогда не вызываете функцию handleSubmit. Идите вперед и передайте его onClick вашего <Button>