I'm going through my first Vue tutorial and am a bit stuck. I've got an App.vue file, which I can see with the browser inspector extension is loading, a router index.js file, and login/signup forms. I can see the default Hello component.
I should be able to go to /login and /signup, but the components do not load. There are no console errors. Where do I begin troubleshooting?
App.Vue:
<template>
<div id="app">
<router-view/>
</div>
</template>
<script>
export default {
name: 'app'
}
</script>
<style>
body {
background-color: #f7f7f7;
padding-top: 50px;
padding-bottom: 50px;
}
.is-danger {
color: #9f3a38;
}
</style>
Router index.js file:
import Vue from 'vue'
import Router from 'vue-router'
import HelloWorld from '#/components/HelloWorld'
import SignUpForm from '#/components/Auth/SignUpForm'
Vue.use(Router)
export default new Router({
routes: [
{
path: '/',
name: 'HelloWorld',
component: HelloWorld
},
{
path: '/signup',
component: SignUpForm
}
]
}))
SignUpForm.vue:
<template>
<div class="ui stackable three column centered grid container">
<div class="column">
<h2 class="ui dividing header">Sign Up, it's free!</h2>
<Notification
:message="notification.message"
:type="notification.type"
v-if="notification.message"
/>
<form class="ui form" #submit.prevent="signup">
<div class="field" :class="{ error: errors.has('name') }">
<label>Full Name</label>
<input type="text" name="name" v-model="name" v-validate="'required'" placeholder="Full name">
<span v-show="errors.has('name')" class="is-danger">{{ errors.first('name') }}</span>
</div>
<div class="field" :class="{ error: errors.has('username') }">
<label>Username</label>
<input type="text" name="username" :class="{'input': true, 'is-danger': errors.has('username') }" v-model="username" v-validate="'required'" placeholder="Username">
<span v-show="errors.has('username')" class="is-danger">{{ errors.first('username') }}</span>
</div>
<div class="field" :class="{ error: errors.has('email') }">
<label>Email</label>
<input type="email" name="email" :class="{'input': true, 'is-danger': errors.has('email') }" v-model="email" v-validate="'required|email'" placeholder="Email">
<span v-show="errors.has('email')" class="is-danger">{{ errors.first('email') }}</span>
</div>
<div class="field" :class="{ error: errors.has('password') }">
<label>Password</label>
<input type="password" name="password" :class="{'input': true, 'is-danger': errors.has('password') }" v-model="password" v-validate="'required'" placeholder="Password">
<span v-show="errors.has('password')" class="is-danger">{{ errors.first('password') }}</span>
</div>
<button class="fluid ui primary button" :disabled="!isFormValid">SIGN UP</button>
<div class="ui hidden divider"></div>
</form>
<div class="ui divider"></div>
<div class="ui column grid">
<div class="center aligned column">
<p>
Got an account? <router-link to="/login">Log In</router-link>
</p>
</div>
</div>
</div>
</div>
</template>
<script>
import Notification from '#/components/Notification'
export default {
name: 'SignUpForm',
components: {
Notification
},
data () {
return {
name: '',
username: '',
email: '',
password: '',
notification: {
message: '',
type: ''
}
}
},
computed: {
isFormValid () {
return Object.keys(this.fields).every(key => this.fields[key].valid)
}
},
beforeRouteEnter (to, from, next) {
const token = localStorage.getItem('tweetr-token')
return token ? next('/') : next()
},
methods: {
signup () {
axios// eslint-disable-line no-use-before-define
.post('/signup', {
name: this.name,
username: this.username,
email: this.email,
password: this.password
})
.then(response => {
// save token in localstorage
localStorage.setItem('tweetr-token', response.data.data.token)
// redirect to user home
this.$router.push('/')
})
.catch(error => {
// display error notification
this.notification = Object.assign({}, this.notification, {
message: error.response.data.message,
type: error.response.data.status
})
})
}
}
}
</script>
My main.js file:
// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import App from './App'
import router from './router'
import axios from 'axios'
import VeeValidate from 'vee-validate'
window.axios = axios
axios.defaults.baseURL = 'http://127.0.0.1:3333'
Vue.config.productionTip = false
Vue.use(VeeValidate)
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
components: { App },
template: '<App/>'
})
Your main Vue instance needs to mount itself to the #app element:
new Vue({
router
}).$mount('#app');
Here's a basic example with vue router. Codepen
Couple other things to try in main.js:
// render function
new Vue({
el: '#app',
router,
render: h => h(App)
})
// component
new Vue({
el: '#app',
router,
App
})
My mistake was router configuration. It was not allowing me to go to the proper route without a #. I needed to add mode:'history' to the new Router object.
Proper router configuration:
export default new Router({
mode: 'history',
routes: [
blahblahblah
]
})
Related
As a beginner in Vue.js,I am trying to create a Todo app, but problems seems to passing a variable to another component when looping.
i want to pass item to another embedded component.
Here what I have with all the components:
in main.js:
import Vue from "vue";
import App from "./App.vue";
import Todo from "./components/Todo";
import itemComponent from "./components/itemComponent";
Vue.config.productionTip = false;
Vue.component('todo', Todo);
Vue.component('itemcomponent', itemComponent);
new Vue({
render: h => h(App)
}).$mount("#app");
in App.vue:
<template>
<div id="app">
<todo></todo>
<img alt="Vue logo" src="./assets/logo.png" width="25%" />
<!-- <HelloWorld msg="Hello Vue in CodeSandbox!" /> -->
</div>
</template>
<script>
export default {
name: "App",
components: {
},
};
</script>
in Todo.vue:
<template>
<div>
<input type="text" v-model="nameme" />
<button type="click" #click="assignName">Submit</button>
<div v-for="(item, index) in result" :key="item.id">
<itemcomponent {{item}}></itemcomponent>
</div>
</div>
</template>
<script>
import { itemcomponent } from "./itemComponent";
export default {
props:['item'],
data() {
return {
nameme: "",
upernameme: "",
result: [],
components: {
itemcomponent,
},
};
},
methods: {
assignName: function () {
this.upernameme = this.nameme.toUpperCase();
this.result.push(this.nameme);
this.nameme = "";
},
},
};
</script>
in itemComponent.vue:
<template>
<div>
<input type="text" value={{item }}/>
</div>
</template>
<script>
export default {
props: {
item: String,
},
data() {
return {};
},
};
</script>
what is my mistake? thanks for help.
Quite a bunch of mistakes:
You should import single page components inside their parent components and register them there, not in main.js file. EDIT: Explaination to this is given in docs
Global registration often isn’t ideal. For example, if you’re using a build system like Webpack, globally registering all components means that even if you stop using a component, it could still be included in your final build. This unnecessarily increases the amount of JavaScript your users have to download.
You have a component registration in Todo.vue, but you have misplaced it inside data so its is just a data object that is not getting used, move into components.
In your loop you have item.id, but your item is just a plain string, it does not have an id property. Either change item to object with id property, or simply use the index provided in loop (not recommended).
Pass your item as a prop, not mustache template. So in Todo.vue replace {{ item }} with :item="item"
In your ItemComponent.vue you have mustache syntax once again in the attribute. Change value={{item }} to :value="item"
So here's the final code:
main.js
import Vue from "vue";
import App from "./App.vue";
Vue.config.productionTip = false;
new Vue({
render: h => h(App)
}).$mount("#app");
in App.vue:
<template>
<div id="app">
<todo></todo>
<img alt="Vue logo" src="./assets/logo.png" width="25%" />
<!-- <HelloWorld msg="Hello Vue in CodeSandbox!" /> -->
</div>
</template>
<script>
import Todo from './components/Todo.vue';
export default {
name: "App",
components: {
Todo,
},
};
</script>
in Todo.vue:
<template>
<div>
<input type="text" v-model="nameme" />
<button type="click" #click="assignName">Submit</button>
<div v-for="(item, index) in result" :key="index">
<itemcomponent :item="item"></itemcomponent>
</div>
</div>
</template>
<script>
import { itemcomponent } from "./itemComponent";
export default {
props:['item'],
data() {
return {
nameme: "",
upernameme: "",
result: [],
};
},
methods: {
assignName: function () {
this.upernameme = this.nameme.toUpperCase();
this.result.push(this.nameme);
this.nameme = "";
},
},
components: {
itemComponent,
}
};
</script>
itemComponent.vue:
<template>
<div>
<input type="text" :value="item"/>
</div>
</template>
<script>
export default {
props: {
item: String,
},
data() {
return {};
},
};
</script>
The connection in between components gets disrupted. Which shouldnt happen since I am using bootstrap-vue inbuilt router link (using to=" " instead of href="").
The app works perfectly fine when running without dist.
App.vue
<template>
<div class="container">
<app-header></app-header>
<div class="row">
<div class="col-xs-12">
<transition name="slide" mode="out-in">
<router-view></router-view>
</transition>
</div>
</div>
</div>
</template>
<script>
import Header from "./components/Header.vue";
export default {
components: {
appHeader: Header
},
created() {
this.$store.dispatch("initStocks");
}
};
</script>
Header.vue
<template>
<div>
<b-navbar toggleable="lg" type="dark" variant="info">
<b-navbar-brand to="/">Stock Broker</b-navbar-brand>
<b-navbar-toggle target="nav-collapse"></b-navbar-toggle>
<b-collapse id="nav-collapse" is-nav>
<b-navbar-nav>
<b-nav-item to="/portfolio">Portfolio</b-nav-item>
<b-nav-item to="/stocks">Stocks</b-nav-item>
<b-nav-item #click="endDay">End Day</b-nav-item>
<b-navbar-nav right>
<b-nav-item right>Funds: {{ funds }}</b-nav-item>
</b-navbar-nav>
</b-navbar-nav>
</b-collapse>
</b-navbar>
</div>
</template>
<script>
import { mapActions } from "vuex";
export default {
data() {
return {
isDropdownOpen: false
};
},
computed: {
funds() {
return this.$store.getters.funds;
}
},
methods: {
...mapActions({
randomizeStocks: "randomizeStocks",
fetchData: "loadData"
}),
endDay() {
this.randomizeStocks();
},
saveData() {
const data = {
funds: this.$store.getters.funds,
stockPortfolio: this.$store.getters.stockPortfolio,
stocks: this.$store.getters.stocks
};
this.$http.put("data.json", data);
},
loadData() {
this.fetchData();
}
}
};
</script>
vue.config.js
module.exports = {
pluginOptions: {
prerenderSpa: {
registry: undefined,
renderRoutes: ["/", "/portfolio", "/stocks"],
useRenderEvent: true,
headless: true,
onlyProduction: true
}
}
};
router/index.js
import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from '../views/Home.vue'
Vue.use(VueRouter)
const routes = [
{
path: '/',
name: 'Home',
component: Home
},
{
path: '/stocks',
name: 'Stocks',
component: () => import(/ '../views/Stocks.vue')
},
{
path: '/portfolio',
name: 'Portfolio',
component: () => import('../views/Portfolio.vue')
}
]
const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes
})
export default router
I figured it a minute after making this post haha.
The issue was that in App.vue I had my main div with a class of "container" instead of id of "app".
Corrected below:
<template>
<div id="app"> //correction here
<app-header></app-header>
<div class="row">
<div class="col-xs-12">
<transition name="slide" mode="out-in">
<router-view></router-view>
</transition>
</div>
</div>
</div>
</template>
I am attempting to refactor my code to use vuex. I am getting 2 errors: app.js:81010 [Vue warn]: Error in mounted hook: "ReferenceError: $store is not defined" and ReferenceError: $store is not defined. I think I imported vuex properly.
My goal is to update my bootstrap-vue data-table with the employee data from my database using vuex.
In the EmployeeDataTable.vue file I have a getEmployees method in methods: {} which I would like it to dispatch the fetchAllEmployees action from employees.js. fetchAllEmployees should grab all of the employees from the database and save the result to the employees.js employees: [] state.
I am now confused and need help getting in the right direction to fix this issue.
I don't know if I needed to show all of this code, but I wanted to show the flow of my components.
Entry point App.js:
import Vue from 'vue';
import store from './store';
import router from './router';
import { BootstrapVue, IconsPlugin } from 'bootstrap-vue'
import App from './components/App';
Vue.use(BootstrapVue);
Vue.use(IconsPlugin);
require('./bootstrap');
const app = new Vue({
el: '#app',
components: {
App,
},
router,
store,
});
Vuex Store index.js:
import Vue from 'vue';
import Vuex from 'vuex';
import Employees from './modules/employees';
Vue.use(Vuex);
export default new Vuex.Store({
modules: {
Employees,
}
});
Vuex module employees.js:
const state = {
employees: [],
employeesStatus: null,
};
const getters = {
allEmployees: (state) => state.employees
};
const actions = {
fetchAllEmployees({commit, state}) {
commit('setPostsStatus', 'loading');
axios.get('/api/employees')
.then(res => {
commit('employees', res.data);
commit('employeesStatus', 'success');
})
.catch(error => {
commit('setEmployeesStatus', 'error');
});
},
};
const mutations = {
setEmployees(state, employees) {
state.employees = employees;
},
setEmployeesStatus(state, status) {
state.employeesStatus = status;
}
};
export default {
state, getters, actions, mutations,
};
App.vue:
<template>
<div>
<router-view></router-view>
</div>
</template>
<script>
export default {
name: "App"
}
</script>
<style scoped>
</style>
DashBoard.vue:
<template>
<div>
<b-container>
<b-row>
<b-col class="col-12 col-sm-12 col-md-5 col-lg-4 col-xl-4">
<b-list-group class="d-flex horiz mx-5">
<b-list-group-item class="list-group-item-padding">
<b-link v-on:click="component='home'">
<i class="fas fa-home"></i>
<span class="custom-sm-d-none">Home</span>
</b-link>
</b-list-group-item>
<b-list-group-item class="list-group-item-padding">
<b-link v-on:click="component = 'projects'">
<i class="fas fa-project-diagram"></i>
<span class="custom-sm-d-none">Projects</span>
</b-link>
</b-list-group-item>
<b-list-group-item class="list-group-item-padding">
<b-link v-on:click="component = 'employees'">
<i class="fas fa-user"></i>
<span class="custom-sm-d-none">Employees</span>
</b-link>
</b-list-group-item>
<b-list-group-item class="list-group-item-padding">
<b-link v-on:click="component = 'customers'">
<i class="fas fa-users"></i>
<span class="custom-sm-d-none">Customers</span>
</b-link>
</b-list-group-item>
<b-list-group-item class="list-group-item-padding">
<b-link v-on:click="component = 'batch-create-material-list'">
<i class="fas fa-toolbox"></i>
<span class="custom-sm-d-none">Materials</span>
</b-link>
</b-list-group-item>
<b-list-group-item class="">
<b-link v-on:click="component = 'product-list'">
<i class="fas fa-clipboard-list icon-5x"></i>
<span class="custom-sm-d-none">Tasks</span>
</b-link>
</b-list-group-item>
</b-list-group>
</b-col>
<b-col class="col-12 col-md-7 col-lg-8 col-xl-8">
<keep-alive>
<component v-bind:is="component"></component>
</keep-alive>
</b-col>
</b-row>
</b-container>
</div>
</template>
<script>
import Home from '../../components/admin/Home';
import Projects from '../../components/admin/Projects';
import Employees from '../../components/admin/Employees';
import Customers from '../../components/admin/Customers'
import ProductList from '../../components/admin/ProductList';
import CreateProductAndCategory from '../../components/admin/CreateProductAndCategory';
export default {
name: 'Dashboard',
components: {
'home': Home,
'projects': Projects,
'employees': Employees,
'customers': Customers,
'product-list': ProductList,
'batch-create-material-list': CreateProductAndCategory,
},
data() {
return {
component: 'product-list',
}
},
}
</script>
<style scoped>
/* small screen below 768px width */
#media only screen and (max-width : 691px) {
.custom-sm-d-none{display:none;}
.horiz {
flex-direction: row;
justify-content: space-evenly;
padding-bottom: 10px;
}
.list-group-item-padding {
margin-right: 10px;
}
}
</style>
Component Employees.vue:
<template>
<div>
<EmployeeDataTable/>
<CreateEmployee />
</div>
</template>
<script>
import EmployeeDataTable from "./EmployeeDataTable"
import CreateEmployee from "./CreateEmployee"
export default {
components: {
EmployeeDataTable,
CreateEmployee,
},
}
</script>
<style scoped>
</style>
Component EmployeeDataTable.vue:
<template>
<div class="overflow-auto pb-3" style="background: white; ">
<b-card
header="Employees"
header-tag="header"
>
<b-pagination
v-model="currentPage"
:total-rows="rows"
:per-page="perPage"
aria-controls="my-table"
></b-pagination>
<p class="mt-3">Current Page: {{ currentPage }}</p>
<b-table
id="employee-table"
ref="employee-table"
:items="items"
:per-page="perPage"
:current-page="currentPage"
small
></b-table>
</b-card>
</div>
</template>
<script>
import { mapGetters } from 'vuex';
export default {
name: "EmployeeDataTable",
data() {
return {
perPage: 3,
currentPage: 1,
items: [],
}
},
computed: {
...mapGetters(['allEmployees']),
rows() {
return this.items.length
}
},
methods: {
getEmployees() {
$store.dispatch('fetchAllEmployees').then(() => {
console.log('Dispatched getEmployees method!');
});
}
},
mounted() {
this.getEmployees();
}
}
</script>
Use this.$store instead of $store in the component. Change your API call to:
axios.get('/api/employees')
.then(res => {
commit('setEmployees', res.data);
commit('setEmployeesStatus', 'success');
})
.catch(error => {
commit('setEmployeesStatus', 'error');
});
The difference now is that you're calling the mutation names. In your success commit, you had the state names instead of the mutations.
One common convention people use in Vuex is to name mutations in all caps, and it might help in a situation like this (by making it more obvious if you used a state name instead). You'd rename them to SET_EMPLOYEES and SET_EMPLOYEES_STATUS.
Like for example
<router-link :to="{ name : 'criminalView', params : { criminalId : this.criminal.id , criminal : this.criminal } }" tag="a" >
<img class="h-18 w-18 rounded-full mr-4 mt-2" src="{{ asset('assets/images/'.$criminal->photo) }}" id="criminalsPhoto" alt="Criminals View" >
</router-link>
how can i accept those params in my CriminalView.vue which handles the router-view component
This is my routes.js
import VueRouter from 'vue-router';
import CriminalView from './components/CriminalView.vue';
import GroupView from './components/GroupView.vue';
let routes = [
{
path : '/criminal/:criminalId',
name : 'criminalView',
component : CriminalView,
props : { criminals }
},
{
path : '/group/:groupId',
name : 'groupView',
component : GroupView,
},
]
export default new VueRouter({
routes,
linkActiveClass: 'is-active'
});
how am I gonna display this in my template such as like this
<section class="w-2/5 ml-2 font-basic" id="criminalProfile">
<p class="font-basic tracking-normal text-2xl mb-4 mt-4 font-normal text-black mr-2">Criminal Profile of {{ this.criminal.full_name }}</p>
<div class="bg-white px-8 py-8 pt-4">
<div class="text-center">
<div id="avatar" class="inline-block mb-6" >
<img :src="avatarPath" class="h-50 w-50 rounded-full border-orange border-2">
<p class="font-bold mt-2 text-blue">$15,000</p>
<p class="mt-2 text-lg font-bold" >Notable Crimes:
<p class="mt-2 text-lg font-normal" >
<em class="font-bold roman">Offenses</em>Descr..
</p>
</p>
<div class="w-full flex justify-between">
<button class="w-full bg-green-theme p-3 text-white mt-4 ml-2 hover:bg-green-second" href="/criminal/" >View Full Profile</button> </div>
</div>
</div>
</div>
</section>
This is in my script tag..
export default {
props : ['criminals'],
name: 'CriminalProfile',
data(){
return {
criminal : this.criminals,
url : window.App.apiDomain
}
},
}
How can i display the props in my router-view which is there in my CriminalView.vue
I'm not 100% sure about camelCasing for groupId so I'm using groupid. Try it out, and let us know about camelCase in the comments.
props: {
//you could set the watch to this if you need.
//groupid: {default: 1}
},
watch: {
$route(to, from) {
var groupid = (typeof to.params.groupid != 'undefined') ? to.params.groupid : 1;
//... do some stuff
}
},
In order to display data from the route params you should be doing something like this
// start route.js
export default new Router({
mode: 'hash', // https://router.vuejs.org/api/#mode
routes: [
{
path: 'my-route/:criminal',
name: 'MyRoute',
component: MyComponent
}
]
})
// End route.js
<template>
<section>
<div>
{{criminals}}
</div>
</section>
</template>
<script>
export default {
name: 'CriminalProfile',
data(){
return {
criminals: ''
}
},
created(){
this.criminals = this.$route.query.myparam
}
}
</script>
Here it's the router link
<router-link :to="{ name: 'CriminalView', params: { criminalId: 123 }}">Criminal</router-link>
In my case this work perfectly,
Try this:
Using this.$route.params.paramName you can access params in router-view component.
<script>
export default{
data() {
criminal_id:'',
criminal_data: ''
},
created() {
this.criminal_id = this.$route.params.criminalId;
this.criminal_data = this.$route.params.criminal;
}
}
</script>
I have a "vue-cli webpack" like the following :
src/components/Signin.vue:
<template>
...
<form v-on:submit.prevent="userSignIn">
...
<div class="field">
<p class="control has-icons-left has-icons-right">
<input
v-validate="'required|email'"
v-bind:class="{'is-danger': errors.has('name')}"
name="email"
v-model="form.email"
class="input"
id="email"
type="email"
placeholder="Email"
>
<span class="icon is-small is-left">
<i class="fa fa-envelope"></i>
</span>
<span class="icon is-small is-right">
<i class="fa fa-check"></i>
</span>
<span class="help is-danger" v-show="errors.has('email')">{{ errors.first('email') }}</span>
</p>
</div>
<div class="field">
<p class="control has-icons-left">
<input
v-validate="'required|min:5'"
v-bind:class="{'is-danger': errors.has('name')}"
name="password"
v-model="form.password"
class="input"
id="password"
type="password"
placeholder="Password"
>
<span class="icon is-small is-left">
<i class="fa fa-lock"></i>
</span>
<span class="help is-danger" v-show="errors.has('password')">{{ errors.first('password') }}</span>
</p>
</div>
<div class="field is-grouped">
<div class="control">
<button v-bind:disabled="errors.any()" class="button is-primary" type="submit" :disabled="loading">
Submit
</button>
</div>
</div>
</form>
...
</template>
<script>
...
export default {
data () {
return {
form: {
email: '',
password: '',
alert: false
}
}
},
computed: {
error () {
return this.$store.getters.getError
},
loading () {
return this.$store.getters.getLoading
}
},
watch: {
error (value) {
if (value) {
this.alert = true
}
},
alert (value) {
if (!value) {
this.$store.dispatch('setError', false)
}
},
methods: {
userSignIn () {
this.$store.dispatch('userSignIn', {email: this.email, password: this.password})
}
}
},
...
}
</script>
src/App.vue:
<template>
<main>
<router-view></router-view>
</main>
</template>
<style lang="sass">
#import "~bulma"
/* Your css for this file... */
</style>
src/main.js
import Vue from 'vue'
import App from './App'
import router from './router'
import firebase from 'firebase'
import { store } from './store'
import VeeValidate from 'vee-validate'
import { firebaseConfig } from './config'
Vue.use(VeeValidate)
Vue.config.productionTip = false
firebase.initializeApp(firebaseConfig)
/* eslint-disable no-new */
const unsubscribe = firebase.auth()
.onAuthStateChanged((firebaseUser) => {
new Vue({
el: '#app',
router,
store,
render: h => h(App),
created () {
store.dispatch('autoSignIn', firebaseUser)
}
})
unsubscribe()
})
and I get two errors when I click the button :
Property or method "userSignIn" is not defined on the instance but
referenced during render. Make sure to declare reactive data
properties in the data option.
Signin.vue?d58e:24 Uncaught TypeError: _vm.userSignIn is not a
function
You've defined your methods inside your watch. Move them outside.
watch: {
error (value) {
if (value) {
this.alert = true
}
},
alert (value) {
if (!value) {
this.$store.dispatch('setError', false)
}
},
},
methods: {
userSignIn () {
this.$store.dispatch('userSignIn', {email: this.email, password: this.password})
}
}