I'm trying to make my button a route that shows a modal, but I want to stay on the same page - vue.js

When I'm on the Home page, I want to click on the Login button in the Header and when I click, the Login modal is displayed, which is a separate component, but I want to stay on the Home page and not be redirected to another page.
Login component:
<script setup>
import { ref, onMounted } from 'vue'
import VueCookies from 'vue-cookies'
import axios from 'axios';
const LogInModalVisible = ref(false)
var baseURL = 'http://localhost:3000/'
var userURL = baseURL + "users";
let users = ref(null)
let email = ref(null)
let password = ref(null)
let emailError = ref(null)
let passwordError =ref(null)
let passwordValidation = ref(false)
onMounted(async () => {
try {
const res = await axios.get(baseURL + 'users');
users = res.data;
/* console.log(users) */
/* loginSubmit() */
} catch (e) {
console.error(e)
}
})
async function loginSubmit()
{
if(email.lenght == 0)
{
this.emailError = "Field is empty"
}
else
{
this.emailError = null
}
if(password.length == 0)
{
this.passwordError = "Field is empty!";
}
else if(password.length>0 && password.length<8)
{
this.passwordError = "Password is too short!"
}
else
{
this.passwordError = null
}
/* const res = await axios.get(userURL);
this.users = res.data; */
for (var i = 0; i <users.length; i++)
{
if (this.users[i].email == this.email.value)
{
if (this.users[i].password == this.password.value)
{
this.passwordValidation = true;
VueCookies.set('username', this.users[i].username, "120min");
VueCookies.set('email', this.email.value, "120min");
VueCookies.set('password', this.password.value, "120min");
VueCookies.set('id', this.users[i].id, "120min");
window.location.href = '/';
alert("Login successful");
}
}
}
if(this.passwordValidation == false){ this.passwordError = "Inccorect password or username!"}
}
</script>
<template>
<el-dialog v-model="LogInModalVisible" title="LogIn" width="50%" height="50%" center>
<el-form label-position='top' status-icon :label-width="80">
<el-form-item label="Email">
<el-input type="email" id='email' placeholder="Enter Email" v-model="email" />
<div class="input-message" v-if="emailError"><h6>{{emailError}}</h6></div>
</el-form-item>
<el-form-item label="Password">
<el-input type="password" id='password' placeholder="Enter Password" v-model="password" />
<div class="input-message" v-if="passwordError"><h6>{{passwordError}}</h6></div>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button #click="LogInModalVisible = false">Cancel</el-button>
<el-button type="primary" #click="LogInModalVisible = false; loginSubmit()">
Confirm
</el-button>
</span>
</template>
</el-dialog>
</template>
<style>
</style>
Header component:
<script setup>
import {Sunny, Moon} from '#element-plus/icons-vue'
import { ref, onMounted } from 'vue'
import { RouterLink } from 'vue-router'
import Home from '../views/Home.vue'
import { pokeStore } from '../store/store'
import VueCookies from 'vue-cookies'
import axios from 'axios';
import Pokedex from './Pokedex.vue'
const pokedexVisible = ref(false)
const PokemonStore = pokeStore();
let allpokemons = []
async function GetAllPokemons() {
try {
let response = await PokemonStore.getPokemonData();
allpokemons.value = response.data.results;
let randomPokemon = allpokemons.value[Math.floor(Math.random() * 151) + 1]
console.log(randomPokemon)
} catch (error) {
throw error;
}
}
GetAllPokemons()
</script>
<template>
<el-header class="navbar">
<div class="navbar-content">
<div>
<router-link to="/" custom v-slot="{ navigate }">
<img #click="navigate" role="link" class="logo" src="/src/assets/images/logo.png" />
</router-link>
</div>
<el-space size="large">
<div>
<input class="search" type="text" placeholder="Search pokemon" />
</div>
<div>
<el-button link><el-icon :size="20">
<Sunny />
</el-icon></el-button>
<el-button link><el-icon :size="20">
<Moon />
</el-icon></el-button>
</div>
<div>
<el-button #click="pokedexVisible = true" class="pokedexBtn">Pokedex</el-button>
<!-- <router-link #click="pokedexVisible = true" to="/pokedex" class="nav-link">Pokedex</router-link> -->
</div>
<div>
<el-button #click="LogInModalVisible = true" text>LogIn</el-button>
<router-link to="/login" custom v-slot="{ navigate }">
<button #click="navigate" role="link">
Login
</button>
</router-link>
</div>
</el-space>
</div>
</el-header>
</template>
<style scoped>
.navbar {
background-color: whitesmoke;
padding: 5px 30px;
position: fixed;
top: 0;
left: 0;
right: 0;
opacity: 0.9;
}
.navbar-content{
align-items: center;
justify-content: space-between;
display: flex;
}
.logo{
width: 100%;
max-width: 50px;
display: block;
}
.search{
display: block;
padding: 5px;
border-radius: 4px;
font-size: 14px;
width: 100%;
background-color: transparent;
float: right;
}
.search::placeholder {
opacity: 0.5;
}
.navbar-right{
align-items: center;
justify-content: space-between;
}
.pokedexBtn{
background-color: black;
color: whitesmoke;
}
</style>
Index.js component:
import { createRouter, createWebHistory } from 'vue-router'
import Login from '../components/Login.vue'
import Home from '../views/Home.vue'
import Pokedex from '../components/Pokedex.vue'
import App from '/src/App.vue'
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
{
path: '/',
name: 'Home',
component: Home
},
{
path: '/pokedex',
name: 'Pokedex',
component: Pokedex
},
{
path: '/login',
name: 'Login',
component: Login
},
]
})
export default router
When I'm on the Home page, I want to click on the Login button in the Header and when I click, the Login modal is displayed, which is a separate component, but I want to stay on the Home page and not be redirected to another page.

I haven't tested this but it would be something like this.
<script setup>
import {Sunny, Moon} from '#element-plus/icons-vue'
import { ref, onMounted } from 'vue'
import { RouterLink } from 'vue-router'
import Home from '../views/Home.vue'
import { pokeStore } from '../store/store'
import VueCookies from 'vue-cookies'
import axios from 'axios';
import Pokedex from './Pokedex.vue'
const pokedexVisible = ref(false)
const PokemonStore = pokeStore();
let allpokemons = []
async function GetAllPokemons() {
try {
let response = await PokemonStore.getPokemonData();
allpokemons.value = response.data.results;
let randomPokemon = allpokemons.value[Math.floor(Math.random() * 151) + 1]
console.log(randomPokemon)
} catch (error) {
throw error;
}
}
GetAllPokemons()
import { LoginModal } from 'somewhere/LoginModal.vue';
const showLoginModal = ref(false);
</script>
<template>
<el-header class="navbar">
<div class="navbar-content">
<div>
<router-link to="/" custom v-slot="{ navigate }">
<img #click="navigate" role="link" class="logo" src="/src/assets/images/logo.png" />
</router-link>
</div>
<el-space size="large">
<div>
<input class="search" type="text" placeholder="Search pokemon" />
</div>
<div>
<el-button link><el-icon :size="20">
<Sunny />
</el-icon></el-button>
<el-button link><el-icon :size="20">
<Moon />
</el-icon></el-button>
</div>
<div>
<el-button #click="pokedexVisible = true" class="pokedexBtn">Pokedex</el-button>
<!-- <router-link #click="pokedexVisible = true" to="/pokedex" class="nav-link">Pokedex</router-link> -->
</div>
<div>
<el-button #click.prevent="showLoginModal = true" text>LogIn</el-button>
</div>
</el-space>
</div>
</el-header>
<LoginModal v-if="showLoginModal">
</template>

Related

Vue 3 Composition API...how to replace getElementById

I wrote the following in Vue 3 Composition API.
If you look on the "onMounted" I'm attaching an event listener to the window to keep the status box to the bottom of the screen. I'm using absolute position and bottom 0.
I'm wondering if I can make this more "Vue" by replacing the getElementById? I tried refs but it's not working.
Any suggestions or should I leave well enough alone?
Thanks
<template>
<div>
<transition name="toast">
<div id="slide" class="slide-modal" v-if="statuses.length">
<div class="clear-notifications" #click='clearAllNotifications()'>Clear all notifications</div>
<div v-for="status in statuses" :key="status.id" class="status-div">
<div>{{ status.text }}</div>
<div #click="closeStatus(status)"><span class="-x10-cross"></span></div>
</div>
</div>
</transition>
</div>
</template>
<script lang="ts">
import { defineComponent, ref, computed, watch, onMounted } from "vue";
import { useStore } from "../model/DataStore";
export default defineComponent({
setup() {
const store = useStore();
const statuses = ref([]);
const statusMessage = computed(() => store.state.statusMessage);
function addStatus(newMessage) {
statuses.value.push({
id: statuses.value.length + 1,
text: newMessage
})
}
watch(statusMessage, (newValue: string, oldValue: string) => {
addStatus(statusMessage.value);
})
onMounted(() => {
window.addEventListener("scroll", function (e) {
let vertical_position = 0;
vertical_position = pageYOffset;
if(document.getElementById("slide")){
document.getElementById('slide').style.bottom = -(vertical_position) + 'px';
}
});
})
return {
store,
statuses,
addStatus
};
},
methods: {
clearAllNotifications() {
this.statuses = []
},
closeStatus(elm: any) {
const index = this.statuses.map((status) => status.id).indexOf(elm.id);
this.statuses.splice(index, 1);
}
}
})
</script>
and here's the slide modal style:
.slide-modal {
max-height: 200px;
width: 500px;
background-color: #f2f2f2;
color: #505050;
padding: 8px;
display: flex;
flex-direction: column;
gap: 8px;
overflow-x: hidden;
position: absolute;
bottom: 0;
}
The docs give a pretty simple example
<template>
<div ref="root">This is a root element</div>
</template>
<script>
import { ref, onMounted } from 'vue'
export default {
setup() {
const root = ref(null)
onMounted(() => {
// the DOM element will be assigned to the ref after initial render
console.log(root.value) // <div>This is a root element</div>
})
return {
root
}
}
}
</script>
use ref() when creating
pass to template in return function
use ref="..." in template
and in onMounted, access via ref.value
so for your code it would be...
<template>
<div>
<transition name="toast">
<div ref="slideRef" class="slide-modal" v-if="statuses.length">
<div class="clear-notifications" #click='clearAllNotifications()'>Clear all notifications</div>
<div v-for="status in statuses" :key="status.id" class="status-div">
<div>{{ status.text }}</div>
<div #click="closeStatus(status)"><span class="-x10-cross"></span></div>
</div>
</div>
</transition>
</div>
</template>
<script lang="ts">
import { defineComponent, ref, computed, watch, onMounted } from "vue";
import { useStore } from "../model/DataStore";
export default defineComponent({
setup() {
const store = useStore();
const statuses = ref([]);
const slideRef = ref();
const statusMessage = computed(() => store.state.statusMessage);
function addStatus(newMessage) {
statuses.value.push({
id: statuses.value.length + 1,
text: newMessage
})
}
watch(statusMessage, (newValue: string, oldValue: string) => {
addStatus(statusMessage.value);
})
onMounted(() => {
window.addEventListener("scroll", function (e) {
let vertical_position = 0;
vertical_position = pageYOffset;
if(slideRef.value){
slideRef.value.style.bottom = -(vertical_position) + 'px';
}
});
})
return {
store,
statuses,
addStatus,
slideRef
};
},
})
</script>

How to simply navigate from One Component (Home Page ) to Another Component by clicking button using Router concept in Vue.js

I know basics of html, css and js. I have just started learning Vue.js. There is a Home Page in my Vue JS Application which has two buttons. On Click of that button, navigation should happen. (New Component to be loaded). But, in the current code, on button click, navigation is not happening. Please Assist. Copying few file as seen below.
App.vue
<template>
<h3> Home </h3>
<button #click="goToCreate()"> Create Package </button>
<br><br>
<button #click="goToEdit()"> Update Package </button>
</template>
<script>
export default {
name: 'App',
components: {
},
methods:{
goToCreate(){
this.$router.push('/createpackage');
},
goToEdit(){
this.$router.push('/updatepackage');
}
}
}
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
main.js
import { createApp } from 'vue'
import { createRouter, createWebHistory } from 'vue-router'
import App from './App.vue'
import CreatePackage from './components/CreatePackage.vue'
import SearchAndUpdatePackage from './components/SearchAndUpdatePackage.vue'
const router = createRouter({
history: createWebHistory(),
routes:[
{
path : '/createpackage',
component:CreatePackage
},{
path : '/updatepackage',
component:SearchAndUpdatePackage
}
]
})
const app= createApp(App);
app.use(router).mount('#app')
config.js
let baseUrl = ''
if (process.env.NODE_ENV === 'production') {
baseUrl = 'http://yourdomain.com/api/'
}
else {
baseUrl = 'http://localhost:9000/'
}
export const apiHost = baseUrl
CreatePackage.vue
<template>
<div>
<form name="createPackageForm" #submit="submitNewPackage" method="post">
<input type="number" name="noOfPostpaid" placeholder="PostPaid" v-model="posts.noOfPostpaid">
<br> <br> <br>
<input type="number" name="noOfPrepaid" placeholder="PrePaid" v-model="posts.noOfPrepaid">
<br> <br> <br>
<button>Submit</button>
</form>
</div>
</template>
<script>
import { apiHost } from '../config'
import axios from 'axios'
export default {
name:"CreatePackage",
data(){
return{
posts: {
noOfPostpaid:null,
noOfPrepaid:null
}
}
},
methods:{
submitNewPackage(e){
console.warn(apiHost+'tdg/createpackage/'+this.posts.noOfPostpaid+'/'+this.posts.noOfPrepaid);
e.preventDefault();
axios.post(apiHost+'tdg/createpackage/'+this.posts.noOfPostpaid+'/'+this.posts.noOfPrepaid,{
headers: {
"Access-Control-Allow-Origin": "*"
}},null).then(
response => {
console.log(response.data)}
).catch(e => {
console.log(e);
})
this.posts.noOfPostpaid='';
this.posts.noOfPrepaid='';
}
}
}
</script>
SearchAndUpdatePackage.vue
<template>
<div>
<input type="search" name="accountUUID" placeholder="Account UUID" v-model="posts.accountUUID">
<br> <br> <br>
<button #click="searchAccountUUID">Search </button>
<br> <br> <br>
<textarea id="myTextArea" cols=100 rows=20 v-model="posts.responseJSON"></textarea>
</div>
</template>
<script>
import { apiHost } from '../config'
export default {
name:"SearchAndUpdatePackage",
data(){
return{
posts: {
accountUUID:null,
responseJSON:null
},
}
},
methods:{
searchAccountUUID(e){
const url=apiHost+'tdg/carbon/'+this.posts.accountUUID;
console.log(url);
e.preventDefault();
fetch(url).then(response => response.json())
.then(data=>this.posts.responseJSON=JSON.stringify(data,null,4))
.catch(e => {
console.log(e);
})
console.log(this.posts.responseJSON);
}
}
}
</script>
With Vue-Router, you need to use the router-view component. When you navigate to a URL defined in your routes config, Vue-Router will match that URL and display the associated component.
It's common to place it in App.vue:
<template>
<h3> Home </h3>
<button #click="goToCreate()"> Create Package </button>
<br><br>
<button #click="goToEdit()"> Update Package </button>
<router-view />
</template>
<script>
export default {
name: 'App',
methods: {
goToCreate() {
this.$router.push('/createpackage');
},
goToEdit() {
this.$router.push('/updatepackage');
}
}
}
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
You might have an issue with it being a direct descendent of <template>, but I'm not sure.

Nuxt - Using router.push inside a component not changing pages correctly

index.vue -
<template>
<div>
<Header />
<div class="container">
<SearchForm />
</div>
</div>
</template>
<script>
const Cookie = process.client ? require('js-cookie') : undefined
export default {
data() {
return {
form: {
email: '',
password: ''
},
show: true
}
},
methods: {
logout() {
// Code will also be required to invalidate the JWT Cookie on external API
Cookie.remove('auth')
this.$store.commit('setAuth', {
auth: null,
user_type: null
})
}
}
}
</script>
<style>
.container {
/* margin: 0 auto; */
/* min-height: 100vh; */
display: flex;
justify-content: center;
align-items: center;
text-align: center;
}
</style>
jobs.vue -
<template>
<div>
<Header />
<SearchForm />
<b-container class="main_container">
<b-row>
<h1> Results for "{{q}}"</h1>
</b-row>
<b-row>
<ul id="array-rendering">
<li v-for="item in results" :key="item.job_id">
{{ item.job_title }}
{{ item.job_city }}
{{ item.job_state }}
{{ item.job_work_remote }}
</li>
</ul>
</b-row>
</b-container>
</div>
</template>
<script>
const Cookie = process.client ? require('js-cookie') : undefined
export default {
// middleware: 'notAuthenticated',
watchQuery: ['q'],
data() {
return {
q: null,
results: []
}
},
async fetch() {
this.q = this.$route.query.q
this.results = await this.$axios.$get('/api/job/search', {
params: {
keyword: this.q,
}
})
},
methods: {
}
}
</script>
<style>
.container {
align-items: center;
text-align: center;
}
</style>
SearchForm.vue component -
<template>
<div id='searchFormDiv'>
<b-form inline #submit.prevent="onSubmit">
<label class="sr-only" for="inline-form-input-name"> keyword</label>
<b-form-input v-model="form.keyword" id="inline-form-input-name" class="mb-2 mr-sm-2 mb-sm-0" placeholder="Job title or keyword" size="lg"></b-form-input>
<label class="sr-only" for="inline-form-input-username">location</label>
<b-input-group class="mb-2 mr-sm-2 mb-sm-0">
<b-form-input v-model="form.location" id="inline-form-input-username" size="lg" placeholder="City, state or zip"></b-form-input>
</b-input-group>
<b-button type="submit" variant="primary">Find Jobs</b-button>
</b-form>
</div>
</template>
<script>
import {
BIconSearch,
BIconGeoAlt
} from 'bootstrap-vue'
export default {
data() {
return {
form: {
keyword: '',
location: ''
}
}
},
created () {
this.form.keyword = this.$route.query.q
},
methods: {
onSubmit() {
this.$router.push({
path: 'jobs',
query: {
q: this.form.keyword
}
});
}
},
components: {
BIconSearch,
BIconGeoAlt
},
}
</script>
<style>
#searchFormDiv {
margin-top: 50px
}
</style>
The route for "http://localhost:3000/" returns the index.vue page.
In this vue page, I have a component with a search form. Once you complete these form and hit the seach button, it will re-direct to a results page
if this.form.keyword = "Data", the next URL will be "http://localhost:3000/jobs?q=Data" and it will be using the jobs.vue page.
The issue I'm running into is the CSS is not being loaded from the jobs.vue page. It's still coming from the index.vue page for some reason. If I refresh the page, then the CSS from jobs.vue is loading. I need the CSS to load from jobs.vue on the initial redirect. All of the query data is working as expected so thats a plus.
However, the following CSS is being applied from index.vue for some reason instead of the CSS from the jobs.vue page -
display: flex;
justify-content: center;
Does anyone know whats going on here? This app is SSR and not SPA.
You have to scope your css from the index.vue page to the other pages with the scoped directive (see docs https://vue-loader.vuejs.org/guide/scoped-css.html)
<style scoped>
/* local styles */
</style>
<style>
/* global styles */
</style>
You can add your global CSS in your layouts/default.vue file.
This solved the issue -
methods: {
onSubmit() {
window.location = 'http://localhost:3000/jobs?q=' + this.form.keyword;
}
},

Vuex State data is not persisting?

I'm working on admin panel in vuejs and using vuex for state management.
store/module/home/home.js:
import instance from "../../../services/Http";
const state = {
usersCount: 0,
customersCount: 0,
chefsCount: 0,
driversCount: 0,
Users: [],
};
const getters = {
Userscount: state => state.usersCount,
Customerscount: state => state.customersCount,
Chefscount: state => state.chefsCount,
Driverscount: state => state.driversCount,
users: state => state.Users,
};
const actions = {
getStats({commit})
{
instance.get('admin/stats').then(res => commit('setStats', res.data));
},
getUsers({commit})
{
instance.get('admin/users').then(res => commit('setUsers', res.data));
}
};
const mutations = {
setStats:(state, data) => {
state.usersCount = data.usersCount;
state.customersCount = data.customersCount;
state.chefsCount = data.chefsCount;
state.driversCount = data.driversCount;
},
setUsers:(state, data) => { state.Users = data.users}
};
export default {
state,
getters,
actions,
mutations
}
and then i'm calling getStats and getUsers actions in two different components in created method of respective components.
The issue is that getStats and setStats is executed but it does not set the data, but getUsers and setUsers is working as expected.
src/components/layouts/Widgets.vue:
<template>
<!-- Widgets -->
<div class="row">
<div class="col-lg-3 col-md-6">
<div class="card">
<div class="card-body">
<div class="stat-widget-five">
<div class="stat-icon dib flat-color-1">
<!-- <i class="pe-7s-cash"></i>-->
<i class="pe-7s-users"></i>
</div>
<div class="stat-content">
<div class="text-left dib">
<div class="stat-text"><span class="count">{{ Userscount }}</span></div>
<div class="stat-heading">Total Users</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="col-lg-3 col-md-6">
<div class="card">
<div class="card-body">
<div class="stat-widget-five">
<div class="stat-icon dib flat-color-2">
<!-- <i class="pe-7s-cart"></i>-->
<i class="pe-7s-users"></i>
</div>
<div class="stat-content">
<div class="text-left dib">
<div class="stat-text"><span class="count">{{ Chefscount }}</span></div>
<div class="stat-heading">Total Chefs</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="col-lg-3 col-md-6">
<div class="card">
<div class="card-body">
<div class="stat-widget-five">
<div class="stat-icon dib flat-color-8">
<!-- <i class="pe-7s-browser"></i>-->
<i class="pe-7s-users"></i>
</div>
<div class="stat-content">
<div class="text-left dib">
<div class="stat-text"><span class="count">{{ Customerscount }}</span></div>
<div class="stat-heading">Total Customers</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="col-lg-3 col-md-6">
<div class="card">
<div class="card-body">
<div class="stat-widget-five">
<div class="stat-icon dib flat-color-4">
<i class="pe-7s-users"></i>
</div>
<div class="stat-content">
<div class="text-left dib">
<div class="stat-text"><span class="count">{{ Driverscount }}</span></div>
<div class="stat-heading">Total Drivers</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- /Widgets -->
</template>
<script>
import {mapActions,mapGetters} from 'vuex';
export default {
name: "Widgets",
created() {
this.getStats();
},
computed: mapGetters(['Userscount','Customerscount','Chefscount','Driverscount']),
methods:{
...mapActions(['getStats'])
},
}
</script>
i'have also attached images showing of vue js dev tools for vuex that data is stored in state but it is not being displayed.
edit-1:
setStats-after console.log
{usersCount: 12, customersCount: 4, chefsCount: 7, driversCount: 0, postsCount: 22}chefsCount: 7customersCount: 4driversCount: 0postsCount: 22usersCount: 12__proto__: Object
Edit-2:
vuex binding widgets components
Edit-3:
Store/index.js:
import Vue from 'vue'
import Vuex from 'vuex'
import auth from "./modules/auth/auth";
import home from "./modules/home/home";
Vue.use(Vuex);
export default new Vuex.Store({
state: {
},
mutations: {
},
actions: {
},
modules: {
auth,
home
}
});
sr/main.js
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
Vue.config.productionTip = false;
router.beforeEach((to, from, next) => {
if (to.matched.some(record => record.meta.requiresAuth)) {
// this route requires auth, check if logged in
// if not, redirect to login page.
if (!store.getters.isLoggedIn)
{
next({
name: 'login',
})
}
else
{
next();
}
}
else if(to.matched.some(record => record.meta.Visitor)) {
if (store.getters.isLoggedIn)
{
next({
name: 'home',
})
}
else
{
next();
}
}
else
{
next()
}
});
new Vue({
store,
router,
render: h => h(App)
}).$mount('#app');
src/components/layout/Main.vue:
<template>
<div class="Main">
<SideBar/>
<div id="right-panel" class="right-panel">
<Header/>
<!-- Content -->
<div class="content">
<div class="animated fadeIn">
<Widgets/>
<div class="clearfix"></div>
<Users/>
<div class="clearfix"></div>
<Footer/>
</div>
</div>
</template>
<script>
import SideBar from "./SideBar";
import Header from "./Header";
import Footer from "./Footer";
import Widgets from "./Widgets";
import Users from "../users/Users";
export default {
name: "Main",
components: {Users, Widgets, Footer, Header, SideBar}
}
</script>
<style scoped>
#weatherWidget .currentDesc {
color: #ffffff!important;
}
.traffic-chart {
min-height: 335px;
}
#flotPie1 {
height: 150px;
}
#flotPie1 td {
padding:3px;
}
#flotPie1 table {
top: 20px!important;
right: -10px!important;
}
.chart-container {
display: table;
min-width: 270px ;
text-align: left;
padding-top: 10px;
padding-bottom: 10px;
}
#flotLine5 {
height: 105px;
}
#flotBarChart {
height: 150px;
}
#cellPaiChart{
height: 160px;
}
</style>
Any help will be appreciated.
Thanks.
you forgot to add "..." before the 'mapGetters', "...mapGetters"
I think your problem would be solved by implementing the strict-mode in Vuex, by the way, it's turned off by default.
take a look here: https://vuex.vuejs.org/guide/strict.html
Have you tried using mapState instead of mapGetters? As your values are just being updated, but your getters aren't computed values, they just map to your state.
Instead of:
<script>
import {mapActions,mapGetters} from 'vuex';
export default {
name: "Widgets",
created() {
this.getStats();
},
computed: mapGetters(['Userscount','Customerscount','Chefscount','Driverscount']),
methods:{
...mapActions(['getStats'])
},
}
</script>
Maybe try:
<script>
import {mapActions,mapGetters} from 'vuex';
export default {
name: "Widgets",
created() {
this.getStats();
},
computed: mapState({
Userscount: state => state.home.Userscount,
Customerscount: state => state.home.Customerscount,
Chefscount: state => state.home.Chefscount,
Driverscount: state => state.home.Drivers.count
}),
methods:{
...mapActions(['getStats'])
},
}
</script>

Error message when trying to pass data from component to page in Nuxt

I've created a component in Nuxt to get data from a Firestore database and would like to show that data in a page I created.
When I embed the component in a page I keep getting the error:
Property or method "provinces" is not defined on the instance but referenced during render. Make sure that this property is reactive, either in the data option, or for class-based components, by initializing the property.
Now, when I copy the code from the component in the page it works just fine, so I assume the problem is with passing the data between the component and the page.
Full code of the component in components/index.vue :
<template>
<section class="container">
<div class="index">
<div v-for="province in provinces" :key="province.id">
<div class="div_title">
<h2>{{ province.name_nl }}</h2>
</div>
</div>
</div>
</section>
</template>
<script>
// import { VueperSlides, VueperSlide } from 'vueperslides'
// import 'vueperslides/dist/vueperslides.css'
import firebase from 'firebase'
// import fireDb from '#/plugins/firebase.js'
export default {
name: 'Index',
components: {
// VueperSlides,
// VueperSlide
},
data: function() {
return {}
},
async asyncData() {
const moment = require('moment')
const date = moment(new Date()).format('YYYY-MM-DD')
const housesArray = []
const provincesArray = []
await firebase
.firestore()
.collection('provinces')
.orderBy('name_nl')
.get()
.then(querySnapshot => {
querySnapshot.forEach(doc => {
provincesArray.push(doc.data())
})
})
await firebase
.firestore()
.collection('houses')
.where('valid_until', '>', date)
.get()
.then(querySnapshot => {
querySnapshot.forEach(doc => {
housesArray.push(doc.data())
})
})
return {
provinces: provincesArray,
houses: housesArray
}
}
}
</script>
<style scoped>
div {
text-align: center;
}
h2 {
margin-top: 5vh;
margin-left: auto;
margin-right: auto;
width: 95vh;
}
h3 {
margin: 40px 0 0;
}
ul {
list-style-type: none;
padding: 0;
}
p {
text-align: left;
}
li {
min-width: 100% !important;
margin-left: 0px;
text-align: left;
}
</style>
Page where I insert the component in pages/index.vue:
<template>
<v-layout column justify-center align-center>
<v-flex xs12 sm8 md6>
<div class="text-xs-center">
<logo />
<tabs />
<index />
</div>
</v-flex>
</v-layout>
</template>
<script>
import Logo from '~/components/Logo.vue'
import Tabs from '~/components/Tabs.vue'
import firebase from 'firebase'
import Index from '~/components/Index.vue'
export default {
components: {
Logo,
Tabs,
Index
},
data: function() {
return {}
}
}
</script>
I would expect the page to display the data that I retrieved when I import the component into the page but I keep getting the same error.
Should I be using the Nuxt store to transfer data between a component and a page or am I doing something else wrong?
The lifecycle hook asyncData is not know within Vue components. It's only known in Nuxt pages.
It's better to do the data request within your pages component and pass it as a property to your component:
pages/index.vue
<template>
<index :houses="houses" />
</template>
<script>
const delay = time => new Promise(resolve => setTimeout(resolve, time));
export default {
async asyncData() {
// fake data
await delay(500);
return {
houses: [...]
}
}
}
</script>
components/index.vue
<template>
<pre>{{ houses }}</pre>
</template>
<script>
export default {
props: {
houses: {
required: true,
type: Array
}
}
}
</script>