How to watch router object in main vue instance - vuejs2

From my main vue instance I am trying to detect when a route is changed. So far what i have learned that i need to deep watch an object to detect property changes. But my approach is not working. What am i doing wrong:
const app1 = new Vue({
el: '#app1',
router,
data: {
activeRoute: router.currentRoute
},
methods: {
printclass: function() {
console.log(router.currentRoute.path);
}
},
computed: {
},
watch: {
'router.currentRoute': {
handler(newVal) {
console.log('changed');
}, deep: true
}
},
created: function() {
console.log(router.currentRoute);
}
});
I know that the currentRoute object's path property changes when the route changes(i.e; a different component is rendered).

Watch the $route.
watch: {
'$route' (to, from) {
this.yourFunction(
},

Related

nested object reactivity vue js

I have the following setup for my vue application
var store = {
...
state: {
currentCustomer:{},
},
};
current customer has a property that is an object called payment method
app:
var app= new Vue({
el:'#application',
data: {
sharedState: store.state
}
});
and a couple of components:
Vue.component('user_search', {
template: '#user_search-template',
data() {
return {
sharedState: store.state
}
},
methods: {
getCustomerData: function () {
this.sharedState.currentCustomer(c);
}
mounted: function () {
...
}
});
and
Vue.component('paymentdetails',{
template: '#payment_details_template',
data(){
return{
sharedState: store.state
}
},
mounted:function(){
...
}});
The issue is like this. The payment method component does not bind to the payment details object that is nested inside the current customer object
any suggestions?
Yeah, I think what you are looking for is a computed property for accessing the data.
Vue.component('paymentdetails',{
template: '#payment_details_template',
computed{
sharedState() {
return store.state
}
},
mounted:function(){
...
}});
Maybe give that a try and see how it works.

Reactive localStorage object across browser tabs using Vue.js

I'm trying to make a localStorage object reactive, so that if it updates, it will also update in other tabs using that same object. I'm using Vue.js and have tried to create a computed property that returns the localStorage object, but this doesn't work. How can I make the object reactive and make it update in other browser tabs also?
computed: {
localStorageObject() {
return JSON.parse(localStorage.getItem('object'));
}
}
#Matthias has the start of an answer, but it won't react to changes in other tabs. To do that, you can handle the StorageEvent. Here's a rough sketch that you could enhance as desired.
const app = new Vue({
el: '#app',
data: {
name: ''
},
methods: {
onStorageUpdate(event) {
if (event.key === "name") {
this.name = event.newValue;
}
}
},
mounted() {
if (localStorage.name) {
this.name = localStorage.name;
}
window.addEventListener("storage", this.onStorageUpdate);
},
beforeDestroy() {
window.removeEventListener("storage", this.onStorageUpdate);
},
watch: {
name(newName) {
localStorage.name = newName;
}
}
});
There is an example of how to achieve this on the Vue.js cookbook page: https://v2.vuejs.org/v2/cookbook/client-side-storage.html
In the example, a name is stored in local Storage.
const app = new Vue({
el: '#app',
data: {
name: ''
},
mounted() {
if (localStorage.name) {
this.name = localStorage.name;
}
},
watch: {
name(newName) {
localStorage.name = newName;
}
}
});

How to make vuex DRY when defining global accessed property?

I've some generic state like error and session in $store which I want to be accessed as computed property error and session in all components (including those in router). I know I should do like this:
var store = new Vuex.Store({
state: {
error: undefined,
session: JSON.parse(localStorage.getItem('session')),
},
mutations: {
error: function(state, error) {
state.error = error
},
session: function(state, session) {
state.session = session
},
},
})
var loginView = new Vue({
computed: {
error: {
get () {
return this.$store.state.error
},
set (val) {
this.$store.commit('error', val)
}
},
session: {
get () {
return this.$store.state.session
},
set (val) {
this.$store.commit('session', val)
}
},
},
})
var router = new VueRouter({
routes: [
{
path:'/login',
component: loginView,
},
],
})
var app = new Vue({
store,
router,
computed: {
error: {
get () {
return this.$store.state.error
},
set (val) {
this.$store.commit('error', val)
}
},
session: {
get () {
return this.$store.state.session
},
set (val) {
this.$store.commit('session', val)
}
},
},
})
As you can see, it's not DRY. What if I have 100 components in router and I want them to be able to have computed properties like this? Is there something to solve this? Maybe something like an option globals in Vuex:
var store = new Vuex.Store({
globals: ['error', 'session'], // this will enable all components' computed property of error and session
state: {
error: undefined,
session: JSON.parse(localStorage.getItem('session')),
},
mutations: {
error: function(state, error) {
state.error = error
},
session: function(state, session) {
state.session = session
},
},
})
var loginView = new Vue({
computed: {
},
})
var router = new VueRouter({
routes: [
{
path:'/login',
component: loginView,
},
],
})
var app = new Vue({
store,
router,
computed: {
},
})
I do not see anywhere in the documentation that Vuex supports the set of a computed value. If it did, you would probably be better off using mapGetters.
If you want to follow the spirit of Vuex, I expect the best approach would be
to take advantage of mapGetters and mapActions/mapMutations and map your set to an action/mutation.
You could also use a mixin to do this. I know you said you want to define these values globally (and you could do that with a global mixin), but you could stay DRY by defining a mixin with these properties and apply it where you actually need them.
Lastly you could use a global mixin. Take note of the caveats of using a global mixin in the linked documentation.
Vue.mixin({
computed: {
error: {
get () {
return this.$store.state.error
},
set (val) {
this.$store.commit('error', val)
}
},
session: {
get () {
return this.$store.state.session
},
set (val) {
this.$store.commit('session', val)
}
},
}
})
error is a pretty generic name and you may want to rethink it if this is meant to be a property of every component.

Where I should place handler of emit?

I have child component and want to pass some data to it's parent.
My child component looks like:
// <button #click="sendClick($event)">Send</button>
// ...
data: function (){
return {
mycode: ""
}
},
methods: {
sendClick(e)
{
bus.$emit('change', this.mycode);
}
}
My parent component looks:
var app = new Vue({
el: '#app',
data: {
currentView: 'past-form',
mycode: ''
},
methods:
{
changeView()
{
this.currentView = 'past-form'
console.log(this.mycode);
},
},
created()
{
bus.$on('change', function(mycode){
this.mycode = mycode;
});
}
})
I haven't found a better place for placing bus.$on (bus is declared globally) than in created(), but the docs state that created() is for stuff that should be initialized after the page is loaded. The created() block works; I checked it by placing in it console.log(this.mycode), but should I move emit handler somewhere else?
It's look like my code does not execute mycode: '', because console.log(this.mycode); does not print anything.
As I mentioned in the comment, if your component is a direct child of your Vue, then there is no need for a bus.
That said, the created handler is fine for adding your bus event handler.
I expect the issue you have is a this issue. Try changing your handler to
bus.$on('change', mycode => this.mycode = mycode)
See How to access the correct this inside a callback?
Here is an example.
console.clear()
const bus = new Vue()
Vue.component("child", {
template: `<button #click="sendClick($event)">Send</button>`,
data: function() {
return {
mycode: "something"
}
},
methods: {
sendClick(e) {
bus.$emit('change', this.mycode);
}
}
})
var app = new Vue({
el: '#app',
data: {
currentView: 'past-form',
mycode: ''
},
methods: {
changeView() {
this.currentView = 'past-form'
console.log(this.mycode);
},
},
created() {
bus.$on('change', mycode => {
this.mycode = mycode
this.changeView()
})
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.3.4/vue.js"></script>
<div id="app">
<child></child>
Parent mycode: {{mycode}}
</div>

Reference component in another one

In a VueJS file i have two component :
var firstComponent = Vue.extend({
template: '#component1',
[...]
methods:
comp1function: function() {
[...]
comp2function()
}
[...]
}),
var secondComponent = Vue.extend({
template: '#component2',
[...]
methods:
comp2function: function(){
do things here
}
[...]
})
so what i want to do is to say : do comp2function when you finished to do comp1function. So is there any way i can reference a function from my template component2 in my template component1 ?
You should use Vue Events. I assume you are using VueJS 1.x?
So let's imagine you have those two components as you stated in your question:
var firstComponent = Vue.extend({
template: '#component1',
methods: {
doSomething () {
// Do things here
console.log('I do things')
// Fire an event when you want:
this.$dispatch('some-event')
}
}
})
var secondComponent = Vue.extend({
template: '#component2',
methods: {
doSomethingElse () {
console.log('I do something else')
}
},
events: {
trigger () {
this.doSomethingElse()
}
}
})
You need to trigger an event from your first component using the $dispatch method. Then, in your Vue Instance, you need to get that event, and use $broadcast to broadcast a new event to the other Vue children:
new Vue({
el: '#app',
components: {
firstComponent,
secondComponent
},
events: {
'some-event' () {
this.$broadcast('trigger')
}
}
})
You will then be able to get the event in your secondComponent and trigger whatever method you want
You can learn more about custom events on this page: http://v1.vuejs.org/guide/components.html#Parent-Child-Communication