Issue with nuxt/auth - vue.js

For auth I do use nuxt-auth, when the login is successful, I want to redirect to the main page using this.$router.push('/'), then I get a response like blank page with the following message
2021
,
// for login
<template>
<div class="limiter">
<div
class="container-login100"
:style="{
backgroundImage: 'url(' + require(`#/assets/login/images/bg-01.jpg`) + ')',
}"
>
<div class="wrap-login100 p-l-110 p-r-110 p-t-62 p-b-33">
<form class="login100-form validate-form flex-sb flex-w">
<span class="login100-form-title p-b-53"> Login Admin </span>
<a href="facebook.com" class="btn-face m-b-20">
<i class="fa fa-facebook-official"></i>
Facebook
</a>
<a href="google.com" class="btn-google m-b-20">
<img :src="require(`#/assets/login/images/icons/icon-google.png`)" alt="GOOGLE" />
Google
</a>
<div class="p-t-31 p-b-9">
<span class="txt1"> Email </span>
</div>
<div class="wrap-input100 validate-input" data-validate="Email is required">
<input v-model="auth.email" class="input100" type="email" name="email" />
<span class="focus-input100"></span>
</div>
<div class="p-t-13 p-b-9">
<span class="txt1"> Password </span>
Forgot?
</div>
<div class="wrap-input100 validate-input" data-validate="Password is required">
<input v-model="auth.password" class="input100" type="password" name="pass" />
<span class="focus-input100"></span>
</div>
<div class="container-login100-form-btn m-t-17">
Login
</div>
<div class="w-full text-center p-t-55">
<span class="txt2"> Not a member? </span>
Register now
</div>
</form>
</div>
</div>
</div>
</template>
<script>
export default {
auth: false,
data() {
return {
auth: {
email: null,
password: null,
},
}
},
mounted() {
if (this.$auth.loggedIn) {
this.$router.push('/')
}
},
methods: {
async submit() {
try {
const response = await this.$auth.loginWith('local', { data: this.auth })
this.$router.push('/')
} catch (err) {
console.log(err)
}
},
},
}
</script>
store vuex index.js
export const getters = {
isAuthenticated(state) {
return state.auth.loggedIn
},
loggedInUser(state) {
return state.auth.user
}}
}
layout default.vue
<template>
<div class="wrapper">
<Sidebar v-if="isAuthenticated" />
<div :class="isAuthenticated ? 'main-panel' : ''">
<Navbar v-if="isAuthenticated" />
<Nuxt />
<Footer v-if="isAuthenticated" />
</div>
</div>
</template>
<script>
import Sidebar from '#/components/layout/Sidebar.vue'
import Navbar from '#/components/layout/Navbar.vue'
import Footer from '#/components/layout/Footer.vue'
import { mapGetters } from 'vuex'
export default {
components: { Sidebar, Navbar, Footer },
computed: {
...mapGetters(['isAuthenticated', 'loggedInUser']),
},
}
</script>
// auth nuxt config
auth : {
strategies: {
local: {
token: {
property: 'token',
required: true,
type: 'Bearer'
},
user: {
property: 'user',
autoFetch: true
},
endpoints: {
login: { url: '/sign/login', method: 'post' },
logout: { url: '/sign/logout', method: 'post' },
user: { url: '/sign/user-login', method: 'get' }
}
}
}
}
base index ('/')
<template>
<div class="container">
<div>
<Logo />
<h1 class="title">Learn Nuxt</h1>
<div class="links">
<a href="https://nuxtjs.org/" target="_blank" rel="noopener noreferrer" class="button--green">
Documentation
</a>
<a
href="https://github.com/nuxt/nuxt.js"
target="_blank"
rel="noopener noreferrer"
class="button--grey"
>
GitHub
</a>
</div>
</div>
</div>
</template>
<script>
import { mapGetters } from 'vuex'
export default {
computed: {
...mapGetters(['isAuthenticated', 'loggedInUser']),
},
}
</script>

In your vuex store, the state parameter in your getter only has access to local state. You can't access the auth state the way you tried.
In a vuex module, a getter gets 4 arguments, namely local state, local getters, root state and root getters. So if you would rewrite your getters like this it would probably work:
export const getters = {
isAuthenticated(state, getters, rootState) {
return rootState.auth.loggedIn
},
loggedInUser(state, getters, rootState) {
return rootState.auth.user
}}
}
But I still think it is a bit redundant doing it like that. I would replace isAuthenticated with this.$auth.loggedIn in your default layout. The nuxt-auth module globally injects the $auth instance, meaning that you can access it anywhere using this.$auth.

I had same problem after authorizing user and redirect user to the home page.
After many tries and doing many works, the right config of auth in nuxt.config.js seemed like this:
auth: {
strategies: {
local: {
scheme: 'refresh',
token: {
property: 'access_token',
tokenType: false,
maxAge: 60 * 60
},
refreshToken: {
property: 'refresh_token',
data: '',
maxAge: 60 * 60
},
endpoints: {
login: {
url: 'url/of/token',
method: 'urlMethod'
},
refresh: {
url: 'url/of/refreshToken',
method: 'urlMethod'
},
logout: false,
user: false
}
}
},
cookie: false,
redirect: {
login: '/login/page',
logout: '/login/page',
callback: false,
home: '/home/page'
}
},
Note that I didn't have any refreshToken property, but I should set it as empty string in config to be able to work with nuxt/auth!
Hope I could help

Related

Correctly testing vee-validate validated form submit with Jest

I am trying to submit a form that uses vee-validate and test if the form calls the underlying store with Jest.
Here is my code:
Form:
<template>
<div class="flex flex-col justify-center h-screen bg-site-100">
<!-- Login body -->
<div class="container">
<div class="mx-auto w-4/12 p-7 bg-white">
<!-- Form -->
<Form id="loginForm" #submit="login" :validation-schema="schema" v-slot="{ errors }">
<div class="mt-4">
<div>
<text-box
:type="'email'"
:id="'email'"
:label="'Your Email'"
v-model="email"
:place-holder="'Email'"
:required="true"
:error="errors.email"
/>
</div>
<div>
<text-box
:type="'password'"
:id="'password'"
:label="'Parool'"
v-model="password"
:place-holder="'Password'"
:required="true"
:error="errors.password"
/>
</div>
<!-- Submit -->
<Button
type="submit"
id="loginButton"
:disabled="Object.keys(errors).length > 0"
class="text-white bg-site-600 w-full hover:bg-site-700 focus:ring-4 focus:ring-site-300 font-medium rounded-md text-sm px-5 py-2.5 mr-2 mb-2 focus:outline-none"
>
Log In
</Button>
</div>
</Form>
</div>
</div>
</div>
</template>
<script lang="ts">
import * as Yup from "yup";
import { Form } from "vee-validate";
import { defineComponent } from "vue";
import Button from "../core/Button.vue";
import TextBox from "../core/TextBox.vue";
import { mapActions, mapStores } from "pinia";
import { useAuthStore } from "../../store/auth";
import LoginDataType from "../../types/login_data";
export default defineComponent({
name: "Login",
components: { TextBox, Form, Button },
computed: { ...mapStores(useAuthStore) },
data() {
return {
email: "",
password: "",
schema: Yup.object().shape({
email: Yup.string().required("Email is required").email("Email is invalid"),
password: Yup.string().required("Password is required"),
}),
};
},
methods: {
async login() {
console.log("Logged in mock");
let data: LoginDataType = {
email: this.email,
password: this.password,
};
await this.authStore.login(data);
},
},
});
</script>
Store:
import { defineStore } from "pinia";
export const useAuthStore = defineStore("auth", {
state: () => ({
}),
getters: {
},
actions: {
async login(data: LoginDataType) {
// do something
},
}
})
Test:
it('logs in correctly when right username and password sent to API', async () => {
const store = useAuthStore();
jest.spyOn(store, 'login');
const wrapper = mount(Login, {
stubs: ['router-link']
});
const email = wrapper.find('input[id="email"]');
await email.setValue('testEmail#gmail.com');
// Check if model is set
expect(wrapper.vm.email).toBe(testEmail);
const password = wrapper.find('input[id="password"');
await password.setValue('testPw');
// Check if model is set
expect(wrapper.vm.password).toBe(testPw);
// Check form exists
const loginForm = wrapper.find('#loginForm');
expect(loginForm.exists()).toBe(true);
await loginForm.trigger('submit');
// Check if store method has been called
expect(store.login).toHaveBeenCalled();
expect(store.login).toHaveBeenCalledWith({
email: 'testEmail#gmail.com',
password: 'testPw'
})
});
The test fails at expect(store.login).toHaveBeenCalled(). Implying the form doesn't get submitted. The test works just fine when I replace the vee-validate component Form with a regular HTML form tag.
What might be causing this behaviour any help is highly appreciated? :)

why beforeRouteUpdate is not updating route

I am showing the products page use vueJS and API.
I am showing categories on the left side and products on the right side
but while clicking on category products should be shown and the same component should be a load but the same component is not loading
Please check why beforeRouteUpdate is not working
below is my code
<template>
<div style="padding: 25px">
<div class="container">
<div class="row">
<div class="col-md-3">
<div class="row">
<h3>Categories</h3>
<div class="col-md-12" align="left">
<router-link type="button" align="left" class="card-link" :to="'/'">
All Products</router-link>
</div>
<div class="col-md-12" v-for="category in categories" :key="category.id">
<CategoryCard :category="category" />
</div>
</div>
</div>
<div class="col-md-9">
<h1 class="index-msg">
{{ msg }}
</h1>
<div v-if="isShowId">
<span>props id:{{id}}</span>
<br />
<span>route.params id: {{uid}}</span>
</div>
<div class="row">
<div class="col-md-4" v-for="product in products" :key="product.id">
<ProductCard :product="product" />
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import {
mapActions,
mapGetters
} from "vuex";
import CategoryCard from "../../components/products/CategoryCard";
import ProductCard from "../../components/products/ProductCard";
export default {
name: "Products",
props: ["id"],
computed: {
...mapGetters("product", ["products"]),
...mapGetters("category", ["categories"]),
},
components: {
ProductCard,
CategoryCard,
},
data() {
return {
msg: "Products Page",
uid: "",
isShowId: false,
};
},
methods: {
...mapActions("product", ["getProducts", "addCart", "removeCart"]),
...mapActions("category", ["getCategories"]),
},
mounted() {
let id = this.$route.params.id;
if (id) {
this.isShowId = true;
this.uid = id;
} else {
this.isShowId = false;
this.uid = "";
}
this.getProducts(id);
this.getCategories();
},
beforeRouteUpdate (to, from, next) {
next();
},
beforeRouteLeave (to, from, next) {
console.log(`Leave to =`, to);
console.log(`Leave from =`, from);
next();
},
};
</script>
Try to check what's the 'pathPre' variable here. This will help you finding where to route now.
beforeRouteUpdate: function (to, from, next) {
let pathPre = from.path.split("/");
console.log(pathPre)
next();
},
And if you want to just reload the route on the same url then use
this.$router.go();
Edited
Load with variable as slug
Try to use slug with route
{
path: '/routeA/:slug',
name: 'routeName',
component: routeA,
props:true,
}
and push to url with slug and route name
this.$router.push({
name: "routeName",
params: { slug: passSlugVariable },
});
and catch this prop in data as variable
data: function () {
return {
slug: this.$route.params.slug,
};
}
watch: {
'$route'(to, from) {
let to_path = to.path;
let from_path = from.path;
console.log(`to =`, to_path);
console.log(`from =`, from_path);
if (to_path !== from_path) {
this.$router.push(to_path)
this.$router.go();
} else {
this.$router.go();
}
},
},
This is working as I am refreshing the page every time using this.$router.go(); , I know this is wrong method , please tell me correct solution

problems with Vuex data rendering

I'm studying reactivity of vuex using nuxt and module mode of store. The problem is, that despite all data in store is changed by actions => mutations successfully, they do not appear on the page, and shows only empty new element of store array. here are my files:
store>contacts>index.js:
let initialData = [
{
id: 1,
name: 'Michael',
email: 'michael.s#mail.com',
message: 'message from Michael'
},
{
id: 2,
name: 'Mark',
email: 'mark.sh#email.com',
message: 'message from Mark'
},
{
id: 3,
name: 'Valery',
email: 'valery.sh#mail.com',
message: 'message from Valery'
}
]
const state = () =>{
return {
contacts: []
}
}
const getters = {
allContacts (state) {
return state.contacts
}
}
const actions = {
async initializeData({ commit }) {
commit('setData', initialData)
},
addNewContact({ commit, state }, newContact) {
commit('addContact', newContact)
}
}
const mutations = {
setData: (state, contacts) => (state.contacts = contacts),
addContact: (state, newContact) => state.contacts.push(newContact)
}
export default { state, getters, mutations, actions}
component itself:
<template>
<div class="contact-form">
<div class="links">
<nuxt-link to="/">home</nuxt-link>
<nuxt-link to="/contact-form">contact form</nuxt-link>
</div>
<h1>leave your contacts and message here:</h1>
<div class="input-wrapper">
<form class="feedback-form" action="">
<div class="name">
<label for="recipient-name" class="col-form-label">Ваше имя:</label>
<input type="text" id="recipient-name" v-model="obj.userName" name="name" class="form-control" placeholder="Представьтесь, пожалуйста">
</div>
<div class="form-group">
<label for="recipient-mail" class="col-form-label">Ваш email:</label>
<input type="email" v-model="obj.userEmail" name="email" id="recipient-mail" class="form-control" placeholder="example#mail.ru">
</div>
<div class="form-group">
<label for="message-text" class="col-form-label">Сообщение:</label>
<textarea name="message" v-model="obj.userMessage" id="message-text" class="form-control"></textarea>
</div>
<button #click.prevent="addToStore()" type="submit">submit</button>
</form>
</div>
<h3>list of contacts</h3>
<div class="contacts-list">
<div class="list-element" v-for="contact in allContacts" :key="contact.id">
id: {{contact.id}} <br> name: {{contact.name}}<br/> email: {{contact.email}}<br/> message: {{contact.message}}
</div>
</div>
</div>
</template>
<script>
import { mapMutations, mapGetters, mapActions } from 'vuex'
export default {
data() {
return {
obj: {
userName: '',
userEmail: '',
userMessage: ''
}
}
},
mounted() {
console.log(this.showGetters)
},
created() {
this.initializeData()
},
methods: {
...mapActions({
initializeData: 'contacts/initializeData',
addNewContact: 'contacts/addNewContact'
}),
addToStore() {
this.addNewContact(this.obj)
},
},
computed: {
...mapGetters({
allContacts: 'contacts/allContacts',
}),
showGetters () {
return this.allContacts
}
},
}
</script>
so, could anybody help to understand, what is wrong?
You've got mismatched field names.
Inside obj you've called them userName, userEmail and userMessage. For all the other contacts you've called them name, email and message.
You can use different names if you want but somewhere you're going to have to map one onto the other so that they're all the same within the array.
You should be able to confirm this via the Vue Devtools. The first 3 contacts will have different fields from the newly added contact.

How to set meta og:image after axios request (VueJs)?

I have a vue component which immediately after download sends axios request.
Also i use vue-social-sharing and i need og:image meta, but my image returns after axios respond.
<template>
<div class="container">
<div v-if="collageSrc" class="col-md-12 collage">
<img class="img-responsive" :src="this.collageSrc"/>
<social-sharing>
<div>
<network network="facebook">
<i name="fa fa-facebook"></i> Facebook
</network>
<network network="twitter">
<i class="fa fa-twitter"></i> Twitter
</network>
</div>
</social-sharing>
</div>
<div class="col-md-12 collage" v-else>
<h1>Your image is preparing...</h1>
</div>
</div>
</template>
<script>
export default {
name: 'collage',
props:['id'],
data: function(){
return {
collageWaiter: this.getCollageSrc(),
collageSrc: '',
url : '',
}
},
metaInfo: {
title : 'Compliment Genarator Collage',
meta: [
{charset : 'utf-8'},
{
'vmid' : 'og:image',
'property' : 'og:image',
//'content' : this.collageSrc
}
]
},
methods:{
getCollageSrc(){
const self = this;
axios.get('/api/generate?id='+this.id)
.then(function(result){
self.collageSrc = result.data;
self.url = "http://compgen.ru/collage/" + self.id;
});
}
}
}
</script>
as you can see, prop 'collageSrc' becomes available after axios request. How can i dynamic include this prop to meta og:image ?
Define metaInfo as function, not as object:
metaInfo() {
return {
meta: [
{ vmid: 'og:image', property: 'og:image', content: this.collageSrc }
]
}
}
https://vue-meta.nuxtjs.org/faq/component-props.html

Returning promise from api and putting into global data

Sorry if this is obvious but new to vue and my js not so great.....
I'm trying to get some data from an api and then put that data into a global object so it can be passed down to some child components.
I can't seem to $set the data into the global projects object, it either returns undefined or returns a promise.
parent --
<template>
<div id="app">
<section class="intro">
<h1>WELCOME</h1>
</section>
<transition name="fade"> <router-view class="view" :computedProjects=computedProject ></router-view></transition>
</div>
</template>
<script>
import projectsList from './components/projectsList'
var client = contentful.createClient({
// This is the space ID. A space is like a project folder in Contentful terms
space: '',
// This is the access token for this space. Normally you get both ID and the token in the Contentful web app
accessToken: '',
})
var PRODUCT_CONTENT_TYPE_ID = 'project'
export default {
name: 'app',
components: {
projectsList
},
data: function() {
return {
users:'',
projects:'',
}
},
methods: {},
created : function () {
client.getEntries().then((response) => {
console.log(response)
this.projects = response
console.log(this.projects)
return response
});
},
computed: {
computedProject: function() {
if (!this.projects) return null;
return this.projects;
}
}
}
}
child ---
<template>
<section class="project-list">
<swiper :options="swiperOption">
<swiper-slide v-for="project in projects" v-bind:ref="'project' + project.name" :key="project.name" data-swiper-parallax="-100">
<div class="project-cover wrapper">
{{project.name}}
<router-link :to="{ name: 'project', params: { id: project.name }}">
<div class="cover-img" :style="{ 'background-image': 'url(../static/img/projects/'+project.name+'/'+'project-details-header.jpg)' }"> </div>
<div class="project-copy"></div>
<div href="#" class="tilter tilter--4">
<figure class="tilter__figure">
<img class="tilter__image" :src="'+project.fields.coverImage.fields.file.url+'" alt="" />
<div class="tilter__deco tilter__deco--shine"></div>
<div class="tilter__deco tilter__deco--overlay" v-bind:style="{ backgroundImage: project.gradient }"></div>
<figcaption class="tilter__caption">
<h1 class="projectName tilter__title title">{{project.name}}</h1>
<h2 class="tilter__description">{{project.tagline}}</h2>
</figcaption>
<svg class="tilter__deco tilter__deco--lines" viewBox="0 0 300 415">
<path d="M20.5,20.5h260v375h-260V20.5z" />
</svg>
</figure>
</div>
</router-link>
</div>
</swiper-slide>
<div class="swiper-pagination" slot="pagination"></div>
</swiper>
</section>
</template>
<script>
export default {
data: function () {
return {
swiperOption: {
autoplay:false,
setWrapperSize :true,
observeParents:true,
keyboardControl : true,
centeredSlides :true,
freeMode:false,
freeModeSticky:true,
pagination: '.swiper-pagination',
paginationType: 'progress',
watchSlidesProgress : true,
watchSlidesVisibility : true,
speed:650,
grabCursor:true,
parallax:true
},
}
},
props :['computedProject'],
created : function () {
console.log(this.projects)
},
any help will be appreciated.
You can just assign the vue data variable in the callback of getEntries like following:
created : function () {
var self = this
client.getEntries().then((response) => {
console.log(response)
self.projects = response
})
console.log(this.projects)
},