vue mixin shared between instance and component - vue.js

I have some functionality that I'd like to share throughout my site. My site does have multiple Vue instances, and not just one single app instance entry point.
I've created my mixin like this:
var fooMixin = {
data: {
someProperty: null,
someOtherProperty: "Foo"
},
methods{
...
}
}
This mixin is then injected into my Vue instance like so:
new Vue({
el: '#app',
mixins: [fooMixin],
data: {
...
},
methods: {
...
}
})
Of course this works exactly as intended, but my issue is that I want to reuse this mixin within a component elsewhere. This causes issues.
Here's how it's injected into the component:
Vue.component('bar', {
props: ['someProp'],
template: barTemplate,
mixins: [fooMixin],
data: function () {
return {
mySpecialProperty: null
}
},
methods: {
...
}
})
As you can imagine, the mixin cannot be merged with the data property of the components. Since this is a component, the data property must return a function that return the object. This is not how my mixin has been set up.
This is the error I'm given from Vue:
[Vue warn]: The "data" option should be a function that returns a per-instance value in component definitions.
Can I create a mixin that is reusable across instances and components?

Change your data property in mixin from object to function
var mixin = {
data: function () {
return {
someProperty: null,
someOtherProperty: 'foo'
}
}
}

Related

How to compute a property based on an object with fallback

I have a component that receives an object as prop, like this:
props: ['propObject']
Then, there's a default object defined (I use VueX, so it's actually defined as a $store getter, but to make it simpler, let's say it's defined in the data method) in the data:
data() {
return {
dataObject: {defaultValueA: 1, defaultValueB: 2}
}
}
And I'd like to have a computed property that would behavior like this:
computed: {
computedObject() {
return Object.values(this.propObject).length > 0 ? this.propObject : this.dataObject;
}
}
However, I know this is not possible because Vue watchers don't watch for changes in the key/value pairs of an object.
I have tried to go with a watched property, like this:
props: ['propObject'],
data() {
return {
object: {},
defaultObject: {}
}
},
watch: {
propObject: {
handler: function() {
this.setComputedObject();
},
deep: true
}
},
methods: {
setComputedObject() {
this.object = Object.values(this.propObject).length > 0 ? this.propObject : this.defaultObject;
}
},
mounted() {
this.setComputedObject();
}
However, the watcher handler is not being called at all when the propObject changes, but if I call it directly via console, it works. Is there any way that I can make the computedObject become reactive?
you need to use Vue.set/vm.$set where you change the props (in source component)
for example
changeProp(){
this.$set(propObject,'newprop','newval');
}
and then just you regualr compouted in the target component (the component which receive the prop)
source : https://v2.vuejs.org/v2/guide/list.html#Object-Change-Detection-Caveats

vue.js two way data-binding between components

Please take a look at this not-working pseudo code:
Vue.component('child', {
props: [],
template: '<div><input v-model="text"></div>',
data: function() {
return {child-text: ""}
}
})
Vue.component('parent', {
template: '<h1> {{text}} </h1>'
data: function() {
return {parent-text: ""}
}
})
What is the most elegant way to fix this code that whenever the user changes the content of input box in child component, then the variable child-text in child component and the variable parent-text in parent component will change automatically? I also want that if the variable child-text and/or parent-text change then the content of input box will change respectively?
I solved this with my own little data store, its a very simple approach but works good enough for me without the necessity to dive into Vuex.
First, I create my data store somewhere before initializing anything else.
window.globalData = new Vue({
data: {
$store: {}
},
});
After that, I add a global Mixin that allows to get and set data to the global storage.
Vue.mixin({
computed: {
$store: {
get: function () { return window.globalData.$data.$store },
set: function (newData) { window.globalData.$data.$store = newData; }
}
}
});
Then, every component can access the data storage by this.$store. You can check a working example here:
https://codesandbox.io/s/62wvro7083

Vue.js - How to Avoid Code Duplication

How does one avoid code duplication when using something like computed properties across multiple components?
An example is that I have a computed property to get the parent route name - it's pretty simple:
computed: {
parent_route () {
return this.$route.matched[0].name
}
}
I am finding that I use this computed property across multiple components. How could I store this in one place that all my components can use?
You can use Mixins. For the most general case there's the Global Mixin (use with care):
Vue.mixin({
computed: {
parent_route () {
return this.$route.matched[0].name
}
}
})
The this.parent_route computed property is now automatically defined in all components.
But you should avoid abusing global mixins. Instead, you can apply them locally using Option Merging (the mixins option):
var mixin = {
computed: {
parent_route () {
return this.$route.matched[0].name
}
}
};
new Vue({
mixins: [mixin],
data: function () {},
created: function () {
console.log(this.parent_route) // should be ok
}
})
You can use mixins. Create myMixin.js file with:
export const myMixin = {
computed: {
parent_route () {
return this.$route.matched[0].name
}
}
}
Then you can import it in your vue component like this:
import {myMixin} from '../myMixin.js' //valid path to myMixin.js file here
and register it:
...
data {...},
mixins: [myMixin],
methods: {...}
...
More on mixins:
https://v2.vuejs.org/v2/guide/mixins.html

Vue pass data from child component to child component

So I have a structure like this
<Root>
<HomeNav> router-view
<RouterLink>
<RouterLink>
<RouterLink>
<RouterLink>
<RouterLink>
<Home> router-view
<RouterLink>
Now, each of HomeNav router links is changing the data in component by passing data to root, however that means that I need to bind that data:
<router-view v-bind:title="title" v-bind:text="text" v-bind:youtube="youtube" v-bind:transition="transition"></router-view>
and run the functions on created and updated:
new Vue({
el: '#app',
router,
data: Variables,
created: function () {
path = pathname.pathname();
pathLenght = pathname.countPathLenght(path);
this.homeText(this.data);
},
updated: function () {
path = pathname.pathname();
pathLenght = pathname.countPathLenght(path);
Pace.restart()
this.homeText(this.data);
},
methods: {
homeText(data) {
data = toogleData.checkText(data);
this.title = data[0];
this.text = data[1];
this.youtube = data[2];
}
}
})
However, the problem is I don't need that data on all of the routes and I don't need to trigger this.homeText function or bind that specific data on every single root. It is only needed on homepage, so the first route.
So the question is, is it possible to directly pass data from HomeNav component to Home component without having all that code in global (root) component?
This is a great place for the MessagePump that the VueJs documentation proposes. It is in essence and unbound Vue object that acts as an intermediary between objects. This allows you to define and call events on the pump which gets passed to the appropriate component.
window.MessagePump = new Vue({});
Vue.Component(
'HomeNav',
{
...
data: function () {
return {
homeText: 'something'
}
},
...
mounted: function () {
var thisArg = this
MessagePump.$on(
'homeTextChanged',
function(newText) {
thisArg.homeText = newText;
}
);
}
...
}
);
Vue.Component(
'Home',
{
...
mounted: function () {
MessagePump.$emit('homeTextChanged', 'To This');
}
...
}
);
This will trigger the event and the changing of homeText from 'something' to 'To This'.

How to get data to work when used within a component and Axios?

I'm new to Vue.js and Axios. I don't quite understand how to get the data option to work when used within a component.
Why doesnt' my test work?
I get the following error in the console:
[Vue warn]: The "data" option should be a function that returns a per-instance value in component definitions.
[Vue warn]: Property or method "symbols" is not defined on the instance but referenced during render. Make sure to declare reactive data properties in the data option. (found in root instance)
My simple test:
My data (snipped for brevity):
[{"id":1, "name": "Airfield"}, {"id":2, "name": "Ship Yard"}]
My component:
Vue.component('symbols-table', {
template: '<h1>Hello World</h1>',
data: {
symbols: []
},
created: function(){
axios.get('symbols.json').then(response => this.symbols = response.data);
}
});
Vue instance:
var app = new Vue({ el: '#app' });
My HTML:
<symbols-table>
<ul><li v-for="symbol in symbols">{{symbol.name}}</li></ul>
</symbols-table>
As the error is saying:
The "data" option should be a function
In the component, the data must be a function, you need to modify the data block to be a function which will return the data structure to be used in DOM in a reactive way:
Vue.component('symbols-table', {
template: '<h1>Hello World</h1>',
data: function() {
return {
symbols: []
}
},
created: function(){
axios.get('symbols.json').then(response => this.symbols = response.data);
}
});
data inside a Vue Component should be a function that returns an object, as described in the Vue.js common gotchas.
You can type simply like this:
data() {
return {
...... tra-ta-ta.......
}
},