vue-cli
with the webpack
template. Referring to the Vue tutorial, you can find out that vue-cli
are used to install vue-cli
and prepare the working environment:
# vue-cli $ npm install --global vue-cli # "webpack" $ vue init webpack my-project # $ cd my-project $ npm install $ npm run dev
application
folder in its root directory. You can also skip this step by creating a folder while working with vue-cli
. If you decide not to create a folder, then you need to give the project a name by running a command like this:
vue init webpack name-of-your-project
application
folder:
vue-cli
is not yet installed, run the following command:
npm i --g vue-cli
vue-cli
globally, so it does not matter which folder we will be in by executing it.
vue init webpack
application
folder that has already been created to host the application.
application
folder, install the dependencies and run the project.
npm i npm run dev
application/src/assets
folder and delete logo.png
, since we will not use this file. Next, open the file of the root component App.vue
from the application/src
folder and bring it to the form represented by the following code fragment:
<template> <div id="app"> <v-container> <router-view/> </v-container> </div> </template> <script> export default { name: 'app' } </script>
index.js
file in the router
folder and bring it to this form:
import Vue from 'vue' import Router from 'vue-router' Vue.use(Router) export default new Router({ routes: [ {} ] })
HelloWorld.vue
file from the components
folder.
application
folder:
npm i --save axios vuetify vue-cookie
npm i --save-dev sass-loader node-sass
axios
to handle HTTP requests. In vuetify
we are interested in visual components and the ability to use a grid-based layout. We will use the vue-cookie
library to work with cookies. The sass-loader
and node-sass
allow us to use SCSS.
components
folder and create the pages
folder in it, in which we will create the Authentication
folder. In this folder, you need to create an Authentication.vue
file representing the component that will be used for authentication. This is what should end up:
Authentication.vue
file we place the following code:
<template> <h1>Auth!</h1> </template> <script> export default {} </script>
router
folder and work with the routes.
Authentication
component and configure a route to use it:
import Vue from 'vue' import Router from 'vue-router' // Pages import Authentication from '@/components/pages/Authentication/Authentication' Vue.use(Router) export default new Router({ routes: [ { path: '/login', name: 'Authentication', component: Authentication } ] })
main.js
file from the src
folder and import the vuetify
and vue-cookie
:
import VueCookie from 'vue-cookie' import Vuetify from 'vuetify' import('../node_modules/vuetify/dist/vuetify.min.css') Vue.use(VueCookie) Vue.use(Vuetify) Vue.config.productionTip = false
App.vue
to the App.vue
component from the src
folder and look at the styles. First you need to prepare the style
tag. Post it immediately after closing the script
tag:
<style lang="scss"> </style>
src/assets
folder and create the styles.scss
file and the partials
folder in it. In this folder, create two partial templates, represented by the files _variables.scss
and _animations.scss
. The result should be the following structure:
_variables.scss
file _variables.scss
we will set the following parameters:
// Colors $background-tint: #1734C1; $background-color: rgba(0, 0, 0, .5);
_animations.css
file _animations.css
add the bounceIn
and slideInFromLeft
animation descriptions:
@keyframes bounceIn { to { animation-timing-function: cubic-bezier(.215, .61, .355, 1) } 0% { opacity: 0; transform: scale3d(.3, .3, .3) } 20% { transform: scale3d(1.1, 1.1, 1.1) } 40% { transform: scale3d(.9, .9, .9) } 60% { opacity: 1; transform: scale3d(1.03, 1.03, 1.03) } 80% { transform: scale3d(.97, .97, .97) } to { opacity: 1; transform: scaleX(1) } } @keyframes slideInFromLeft { from { transform: translateX(-2500px); opacity: 0 } 50% { transform: translateX(0); opacity: 1; } 70% { transform: translateX(-20px); } 90% { transform: translateX(10px); } to { transform: translateX(0); } }
styles.scss
:
@import "./partials/variables"; @import "./partials/animations";
assets
folder, create an images
folder. Here you can put any image that will be used as a background. Here , in the repository, you can find the image that is used in this material.
App.vue
styling block in the App.vue
file to the following view:
<style lang="scss"> @import "./assets/styles"; body { background: url('./assets/images/background.jpg') no-repeat center center fixed; &:after { content: ''; position: fixed; width: 100%; height: 100%; top: 0; left: 0; background-color: $background-tint; opacity: .3; z-index: -1; } } </style>
body :after
pseudo-element, we set the background-color
parameter by writing the value of the $background-tint
variable to it. This will apply a magenta color filter to the background image. Here are the different versions of the background image.
index.html
file, which is located in the application
folder, and add the following to the head
tag:
<meta name="viewport" content="initial-scale=1"> <link href='https://fonts.googleapis.com/css?family=Roboto:300,400,500,700|Material+Icons' rel="stylesheet">
Authentication
component. Create an index.js
file in the Authentication
folder.
import Axios from 'axios' import router from '@/router' const BudgetManagerAPI = `http://${window.location.hostname}:3001`
Authentication
object that will contain the methods we need:
export default { user: { authenticated: false } }
user
, which stores information about whether the user is authenticated.
import Axios from 'axios' import router from '@/router' const BudgetManagerAPI = `http://${window.location.hostname}:3001` export default { user: { authenticated: false }, authenticate (context, credentials, redirect) { Axios.post(`${BudgetManagerAPI}/api/v1/auth`, credentials) .then(({data: {token}}) => { context.$cookie.set('token', token, '1D') context.validLogin = true this.user.authenticated = true if (redirect) router.push(redirect) }).catch(({response: {data}}) => { context.snackbar = true context.message = data.message }) }, signup (context, credentials, redirect) { Axios.post(`${BudgetManagerAPI}/api/v1/signup`, credentials) .then(({data: {token}}) => { context.$cookie.set('token', token, '1D') context.validSignUp = true this.user.authenticated = true if (redirect) router.push(redirect) }).catch(({response: {data}}) => { context.snackbar = true context.message = data.message }) }, checkAuthentication () { const token = document.cookie if (token) this.user.authenticated = true else this.user.authenticated = false }, getAuthenticationHeader (context) { return `Bearer ${context.$cookie.get('token')}` } }
context
: this is a component of Vue.
credentials
: there will be a username ( username
) and password ( password
).
redirect
: here will be the path we are going to redirect the user to.
credentials
argument passed. Then we unstructure the response, data
, since here we are only interested in the value of token
, we save this value in the cookie and set the lifetime of this data to be one day. We also set the validLogin
variable and the authenticated
value of the user
object to true
, and finally, redirect the user along the path from the redirect
argument.
context
field's snackbar
field to true
and write an error message to the message.
Authorization
header.
Authentication.vue
file. Here we will use the tools Vuetify:
<template> <div class="l-auth-container"> <div class="l-auth"> <v-form v-model="validLogin"> <v-text-field label="Username" v-model="credentials.username" prepend-icon="account_box" :rules="rules" required color="light-blue lighten-1"> </v-text-field> <v-text-field label="Password" v-model="credentials.password" prepend-icon="lock" :rules="rules" :append-icon="loginPasswordVisible ? 'visibility' : 'visibility_off'" :append-icon-cb="() => (loginPasswordVisible = !loginPasswordVisible)" :type="loginPasswordVisible ? 'text' : 'password'" color="light-blue lighten-1" required> </v-text-field> <v-btn flat color="light-blue lighten-1" @click.native="signUpVisible = true">Create account</v-btn> <v-btn color="light-blue lighten-1" @click.native="submitAuthentication()">Login</v-btn> </v-form> </div> <div class="l-signup" v-if="signUpVisible"> <v-form v-model="validSignUp"> <v-text-field label="Username" v-model="newUser.username" prepend-icon="account_box" :rules="rules" required color="light-blue lighten-1"> </v-text-field> <v-text-field label="Password" v-model="newUser.password" prepend-icon="lock" :rules="rules" :append-icon="signUpPasswordVisible ? 'visibility' : 'visibility_off'" :append-icon-cb="() => (signUpPasswordVisible = !signUpPasswordVisible)" :type="signUpPasswordVisible ? 'text' : 'password'" color="light-blue lighten-1" required> </v-text-field> <v-btn block color="light-blue lighten-1" @click.native="submitSignUp()">Sign Up</v-btn> </v-form> </div> <v-snackbar timeout="6000" bottom="bottom" color="red lighten-1" v-model="snackbar"> {{ message }} </v-snackbar> </div> </template>
div
element with the class l-auth-container
, which acts as a container. Next comes another div
with the l-auth
class, which contains the structure of the elements for organizing the input form, in particular, this is the v-form
element bound to the validLogin
variable.
v-text-field
input fields that are bound to data from the credentials
(we will deal with this data below). The fields are equipped with icons taken from https://material.io/icons/ , the input validation rules are associated with them (both here and there are the same rules, we will not complicate the project), besides, both of these fields are mandatory.
loginPasswordVisible
from true
to false
and vice versa. If this variable is set to true
, then the type
parameter of the input field is set to text
, otherwise it is a password
.
signUpVisible
set to true
. The device of this form is similar to the device of the login form, only a few lines are changed here. In particular, the variable signUpPasswordVisible
is used instead of loginPasswordVisible
and another button click handling method.
v-snackbar
, which, during authentication, is used to display messages.
Authentication.vue
file, we describe the component script:
<script> import Authentication from '@/components/pages/Authentication' export default { data () { return { snackbar: false, validLogin: false, validSignUp: false, signUpVisible: false, loginPasswordVisible: false, signUpPasswordVisible: false, rules: [ (value) => !!value || 'This field is required' ], credentials: { username: '', password: '' }, newUser: { username: '', password: '' }, message: '' } }, methods: { submitAuthentication () { Authentication.authenticate(this, this.credentials, '/') }, submitSignUp () { Authentication.signup(this, this.newUser, '/') } } } </script>
index.js
file from the Authentication
folder, since we need the authenticate
method defined inside this file.
snackbar
: used for message bar.
validLogin
: used to validate the login form.
validSignUp
: used to validate the registration form.
signUpVisible
: used to display the registration form (when set to true
).
loginPasswordVisible
: indicates whether the user can see the password entered on the login form.
signUpPasswordVisible
: indicates whether the password entered in the registration form can be seen.
rules
: rules for checking data entered in form fields.
credentials
: an object bound to the input fields of the login form used to authenticate the user.
newUser
: an object newUser
with the input fields of the registration form in the system.
message
: used to display messages during authentication.
submitAuthentication
method calls the authenticate
method from the Authentication
file, passing context, credentials, and redirection paths. The submitSignUp
method submitSignUp
used to call the signup
method.
Authentication.vue
file (here you can unleash the imagination and do everything the way you want):
<style lang="scss"> @import "./../../../assets/styles"; .l-auth { background-color: $background-color; padding: 15px; margin: 45px auto; min-width: 272px; max-width: 320px; animation: bounceIn 1s forwards ease; } .l-signup { background-color: $background-color; padding: 15px; margin: 45px auto; min-width: 272px; max-width: 320px; animation: slideInFromLeft 1s forwards ease; } </style>
pages
folder and create the Home.vue
component Home.vue
:
<template> <div> <h3>Hi! this is our App's Home</h3> <ul> <li v-if="users != null" v-for="user in users"> {{ user.username }} </li> </ul> </div> </template> <script> import Axios from 'axios' import Authentication from '@/components/pages/Authentication' const BudgetManagerAPI = `http://${window.location.hostname}:3001` export default { data () { return { users: [] } }, mounted () { this.getAllUsers() }, methods: { getAllUsers (context) { Axios.get(`${BudgetManagerAPI}/api/v1/users`, { headers: { 'Authorization': Authentication.getAuthenticationHeader(this) } }).then(({data}) => (this.users = data)) } } } </script>
index.js
file from the router
folder. Here is what kind of it needs to lead:
import Vue from 'vue' import Router from 'vue-router' import * as Auth from '@/components/pages/Authentication' // Pages import Home from '@/components/pages/Home' import Authentication from '@/components/pages/Authentication/Authentication' Vue.use(Router) const router = new Router({ routes: [ { path: '/', name: 'Home', component: Home, meta: { requiredAuth: true } }, { path: '/login', name: 'Authentication', component: Authentication } ] }) router.beforeEach((to, from, next) => { if (to.meta.requiredAuth) { if (Auth.default.user.authenticated) { next() } else { router.push('/login') } } else { next() } }) export default router
import * as Auth from '@/components/pages/Authentication'
Authentication
file, calling it Auth
, since the Authentication
component has also been imported.
const router = new Router({ routes: [ { path: '/', name: 'Home', component: Home, meta: { requiredAuth: true } }, { path: '/login', name: 'Authentication', component: Authentication } ] })
Router
object in order to create a navigation system protection later. We also add the path to the Home
component. The meta.requiredAuth
parameter will be meta.requiredAuth
to true
. This means that if an unauthenticated user tries to access this component, it will be redirected to the login page.
router.beforeEach((to, from, next) => { if (to.meta.requiredAuth) { if (Auth.default.user.authenticated) { next() } else { router.push('/login') } } else { next() } })
meta.requiredAuth
parameter is meta.requiredAuth
to true
. If so, we check the user object from Authentication
. If the user is not authenticated, we redirect him to the login page.
export default router
main.js
file in the application
folder. Here we are going to import the Authentication
file and call the checkAuthentication
method:
import Vuetify from 'vuetify' import Authentication from '@/components/pages/Authentication' import('../node_modules/vuetify/dist/vuetify.min.css') Vue.use(VueCookie) Vue.use(Vuetify) Vue.config.productionTip = false Authentication.checkAuthentication()
Source: https://habr.com/ru/post/340926/