Vuejs nuxtjs Store update not getting reflected in the middleware - javascript

I am new to VueJs and I tried to create a small application using nuxtJS where I have a login page and a home page. I am using middleware authjs to check if the user is authenticated to view the home page.
The vuex store contains a flag isLoggedIn to store the user's logged-in state. The issue I'm facing here is:
When I update the store from the login page(using commit function) and redirect to the home page, the store update is not reflected in the middleware which is called before the home page and I'm going back to the login page.
/pages/login.vue
<template>
<div>
<default-header />
<login-form :onSubmit="onSubmit" />
</div>
</template>
export default {
methods: {
async onSubmit(){
//fetching some data, skipped here for simplicity
this.$store.commit('marklogin')
this.$router.push('home')
}
}
}
/store/index.js
export const state = () => ({
loggedin: false
})
export const mutations = {
marklogin(state) {
state.loggedin = true
}
}
/middleware/auth.js
export default function ({ store, redirect }) {
if (!store.state.loggedin) {
return redirect('/login');
}
}
This is /pages/home.vue
<template>
<div>
<default-header />
<videos :videos="videos.data"/>
</div>
</template>
<script>
export default {
data() {
return {
videos: []
};
},
async fetch() {
this.videos = await fetch(
'http://localhost:8080/videos'
).then(res => res.json());
},
middleware: 'auth'
}
</script>
/components/Login.vue
<template>
<div class="container-form">
<h2>User Login</h2>
<b-form>
<b-form-group
id="input-group-1"
label-for="input-1"
>
<b-form-input
id="input-1"
v-model="form.username"
type="text"
required
:state="usernameValidation"
aria-describedby="input-live-help input-live-feedback"
placeholder="Enter your user name"
></b-form-input>
<b-form-invalid-feedback id="input-live-feedback">
Enter at least 3 letters
</b-form-invalid-feedback>
</b-form-group>
<b-form-checkbox
id="checkbox-1"
v-model="status"
name="checkbox-1"
value="accepted"
class="remember-me-checkbox"
>
Remember me
</b-form-checkbox>
</b-form>
<button type="button" #click="() => onSubmit(form.username)" class="login-button" variant="success">Login</button>
</div>
</template>
In the authjs, the value of store.state.loggedin is coming as false. Can someone help me understand why this is happening?

Related

how to use express.router to access data in url? (vue)

I have a file "app.vue" that implements a date picker for user to choose date:
//app.vue
<div id="q-app">
<div class="q-pa-md" style="max-width: 300px">
<q-input filled v-model="date" mask="date" :rules="['date']">
<template v-slot:append>
<q-icon name="event" class="cursor-pointer">
<q-popup-proxy>
<q-date v-model="date" ></q-date>
</q-popup-proxy>
</q-icon>
</template>
</q-input>
</div>
</div>
import { ref } from 'vue'
export default {
setup () {
return {
date: ref('2019/02/01')
}
}
methods: {
updateDate(){
this.$router.push({
name: this.$router.name,
query: this.date,
}
</script>
I tested "app.vue" successfully updates user selected date to the url.
I have another file "data.js" which is the backend, I want to get the user picked date, to do so, I try to access it like the following:
//data.js
const router = express.Router();
router.get("/", async function (req, res) {
...
console.log("user input date", req.query);
}
But the req.query is always returning empty.
Does anyone know why this is happening and how I can access the date data from "data.js"?

I want to handle components asynchronously in Nuxt.js and display alert messages

What I want to come true
I want to display an alert message considering the result of the data sent to the server.
However, since alert messages are managed by another component, it is necessary to call the component asynchronously.
The official Vue.js documentation used Vue.component, but what's the right way to do it with Nuxt.js?
Code
I want to use search.vue in success.vue
search.vue
<template>
<v-app>
<div
class="teal lighten-1 background pa-10"
>
<!-- <div
v-if="responseBook === 200"
> -->
<alert-success />
<v-sheet
width="1100px"
class="mx-auto pa-5 rounded-xl"
color="grey lighten-5"
min-height="500px"
>
<!-- 書籍検索、表示 -->
<BookPostDialog />
<!-- 選択されたデータの表示 -->
<BookPostSelected />
</v-sheet>
</div>
</v-app>
</template>
<script>
export default {
computed: {
responseBook () {
return this.$store.state.book.responseBook.status
}
}
}
</script>
<style lang="scss" scoped>
.background {
background-image: url('~/assets/images/tree.png');
background-repeat: space repeat;
}
</style>
Alert/success.vue
<template>
<v-alert type="success">
Succeeded
</v-alert>
</template>
If you want to use that kind of feature, you'll be better suited looking for something like this component: https://buefy.org/documentation/toast
Or anything like this in the jungle of CSS frameworks, pretty sure each of them have one.
Or implement it yourself, for this, you need to rely on portals.
For Vue2, this is how to do achieve it: https://portal-vue.linusb.org/guide/getting-started.html#enabling-disabling-the-portal
<portal to="destination" :disabled="true">
<p>
Your content
</p>
</portal>
If you want to show success.vue component after the connection to server (getting or posting data), you can use v-if as follows:
search.vue
<template>
<div>
<p>search compo</p>
<div v-if="this.$store.state.book.responseBook == 'ok'">
data was received.
<success />
</div>
</div>
</template>
<script>
export default {
mounted() {
this.$store.dispatch('getData')
}
}
</script>
success.vue
<template>
<div>
succeess compo
</div>
</template>
And then in your store/index.js file:
import Vuex from "vuex";
const createStore = () => {
return new Vuex.Store({
state: {
book: {
responseBook: ""
}
},
mutations: {
bookMutate(state, data) {
state.book.responseBook = data;
}
},
actions: {
getData(vuexContext) {
let vue = this;
// your request is here
setTimeout(function() {
vue.$axios.$get("https://pokeapi.co/api/v2/pokemon/ditto").then((result) => {
console.log(result);
vuexContext.commit("bookMutate", "ok");
}).catch(err => {
console.log(err);
})
}, 10000)
},
}
});
};
export default createStore;
I intentionally used setTimeout() in my action to see that the success component is loaded after the data was received. in actual situation it is better to use this action:
getData(vuexContext) {
this.$axios.$get("https://pokeapi.co/api/v2/pokemon/ditto").then((result) => {
console.log(result);
vuexContext.commit("bookMutate", "ok");
}).catch(err => {
console.log(err);
})
},
I used axios for calling the api but you can use your own method of getting data. but after that you must commit the mutation to change the state.

Vue Router Universal Navbar Search

new to Vue and frontend dev in general.
I'm trying to make a universal nav bar in Vue Router using bootstrap vue with a search bar implemented.
However, because I have my nav bar placed in App.vue, it is unable to pass the search function to specific routes.
Here is what I have in my App.vue
<div id="app" >
<b-navbar toggleable="lg" variant="light" type="light" class="justify-content-between">
<b-navbar-brand><router-link to="/">Brand</router-link></b-navbar-brand>
<b-collapse class="nav-collapse" is-nav>
<b-nav-item><router-link to="/about">Route 1</router-link></b-nav-item>
</b-collapse>
<b-navbar-nav class="ml-auto">
<b-nav-form>
<b-form-input class="form-control mr-sm-2" v-model="search_term" placeholder="Search..."></b-form-input>
<b-button variant="outline-success my-2 my-sm-2" type="submit" v-on:click="getSearch(search_term)">Search</b-button>
</b-nav-form>
</b-navbar-nav>
</b-navbar>
<router-view/>
</div>
</template>
<script>
</script>
<style>
</style>
The router functions are in its own class
As you can see the search stuff I have implemented under b-nav-form is from when the navbar was in a specific page where the functions are implemented.
However, with a navbar in individual pages, it has to be re-rendered every time the user re-routes. So I put it in the App.vue page, where it is permanently rendered.
How can I pass the search_term to its specific function in its specific page while the navbar being universal? Is that possible? Or is it easier to just keep the navbar in its own page.
The best approach to share a navbar accross your app and using its data through any route component is to keep it at the App.vue level as you thought.
The tricky part is then to access the search in the route components, but that pretty easy using a Vuex store.
The Vuex store is a centralised source of truth for your app. Any component, as deeply burried in your app structure as imaginable, can access it through this.$store or other API (Documentation)
All you have to do is store your search value in it and let all your component access it as needed.
Minimal example below:
const mainComponent = Vue.component("mainComponent", {
computed: {
search() {
return this.$store.state.search
}
},
template: "<div><div>Main component</div><div>Search: {{ search }}</div><div><router-link to='/other'>Go to Other</router-link></div></div>"
});
const otherComponent = Vue.component("otherComponent", {
computed: {
search() {
return this.$store.state.search
}
},
template: "<div><div>Other component</div><div>Search: {{ search }}</div><div><router-link to='/'>Go to Main</router-link></div></div>"
});
const store = new Vuex.Store({
state: {
search: ''
},
mutations: {
search(state, payload) {
state.search = payload;
}
}
});
const router = new VueRouter({
routes: [{
path: "*",
redirect: "/"
},
{
path: "/",
component: mainComponent
},
{
path: "/other",
component: otherComponent
}
]
});
new Vue({
el: "#app",
store,
router,
data: {
searchValue: ''
},
methods: {
submitSearch() {
this.$store.commit("search", this.searchValue);
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vuex/3.5.1/vuex.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue-router/3.4.8/vue-router.min.js"></script>
<div id="app">
<div class="search-bar">
<form #submit.prevent="submitSearch">
<input type="text" v-model="searchValue" placeholder="search" />
<input type="submit" value="search" />
</form>
</div>
<router-view />
</div>

beforeRouteEnter doesn't work in vue child component

I try to detect what is the previous route in my component. I use beforeRouteEnter to find it. it works in CreditAdd.vue but when I use beforeRouteEnter in Back.vue it doesn't work!
I think it because the Back component is a child. but I can't solve the problem
Back.vue:
<template>
<i class="bi ba-arrow-right align-middle ml-3" style="cursor: pointer" #click="handleBack"></i>
</template>
<script>
export default {
name: 'Back',
data() {
return {
id: null
};
},
beforeRouteEnter(to, from, next) {
console.log('please log');
next(vm => {
vm.fromRoute = from;
});
},
mounted() {},
methods: {
handleBack() {
if (!this.fromRoute.name) {
this.$router.push({ name: 'dashboard' });
} else {
this.$router.back();
}
}
}
};
</script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
CreditAdd.vue:
<template>
<Layout>
<template slot="header">
<Back />
<span>افزایش اعتبار</span>
</template>
<div class="col-lg-10 px-0">
<Card>
<template v-if="type == 'select'">
<credit-add-way #select="changeType"></credit-add-way>
</template>
<template v-if="type == 'bank'">
<credit-add-bank-way #back="changeType('select')"></credit-add-bank-way>
</template>
<template v-if="type == 'code'">
<credit-add-code-way #back="changeType('select')"></credit-add-code-way>
</template>
</Card>
</div>
</Layout>
</template>
beforeRouteEnter and other navigation guards should only works on the Vue file that is defined the link in the router, thats why Back.vue not working.
You can use plain javascript to get the previous URL
in Back.vue
mounted() {
console.log(document.referrer);
}
Another way is you can store the previous route in Vuex store,
In AddCredit.vue where navigation guards work
beforeRouteEnter(to, from, next) {
// store this in vuex
},
then in Back.vue can just retrieve right away from the store

vue.js post list not updating after form submission

In my vue app I have two components one which is a form that posts the form data to my api. And the other gets and displays these posts in a section on the page. My issue is that when I submit a new post the posts lists aren't updated. The data stays the same unless I refresh the page. How can I get my posts list to update when I submit the form?
My Code:
client/src/App.vue
<template>
<div id="app">
<MainHeader :modalVisability="modal" v-on:showModal="toggleModal" />
<div id="content_wrap">
<Summary />
</div>
<OppForm :modalVisability="modal" />
</div>
</template>
<script>
import MainHeader from './components/MainHeader.vue';
import OppForm from './components/oppForm.vue';
import Summary from './components/Summary.vue';
export default {
name: 'App',
components: {
MainHeader,
Summary,
OppForm
},
data () {
return {
modal: false
}
},
methods: {
toggleModal (modalBool) {
this.modal = modalBool;
}
}
}
</script>
client/src/components/oppForm.vue
<template>
<div id="opp_form_modal" >
<form #submit.prevent="SubmitOpp" v-if="modalVisability">
<input type="text" name="company_name" v-model="company_name">
<button type="submit">Submit</button>
</form>
</div>
</template>
<script>
import axios from 'axios';
export default {
name: 'oppForm',
props: {
modalVisability: Boolean,
},
data () {
return {
company_name: ''
}
},
methods: {
SubmitOpp () {
axios.post('http://localhost:5000/', {
company_name: this.company_name,
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
}
}
}
</script>
client/src/components/Summary.vue
<template>
<div id="summary_section">
<h2>Summary</h2>
<div id="summary_board">
<div class="column">
<div class="head">
<h3>Opportunities</h3>
</div>
<div class="body">
<div class="post"
v-for="(post, index) in posts"
:key="index"
>
<p class="company">{{ post.company_name }}</p>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import axios from 'axios';
export default {
data() {
return{
posts: []
};
},
created() {
axios.get('http://localhost:5000/')
.then(res => {
// console.log(res);
const data = res.data;
this.posts = data;
})
.catch(error => console.log(error));
}
}
</script>
The problem is that you're actually fetching your posts only on the app creation (i.e. inside the created() method).
You should wrap your axios call inside a function updatePosts() and then call it whenever you add a new post successfully, or you could create a custom event that is triggered whenever a new post is added.
created() is called only once (see vue lifecycle) so you fetch API before submitting form.
Try to add some console.log to understand what is called when.
You could use an global event bus and send form value as event data to summary. I could imagine also a solution where event is used to "tell" summary that form was submitted (just boolean, not data itself). In summary you then call API each time you receive event.
Or simple add an "update" button to summary to manually call API.
See Communication between sibling components in VueJs 2.0
or global vue instance for events for detailed examples.

Categories