0

I'm trying to get the input value to mongoDB while my password is being hashed.

But my password in my scheme is inside an object (localProvider). (This is only for educational only, this is what my teacher coded)

    {
        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);

I've tried in the register.js passing my input values with onChange

[event.target.name]: event.target.value Only this line of code works only for the single values not inside an object. As you've seen in the scheme there's no way, so far I know, to pass value to a single state value and to a nested state value.

So is there a solution to fix the code so I'm able to push also the username & email to the state and push the password to localProvider in 1 handleChange?

code from my register.js

class Signup extends Component {
  constructor() {
        super()
        this.state = {
            username: '',
      email: '',
      redirectTo: null,
      localProvider:{
         password: ''

      }


    }

        this.handleSubmit = this.handleSubmit.bind(this)
    this.handleChange = this.handleChange.bind(this)
  }
  handleInputChange(event, value){
      this.setState({
        [event.target.name]: event.target.value
      })
  }
  handleChange(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.log('sign-up handleSubmit, username: ')
        console.log(this.state.username)
        event.preventDefault()

        //request to server to add a new username/password
        axios.post('/api/v1/users/', {
            ...this.state
        })
            .then(response => {
                console.log(response)
                if (!response.data.errmsg) {
          console.log('successful signup')
          this.props.history.push("/");
                } else {
                    console.log('username already taken')
                }
            }).catch(error => {
                console.log('signup error: ')
                console.log(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.handleChange} 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);```
indy
  • 25
  • 7

1 Answers1

1
  handleChange(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() only copies to one level deep and you need to deep copy for 2 levels, so you can use it twice or use the spread operator over an object also works.

let nextState = {...this.state, localProvider: {...this.state.localProvider, [el.target.name]: [el.target.value] } };
this.setState(nextState);

Look here for more information on nested state changes.

Peter Cheng
  • 758
  • 7
  • 9
  • Oh that's because you never call the handleSubmit function. Go ahead and pass it to your – Peter Cheng Jun 05 '19 at 21:36
  • haha stupid mistake! I'm getting now an error on handleInputChange that this.setState is not an function. Any clues how to fix that? – indy Jun 05 '19 at 21:55
  • 1
    Yes, add this.handleInputChange = this.handleInputChange.bind(this); inside your constructor – Peter Cheng Jun 05 '19 at 22:58