2

I'm used to creating controlled components in react using state; however, I'm new to Vue and would like to learn how I could recreate my code from react into something Vue could use. I want the input fields values to be store in a piece of data so that I could do something with them.

<template>
    <form @submit="handleSubmit" class="form form--signin">
        <h2 class="form__h2">Log into your account</h2>

        <label for="email">Email address</label>
        <input v-model="email" type="email" id="email" autoComplete="off"/>

        <label for="password">Password</label>
        <input v-model="password" type="password" id="password" autoComplete="off"/>

        <button class="form__btn">Login</button>
    </form>
</template>

<script>

import axios from 'axios';

export default {
    name: 'SignIn',
    data() {
        return {
            user: null,
            email: null,
            password: null
        }
    },
    methods: {
        handleSubmit(event) {
            event.preventDefault();
            this.login(this.email, this.password);
            this.email = '';
            this.password = '';
        },
        login(email, password) {
            axios.post('http://localhost:8000/api/v1/users/login', {
                email,
                password
            })
            .then(res => {
                // console.log(this.user);
                this.user = res.data.data.user;
                if(res.data.status === 'success') {
                    // setUserStatus(res.data.status);
                    window.setTimeout(() => {
                        window.location.assign('/');
                    }, 1500)
                }
            })
            .catch(err => console.log(err.response.data.message));
        }
    }, 
    mounted() {
        if (!this.user) return;

        console.log('Writing to session storage...');
        sessionStorage.setItem('user', JSON.stringify(this.user));
    }
}

Ajeet Shah
  • 18,551
  • 8
  • 57
  • 87
albert_anthony6
  • 594
  • 2
  • 9
  • 30

3 Answers3

1

This is how it would look like in Vue

Vue.config.devtools = false;
Vue.config.productionTip = false;

const SignIn = Vue.component('sign-in', {

  template: `<form @submit="handleSubmit" className="form form--signin">
            <h2 className="form__h2">Log into your account</h2>

            <label htmlFor="email">Email address</label>
            <input v-model="email" type="email" id="email" autoComplete="off"/>

            <label htmlFor="password">Password</label>
            <input v-model="password" type="password" id="password" autoComplete="off"/>

            <button className="form__btn">Login</button>
        </form>`,
  data() {
    return {
      user: null,
      email: null,
      password: null,
    }
  },
  methods: {
    handleSubmit(event) {

      this.login(this.email, this.password)
      this.email = '';
      this.password = '';
      
      event.preventDefault();
    },
    login(email, password) {

    }
  },
  mounted() {
    if (!this.user)
      return;

    console.log('Writing to session storage...');
    sessionStorage.setItem('user', JSON.stringify(user));
  }
})

var app = new Vue({
  el: '#app'
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
  <sign-in></sign-in>
</div>
Józef Podlecki
  • 10,453
  • 5
  • 24
  • 50
1

This is how I might approach writing the same component in Vue:

<template>
  <form @submit.prevent="handleSubmit" class="form form--signin">
    <h2 class="form__h2">Log into your account</h2>

    <label for="email">Email address</label>
    <input v-model="email" type="email" id="email" autocomplete="off">

    <label for="password">Password</label>
    <input v-model="password" type="password" id="password" autocomplete="off">

    <button class="form__btn">Login</button>
  </form>
</template>

<script>
const login = async (email, password) => {
  // make the api call and return a promise
};

export default {
  name: "SignIn",
  data: () => ({
    email: "",
    password: ""
  }),
  methods: {
    async handleSubmit(event) {
      console.log("Getting user from API...");

      try {
        const user = await login(this.email, this.password);

        console.log("Writing to session storage...");
        sessionStorage.setItem("user", JSON.stringify(user));

        this.email = "";
        this.password = "";
      } catch (e) {
        // handle login errors
      }
    }
  }
};
</script>

The form controls' state is stored on the data property of the component, and a two-way binding is set up on the controls with the v-model directive. Any changes to the inputs will be synced to the component's state, and vice versa.

Vue provides some handy modifiers when adding event listeners to the template, so by writing @submit.prevent the default submit handler is prevented from firing.

I've created a sandbox example, so you can easily play around with the code and see it in action.

Nilson Jacques
  • 468
  • 3
  • 8
1

You can use v-model directive to bind component data and v-on directive to bind component methods:

<template>
  <div>
    <div class>Login Form</div>

    <form v-on:submit="handleSubmit">
      <input v-model="email" placeholder="email" type="text" class="input" />
      <br />
      <input v-model="password" placeholder="password" type="password" class="input" />
      <br />
      <button v-on:click="handleSubmit" type="submit" class="submit">Login</button>
    </form>

    <hr />

    <div>Email: {{email}} and Password: {{password}}</div>
    <div>User sessions: {{user}}</div>
  </div>
</template>

And can use axios to make API call to authenticate and store user session data from response:

<script>
import axios from "axios";
export default {
  name: "LoginForm",
  props: {},
  data: () => ({
    email: "",
    password: "",
    user: {}
  }),
  methods: {
    login() {
      axios
        .post("http://localhost:8000/api/v1/users/login", {
          email: this.email,
          password: this.password
        })
        .then(res => {
          console.log(res);
          this.user = res.data.data.user;
          sessionStorage.setItem("user", JSON.stringify(this.user));
          if (res.data.status === "success") {
            window.setTimeout(() => {
              window.location.assign("/"); // to verify mounted hook
            }, 1500);
          }
        })
        .catch(err => console.log(err.response.data.message));
    },
    handleSubmit: function(e) {
      e.preventDefault();
      this.login();
    }
  },
  mounted() {
    console.debug("mounted");
  }
};
</script>

Also, note that you can use mounted lifecycle hooks to read data from localStorage or any other of your choice.

You can use async / await or then / catch to handle asynchronous (promise based) API calls.

Here is CodeSandbox.


Here is how to use mounted hook same as useEffect(() => {}, []) in react:

mounted() {
    const user = sessionStorage.getItem("user");
    if (user) {
      this.user = JSON.parse(user);
    }
  }
tony19
  • 125,647
  • 18
  • 229
  • 307
Ajeet Shah
  • 18,551
  • 8
  • 57
  • 87
  • the user is never being stored into session storage because it does not immediately equal the response from login because its async. When mounting happens, it it not yet equal to the response so mounted method returns and no user is stored. That is what I am currently stuck on. I could get around this in React with useEffect because it could run whenever the user value changed. But I need a Vue solution – albert_anthony6 May 23 '20 at 16:40
  • I changed `login` method and added a note for `mounted` which uses `sessionStorage.getItem("user");`. Try now. – Ajeet Shah May 23 '20 at 17:05
  • 1
    Thank you for your help, I just got it working by setting the user data in session storage within the login method and it's working now – albert_anthony6 May 23 '20 at 17:07