I have this structure for a Vue app:
App.vue
-> Router View(with two child components)
App.vue
<template>
<div id="app" #click="mutateStore(null)">
<router-view #storeUpdate="mutateStore"/>
</div>
</template>
<script>
export default {
name: 'app',
methods:{
mutateStore(){
this.$store.commit('increment')
}
},
mounted(){
this.$store.commit('increment')
}
}
</script>
<style>
...styles
</style>
main.js
import Vue from 'vue'
import App from './App'
import router from './router'
import Vuex from 'vuex'
Vue.use(Vuex)
const store = new Vuex.Store({
state: {
type: 1
},
mutations: {
increment (state) {
state.type++
}
}
})
new Vue({
el: '#app',
router,
store,
render: h => h(App)
})
router/index.js
import Vue from 'vue'
import Router from 'vue-router'
import Main from '#/components/Childcomponent1'
import Display from '#/components/Childcomponent2'
Vue.use(Router)
export default new Router({
routes: [
{
path: '/',
name: 'Childcomponent1',
component: Main
},
{
path: '/display',
name: 'Childcomponent2',
component: Display
}
]
})
In Child Component 1, I have a button where if clicked would do:
this.$emit("storeUpdate")
which triggers the event handler mutateStore() in App.vue
In Vue Dev Tools, it also shows the state.type with the incremented value
In Child Component 2, I display directly the state.type value as computed:
<template>
<div class="main">
{{type}}
</div>
</template>
<script>
export default {
name: 'Childcomponent2',
computed: {
type () {
return this.$store.state.type
}
}
}
</script>
<style scoped>
..Styles
</style>
But the value never got updated in Child Component 2, not even when viewed in Vue Dev Tools.
And one curious observation, when the same commit in App.vue is called in mounted(), it increments the state.type all across the two child components, but when via method mutateStore(), it does update in Child Component 1, but the change is not detected in Child Component 2.
Note that before I did the emit/event handler part in the App.vue, I already tried mutating the store directly from within the Child Component but to no effect that's why I tried the emit event handler instead.
Did I do anything incorrectly?
Please help!
Found it. Turns out I had wrongly assumed that Vuex standard has built in support for shared states across multiple browser windows using localStorage. Apparently it only does share state across multiple components in the SAME browser tab/window. To allow for multiple browser support, a plugin must be added: vuex-shared-mutations
npm install vuex-shared-mutations
Related
I am using Jest to test this Vue component:
import { mount } from '#vue/test-utils'
import ExampleComponent from '../Components/Example.vue'
describe("Test", () => {
it('shows no errors', () => {
jest.spyOn(global.console, 'error');
jest.spyOn(global.console, 'warn');
mount(ExampleComponent)
expect(console.warn).not.toHaveBeenCalled()
expect(console.error).not.toHaveBeenCalled()
})
})
I am expecting this test to Fail since I have this component:
Example.vue
<template>
<div>
{{ number }}
</div>
</template>
<script>
export default {}
</script>
as you can see number is not defined, and if opened this component using the browser it will show me:
[Vue warn]: Property or method "number" is not defined on the instance but referenced during render.
but if I test it, the test will pass. How can If the Vue component has warnings or errors?
I want to write a directive in nuxts.js and want to add it via the plugins array in the nuxt.config file, but then my directive doesn't get resolved...
//plugins/myGlobalDirective.js
import Vue from 'vue'
Vue.directive('my-global-directive', {
inserted: function(el) {
console.log('my-global-directive')
}
})
//nuxt.config.js
plugins: [
'~plugins/myGlobalDirective.js'
],
//component
<template>
<p v-my-global-directive>lala</p>
</template>
that doesn't work.
But if I import it directly in the component it works ( I can also remove it from the plugins array)
//component
<template>
<p v-my-global-directive>lala</p>
</template>
<script>
import '~/plugins/myGlobalDirective.js'
</script>
What I'm doing wrong?
Problem solved
I have to restart the server if I change something in the nuxt.config file...
And it works with or without the the '/'
I'm having an issue with Vue. At this point I've read things like this detailing this error occurring when you try to define a method on the root instance, then reference it in local scope.
My issue is slightly different because it is defined in local scope, so I'm not sure what to make of this error. I've also looked at this, and this.
Here is my App.vue:
<template>
<div id="app">
<router-link to="/Home">home</router-link>
<router-link to="/Login">login</router-link>
<router-view/>
</div>
</template>
<script>
export default {
name: 'App'
}
console.log('app.vue loading')
</script>
My main.js:
// 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 store from './store'
import router from './router'
import Home from './components/Home'
import Login from './components/Login'
Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
store,
components: { App, Home, Login },
template: '<App/>'
})
console.log('main js loading');
The component the issue is coming from:
<template>
<div class="login">
<head>
<title>{{title}}</title>
</head>
<form for="login" id="loginMain">
<label for="username">Username:</label>
<input type="text" id="username"></input>
<label for="password">Password:</label>
<input type="password"></input><br/><br/>
<button for="login" #click="processLogin">LOGIN</button>
<button for="logout" #click="processLogout">LOGOUT</button>
</form>
<p> Your login status: {{ loginStatus }} </login></p>
</div>
</template>
<script>
import Vue from 'vue'
import { mapGetters, mapActions, Vuex } from 'vuex'
import store from '#/store'
const Login = {
delimiters: [ '[{','}]'],
data () {
title: 'Foo',
msg: 'Bar'
}
name: 'Login',
props: {
// worry about this later
},
methods: {
...mapActions({
processLogin : 'LOGIN_ACTION',
processLogout : 'LOGOUT_ACTION'
})
},
computed: {
...mapGetters({
title: 'GET_TITLE',
loginStatus: 'GET_LOGIN_STATUS'
}),
}
}
console.log('Login loading');
export default Login
And although I'm unsure if it is related, but my store:
import Vue from 'vue'
import Vuex from 'vuex'
import Home from './components/Home'
import Login from './components/Login'
Vue.use(Vuex)
const store = new Vuex.Store({
state: {
title: 'My Custom Title',
loggedIn: false
},
mutations: {
MUT_LOGIN: (state) => {
state.loggedIn = true
},
MUT_LOGOUT: (state) => {
state.loggedIn = false
}
},
actions: {
LOGIN_ACTION ({ commit }){
store.commit('MUT_LOGIN')
},
LOGOUT_ACTION ({ commit, state }) {
store.commit('MUT_LOGOUT')
}
},
getters: {
GET_LOGIN_STATUS: state => {
return state.loggedIn
},
GET_TITLE: state => {
return state.title
}
}
})
console.log('store loading');
export default store
I know that I had some of these errors at one point and got rid of them by revisiting each import statement and making some corrections to how I had things hooked up. The only other difference between now and then is that I am running all of these files through Webpack and serving them with Django in a template. (The reason for all the consoles, making sure all the files got in there via Webpack.)
The image below is a couple of the specific things it is barking about.
Because of the link from the devtools error, I have looked at this and also experimented with adding local data () properties to the component itself.
Changing
data () {
title: 'Foo',
msg: 'Bar',
},
to
data () {
return {
title: 'Foo',
msg: 'Bar',
}
},
will fix the "title" error.
As for the issue with the actions nothing is jumping out at me. Can you try mapGetters and see if you have access to the getters?
I'm getting different behaviours from session.isAuthenticated depending on how I access it. This is probably only a symptom of my real problem.
What happens:
click sign in button
window pops up, prompts for login
login, popup goes away
templates that rely on
session.isAuthenticated do not change display state (ie, continue
acting as if it is false or undefined)
if I console.log
this.get('session.isAuthenticated') I get true
When this was just torii everything was working, so I've messed up when adding ember-simple-auth somehow.
I get several "DEPRECATION: Using the injected 'container' is deprecated. Please use the 'getOwner' helper instead to access the owner of this object warnings but when it was just torii it worked fine with these warning so I assume they still aren't a problem.
templates/application.hbs
<header>
<nav class="nav-main">
{{#if session.isAuthenticated}}
<button {{action 'invalidateSession'}}>Sign out</button>
{{else}}
<button {{action 'authenticate'}}>Sign in</button>
{{/if}}
Auth: {{session.isAuthenticated}}<br>
<button {{action 'icanhaz'}}>test?</button>
</nav>
</header>
routes/application.js
import Ember from 'ember';
import ApplicationRouteMixin from 'ember-simple-auth/mixins/application-route-mixin';
export default Ember.Route.extend(ApplicationRouteMixin, {
session: Ember.inject.service('session'),
actions: {
authenticate() {
this.get('session').authenticate('authenticator:torii', 'myprovider');
},
invalidateSession() {
this.get('session').invalidate();
},
icanhaz() {
console.log(this.get('session.isAuthenticated'));
}
}
});
adapters/application.js
import DS from 'ember-data';
export default DS.JSONAPIAdapter.extend({
host: 'https://myprovider.herokuapp.com',
namespace: 'api/v2'
});
authenticators/torii.js
import Ember from 'ember';
import ToriiAuthenticator from 'ember-simple-auth/authenticators/torii';
export default ToriiAuthenticator.extend({
torii: Ember.inject.service(),
})
environment.js
module.exports = function(environment) {
var ENV = {
modulePrefix: 'my-app-prefix',
environment: environment,
baseURL: '/',
locationType: 'auto',
},
contentSecurityPolicy: {
'connect-src': "'self' myprovider.herokuapp.com",
},
torii: {
providers: {
'myprovider': {
apiKey: '6T7hlTUqfYMgcxkXPAOeNzVmC5L26bTYe9A8D5fc',
scope: 'read write',
redirectUri: 'http://localhost:4200'
}
}
}
};
};
You have to inject the session service's into all controllers/components that back templates you want to use the session in. As you're using the session in the application template you'd have to inject the session service in the application controller. Injecting it in the application route does not make it available in the template.
I have this as my router.js. Note that it has no application route:
import Ember from 'ember';
import config from './config/environment';
var Router = Ember.Router.extend({
location: config.locationType
});
Router.map(function() {
this.route('todos', { path: '/'});
});
export default Router;
When I hit the home path, I see my application.hbs template and the todos.hbs template is loaded in the outlet. This is my application.hbs:
<section id="todoapp">
<header id="header">
<h1>todos header in application.hbs</h1>
</header>
{{outlet}}
</section>
<footer id="info">
<p>
Footer in application.hbs. Double-click to edit a todo
</p>
</footer>
Why does my application.hbs get loaded?
I assume Ember knows to also load the todos.js in my routes folder which is this:
import Ember from 'ember';
export default Ember.Route.extend({
model() {
let todos = [
{
title: 'Learn Ember',
complete: false,
},
{
title: 'Solve World Hunger',
complete: false,
}
];
return todos;
}
});
And this is my todos.hbs template:
<h2>Todos Template</h2>
<ul>
{{#each model as |todo|}}
<li>
{{todo.title}}
</li>
{{/each}}
</ul>
Main questions
1. Why does my application.hbs get loaded when I hit the home route?
2. What is export default mean?
3. What is the line import Ember from 'ember' doing? Where is 'ember' coming from?
The application route is loaded in every Ember.js application. See http://guides.emberjs.com/v2.1.0/routing/defining-your-routes/#toc_the-application-route
export default is part of the ES6 module specification. See http://exploringjs.com/es6/ch_modules.html It takes the object or variable to be returned as the default thing to be imported when that module is imported inside another module.
The 'ember' namespace is built into Ember CLI. Its default export is itself, the Ember variable, which was once a global variable in prior versions of Ember.