Our website is made possible by displaying online advertisements to our visitors. Please consider supporting us by disabling your ad blocker.

Simple User Login in a Vue.js Web Application

TwitterFacebookRedditLinkedInHacker News

Sometimes the best examples towards learning a new framework is through a simple user login sample. Login involves many concepts, including forms, data binding, routing, and potentially HTTP to a remote service, all of which are core concepts in any web application.

We’re going to see how to implement a simple login form in a web application that uses the Vue.js JavaScript framework.

The animated image below is an example of what we’re going to try to accomplish.

Vue.js Simple Login Example

We’re going to have a login screen with some hard coded credential data. When the correct information is entered, the application will navigate to a potentially secure page. Trying to access the page manually will result in navigation back to the login page because the application hadn’t been authenticated. The authorization status and logout is controlled by a parent component.

Create a New Vue.js Web Application

To make our lives easier, we’re going to use the Vue CLI to create a new project. It is not absolutely necessary to use the Vue CLI, but I totally recommend it.

With the CLI installed and configured, execute the following command:

vue create simple-login-project

When prompted, choose to specify the features that you’d like included. For this application, the only required feature will be the vue-router. It doesn’t matter how you choose to store the configuration data.

At this point we can start development.

Develop Login Functionality in the JavaScript Web Application

For this project we’re going to focus on two components which will act as our routes as well as a parent component that the routes will pass through.

In your project, create a src/views/secure.vue file with the following code:

<template>
    <div id="secure">
        <h1>Secure Area</h1>
        <p>
            This is a secure area
        </p>
    </div>
</template>

<script>
    export default {
        name: 'Secure',
        data() {
            return {};
        }
    }
</script>

<style scoped>
    #secure {
        background-color: #FFFFFF;
        border: 1px solid #CCCCCC;
        padding: 20px;
        margin-top: 10px;
    }
</style>

The above code represents our protected application area. For example, it should only be accessed if the user has logged in. Because this is our end goal, we don’t really need to add any logic to the page. Getting to the page is enough for us.

Now create a src/views/login.vue file with the following code:

<template>
    <div id="login">
        <h1>Login</h1>
        <input type="text" name="username" v-model="input.username" placeholder="Username" />
        <input type="password" name="password" v-model="input.password" placeholder="Password" />
        <button type="button" v-on:click="login()">Login</button>
    </div>
</template>

<script>
    export default {
        name: 'Login',
        data() {
            return {
                input: {
                    username: "",
                    password: ""
                }
            }
        },
        methods: {
            login() {
                if(this.input.username != "" && this.input.password != "") {
                    if(this.input.username == this.$parent.mockAccount.username && this.input.password == this.$parent.mockAccount.password) {
                        this.$emit("authenticated", true);
                        this.$router.replace({ name: "secure" });
                    } else {
                        console.log("The username and / or password is incorrect");
                    }
                } else {
                    console.log("A username and password must be present");
                }
            }
        }
    }
</script>

<style scoped>
    #login {
        width: 500px;
        border: 1px solid #CCCCCC;
        background-color: #FFFFFF;
        margin: auto;
        margin-top: 200px;
        padding: 20px;
    }
</style>

More is happening in our login.vue file than the previous, so let’s break it down, starting with the <script> block.

We know this page will have a login form so we need to initialize the variables that will bind it:

data() {
    return {
        input: {
            username: "",
            password: ""
        }
    }
},

When we are ready to sign in, the login method will be called:

login() {
    if(this.input.username != "" && this.input.password != "") {
        if(this.input.username == this.$parent.mockAccount.username && this.input.password == this.$parent.mockAccount.password) {
            this.$emit("authenticated", true);
            this.$router.replace({ name: "secure" });
        } else {
            console.log("The username and / or password is incorrect");
        }
    } else {
        console.log("A username and password must be present");
    }
}

Both the username and password need to be present to move on. Since we aren’t actually passing the username and password in an HTTP call, we’re going to be comparing against some mock data. The mock data will exist in the parent component which we’ll see next.

If you’d like to see how to make HTTP requests from your Vue.js application, check out my previous tutorial titled, Consume Remote API Data via HTTP in a Vue.js Web Application.

If the user input matches the mock data, we need to emit that we’re authenticated and navigate to the protected component. Don’t worry, we’re going to define our routes later.

With the logic out of the way, we have the HTML template that powers the UI:

<template>
    <div id="login">
        <h1>Login</h1>
        <input type="text" name="username" v-model="input.username" placeholder="Username" />
        <input type="password" name="password" v-model="input.password" placeholder="Password" />
        <button type="button" v-on:click="login()">Login</button>
    </div>
</template>

Using the v-model attribute, we can bind our variables in a two-way fashion. We can also bind our login function via the v-on:click attribute. Nothing fancy is happening in the UI. It more or less strings things together.

This brings us to our parent component in which our two previous components pass through.

Open the project’s src/App.vue file and include the following code:

<template>
    <div id="app">
        <div id="nav">
            <router-link v-if="authenticated" to="/login" v-on:click.native="logout()" replace>Logout</router-link>
        </div>
        <router-view @authenticated="setAuthenticated" />
    </div>
</template>

<script>
    export default {
        name: 'App',
        data() {
            return {
                authenticated: false,
                mockAccount: {
                    username: "nraboy",
                    password: "password"
                }
            }
        },
        mounted() {
            if(!this.authenticated) {
                this.$router.replace({ name: "login" });
            }
        },
        methods: {
            setAuthenticated(status) {
                this.authenticated = status;
            },
            logout() {
                this.authenticated = false;
            }
        }
    }
</script>

<style>
    body {
        background-color: #F0F0F0;
    }
    h1 {
        padding: 0;
        margin-top: 0;
    }
    #app {
        width: 1024px;
        margin: auto;
    }
</style>

Again, we’re going to start by analyzing the <script> block which contains our application logic.

The first thing that we do is initialize the variables that will be used throughout this component:

data() {
    return {
        authenticated: false,
        mockAccount: {
            username: "nraboy",
            password: "password"
        }
    }
},

You’ll notice that we’re initializing our mock data as well as an authentication variable. Remember, we emitted an authentication status from the login.vue file.

When the application mounts, we can do some checks:

mounted() {
    if(!this.authenticated) {
        this.$router.replace({ name: "login" });
    }
},

If we’re not currently authenticated, we should navigate to the login component. This will prevent anyone from trying to directly navigate to a protected page while not authenticated.

Next we have two methods:

setAuthenticated(status) {
    this.authenticated = status;
},
logout() {
    this.authenticated = false;
}

Remember when we emitted the authentication status? We’re going to update it via the setAuthenticated method, but not yet. When we want to sign out, we have the logout method which will set our authentication status to false.

Now let’s check out the UI for this particular component:

<template>
    <div id="app">
        <div id="nav">
            <router-link v-if="authenticated" to="/login" v-on:click.native="logout()" replace>Logout</router-link>
        </div>
        <router-view @authenticated="setAuthenticated" />
    </div>
</template>

We have a link in our parent component that will only show if we are not authenticated. This link will be our means to sign out of the application. Notice the <router-view> tag and the @authenticated attribute. This is a listener. In the login.vue file we are emitting data on an event called authenticated, hence the @authenticated that we use. If we find some data, we’ll call the callback method which will change the authentication status.

More information on accessing or changing parent data from a child component can be found in my previous article titled, Access and Change Parent Variables from a Child Component with Vue.js.

The last thing to accomplish is our routing definitions.

Open the project’s src/router/index.js file and include the following:

import Vue from 'vue'
import VueRouter from 'vue-router'
import LoginComponent from "../views/login.vue"
import SecureComponent from "../views/secure.vue"

Vue.use(VueRouter)

export default new VueRouter({
    routes: [
        {
            path: '/',
            redirect: {
                name: "login"
            }
        },
        {
            path: "/login",
            name: "login",
            component: LoginComponent
        },
        {
            path: "/secure",
            name: "secure",
            component: SecureComponent
        }
    ]
})

We’ve imported each of our two components and set the root path to redirect to our login screen. We’ve linked our components and given them a path as well as a name.

More information on routing in a Vue.js application can be found in a previous article I wrote titled, Use a Router To Navigate Between Pages in a Vue.js Application.

Conclusion

You just saw how to create simple login logic for a Vue.js web application. Using the vue-router, simple form binding, and interaction between components, we can make all this happen.

You might be thinking that this example is incomplete because we’re not validating against real data from a database over HTTP. Technically the only thing you’d change is adding an HTTP call. Everything would pretty much remain the same.

Nic Raboy

Nic Raboy

Nic Raboy is an advocate of modern web and mobile development technologies. He has experience in C#, JavaScript, Golang and a variety of frameworks such as Angular, NativeScript, and Unity. Nic writes about his development experiences related to making web and mobile development easier to understand.