Vue-meta: metaInfo doesn't have an access to computed properties - javascript

I'm using vue-meta to dynamically change my meta tags. I want to change it only on some particular pages.
I'm using metaInfo function and try to change, for example, a title. But data from my getter is undefined which is why I cannot change the title in meta tags. It seems like metaInfo function try to access data before the component actually has it.
Here is my code in the component:
<template>
...
</template>
<script>
export default {
metaInfo() {
return {
title: this.getViewPage.data.meta.title, // data is undefined
};
},
created() {
this.loadViewPage();
},
computed: {
...mapGetters(['getViewPage']),
},
methods: {
...mapActions(['loadViewPage']),
};
</script>

vue-meta just creates computed property from your metaInfo function (according to plugin source code), so I assume that your loadViewPage action fills data object asynchronously and your problem just transforms to null-checking problem.
So you should check data before using its properties, and when data will be loaded metaInfo will update object as well:
metaInfo() {
// don't know your return object structure,
// maybe you should check whole this.getViewPage
let data = this.getViewPage.data;
return {
title: data ? data.meta.title : "some placeholder title",
}
};

Related

Vue 2 watch fails for mixin data property, but works if the component has the data property

I am trying to move some functionality to a vue mixin from the component, to be able to use it in multiple components.
This (simplified version of the code) works:
export default {
data() {
return {
file: {},
audioPlayer: {
sourceFile: null,
},
};
},
watch: {
'audioPlayer.SourceFile': function (nextFile) {
console.log('new sourceFile');
this.$data.file = nextFile;
},
}
}
But if I move the audioPlayer data object to a mixin, the watch does no longer fire.
Is this expected behavior?
N.b. I resolved this by directly making the 'file' data property into a computed value, which works in this particular case, but the behavior is still strange.
You need a lowercase s. sourceFile not SourceFile
watch: {
'audioPlayer.sourceFile': function (nextFile) {
console.log('new sourceFile');
this.$data.file = nextFile;
},
}

Access "this" in template

I want to dynamic create map layers in my template. Therefor I think I need access to the vue instance in my template
<template>
<MglVectorLayer
v-for="(layer, idx) in lLayers"
:key="idx"
:sourceId="'someid'"
:source="somevectorsourcedata"
:layerId="layer"
:layer="this[layer]" <<<<< I need to get the computed propery (layer1 & layer2) here
/>
</template>
export default {
data () {
layers: ['layer1', 'layer2']
},
computed: {
layer1 () {
return (..somelayerdata..)
},
layer2 () {
return (..someotherlayerdata..)
}
}
}
When I leave this out, I get the warning: Expected Object, got String ...
Does anyone know how I can access this? Or maybe there is another/better way of doing this?
Just use a method:
:layer="getLayer(layer)"
methods: {
getLayer(layer) {
return this[layer]
}
}
Since component instance isn't available as this inside v-for, it can be replaced with _self:
:layer="_self[layer]"
Or if specified dynamic property names were data, $data could be used:
:layer="$data[layer]"

Vue.js dynamically rendering svg

I'm trying to render a svg file containing Vue.js template syntax at specific places. The file gets rendered correctly.
Upon instantiation of the inner element (the svg), the template syntax gets replaced, but a vue warning is emitted:
vue.js:597 [Vue warn]: Property or method "data" 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. See: https://v2.vuejs.org/v2/guide/reactivity.html#Declaring-Reactive-Properties.
In the SVG there is a text containing the template syntax {{data}}
Vue.component('svg-show', {
props: {
model: {
required: true
},
compiled:null
},
render: function (h) {
return h("div",
[
h(this.compiled, {
props: {
data: this.model
}
})
]);
},
created() {
},
mounted() {
console.log("mounted");
console.log(this.model.SVG);
this.compiled = Vue.compile("<div>" + model.SVG + "</div>");
}
});
https://jsfiddle.net/dg2hkeby/10/
i also tried to use a static variable for the data property, but this did not succeed either.
How would one achieve what i'm trying to do using Vue.js?

VueJs + Laravel - like button component

I'm trying to get a good understanding of VueJS, and I'm using it with Laravel 5.7 for a personal project, but I can't exactly figure out how to do a, probably, simple task a "like" button\icon.
So, here's the situation, I have a page, displaying various posts from my database, and at the bottom of each post I want a "like toogle" button, which I made with an icon followed by the number of likes on that post; At first the button will contain the data retrieved from the corresponding database table, but if you click it will increase the displayed number by one and insert a new like in the database.
I made the "like" icon as a component :
<section class="bottomInfo">
<p>
<likes now="{{ $article->likes }}"></likes>
<span class="spacer"></span>
<span class="entypo-chat">
{{ $article->comments }}
</p>
</section> <!-- end .bottomInfo -->
As you can see there's a <likes> in which I added a prop now, by what I'm understanding till now about components, in this way I can insert the data from my db as a starting value (now contains the db row value), problem is, I don't know where\how to keep that value in my app, in which I'm gonna also use axios for increasing the likes.
Here's the component:
<template>
<span class="entypo-heart"> {{ now }}</span>
</template>
<script>
export default {
props: ['now'],
data() {
return {
like: this.now
}
},
mounted() {
console.log('Component mounted.');
}
}
</script>
What I tried to do (and I don't know if it's correct) is to pass the value of now to the data function inside a property named like, so, if I understood correctly, that variable like is now part of my properties in my main Vue instance, which is this one
const app = new Vue({
el: '#main',
mounted () {
console.log("The value of 'like' property is " + this.like)
},
methods: {
toggleLike: function() {
} //end toggleLike
}
});
The mounted function should print that property value, but instead I get
The value of 'like' property is undefined
Why? Is this how it works? How can I make it so I can get that value and also update it if clicked, to then do a request to my API? (I mean, I'm not asking how to do those single tasks, just where\how to implement it in this situation). Am i getting the component logic right?
Probably a bit more verbosity never hurt:
props: {
now: {
type: Number,
required: true
}
}
Instead of using the data function, use a computed property:
computed: {
likes: {
get: function() {
return this.now
}
}
}
However, here comes the problem.
If you need to change the # of likes after the user clicks like, you have to update this.now. But you can't! It's a property, and properties are pure. Vue will complain about mutating a property
So now you can introduce a data variable to determine if the user has clicked that like button:
data() {
return {
liked: 0
}
}
Now we can update our computed property:
likes: {
get: function() {
return this.now + this.liked
}
}
However, what are we liking? Now we need another property:
props: {
id: {
type: Number,
required: true
},
now: {
type: Number,
required: true
}
}
And we add a method:
methods: {
add: function() {
//axios?
axios.post(`/api/articles/${this.id}/like`)
.then (response => {
// now we can update our `liked` proper
this.liked = 1
}).catch(error => {
// handle errors if you need to
)}
}
}
And, let's make sure clicking our heart fires that event:
<span class="entypo-heart" #click="add"> {{ now }}</span>
Finally our likes component requires an id property from our article:
<likes now="{{ $article->likes }}" id="{{ $article->id }}"></likes>
With all this in place; you're a wizard now, Harry.
Edit
It should be noted that a user will be forever able to like this, over and over again. So you need some checks in the click function to determine if they like it. You also need a new prop or computed property to determine if it was already liked. This isn't the full monty yet.

Vue JS 2 data manipulation

I have an instance of mounted in Vue, where I'm trying to set a date picker having the starting date depends on a data fed by an ajax response. Let's say this data property is named start_date. I run my ajax request via the created instance in Vue.
It's a little weird when I tried to console log vm.myObject, it shows the correct value start_date property coming from the ajax response. However, whenever I access the specific property via vm.myObject.start_date it will show you the default one I've created for data binding. My code structure below:
<script>
export default {
mounted() {
const vm = this;
console.log(vm.myObject); // this will show the data from ajax response
console.log(vm.myObject.start_date); //this will show the default value I set which si the 2017-10-25
},
created() {
const self = this;
$.ajax({
url: ApiRoutes.paths.GetDealData,
data: { id: 1 },
success: function(res) {
self.myObject.start_date = res.start_date;
}
});
},
data() {
return {
myObject: { start_date: "2017-10-25" }
};
}
};
</script>
I'm very new to Vue JS, so I'm currently having a hard time handling the data in the component via ajax request. I've already tried all the instances included beforeCreate, beforeMount but it didn't fix my issue still. How can I understand this kind of behavior?
Your code can't really work the way you described in your answer.
you are doing asynchronous operation (ajax call) and try to print the values right after synchronous operation? nope.
If you want to console.log(response) , you can do it in your callback function.
If you want to print the value on the page, but show nothing until the value is fetched (asynchronous operation), you can define on your data an attribute that signal if the fectching process is finished or not. and toggle it inside your callback.
I have edited the code to show how to declare the date-picker (have to be declared from the template side.
You have to pass the start_date as a props (I assume the prop name for the date-picker is start-date). when the ajax request is finished, the reactivity of vue will take care of re-rendering of the date-picker
<template>
<div>
<datePicker :start-date="myObject.start_date" />
</div>
</template>
<script>
export default {
created() {
$.ajax({
url: ApiRoutes.paths.GetDealData,
data: { id: 1 },
success: function(res) {
self.myObject.start_date = res.start_date;
self.isFetchedFinished = true
}
});
},
data() {
return {
startDate: ''
};
}
};
</script>
The reason is console.log() working before AJAX response is returned, so console.log(vm.myObject.start_date) prints unchanging string with initial value. However, console.log(vm.myObject) prints your object, which then changes, and you can see actual property value in browser console.
Example:
var obj = { "name": "oldName" };
console.log(obj);
obj.name = "newName";
If you want a "frozen" version of your object, you can create a copy for logging:
console.log(Object.assign({}, vm.myObject));
Also, instead self.myObject.start_date = res.start_date; you probably want to use this.$set(this.myObject, 'start_date', res.start_date); for change tracking.

Categories