Passing values between exported and non-exported part of script - vue.js

How to pass values between exported part of a script and non-exported one?
The construction looks like this:
<script>
// PART 1:
import { EventBus } from './event-bus.js';
EventBus.$on('value-received', value => {
this.receivedValue = value;
});
// PART 2:
export default {
data() {
return {
receivedValue: ''
}
},
watch: {...},
methods: {...}
}
</script>
How can I get the value assigned to receivedValue variable and made usable by Vue methods?

Because your EventBus.$on call uses an arrow function, the this it references is the this in scope at the time of the EventBus call.
If you are okay with all instances of your event having the same receivedValue, you could redirect the values received to an object in the scope of your file:
var shared = { receivedValue: '' };
EventBus.$on('value-received', value => {
shared.receivedValue = value;
});
export default {
data() { return shared; }
watch: ...,
methods: ....
}
Vue.js will register a handler to react on changes to the object you returned, ie. shared.
If for some reason you want a separate event stream per instance of your component, you need to create a new object inside the data() function and have your event bus update the new object directly.

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

How to add emit information to a component dynamically generated using Vue.extent()?

I'm dynamically generating instances of my child component "Action.vue" using Vue.extent() this way:
let ActionClass = Vue.extend(Action)
let ActionInstance = new ActionClass({
store
})
ActionInstance.$mount()
this.$refs.actions.appendChild(ActionInstance.$el)
This works fine. However, besides access to the store, child component also needs to emit an event (in response to user interaction with one of its elements) for the parent component to execute a method.
How to achieve this?
You can use instance.$on method to add eventListenersdynamically :
Consumer
import Dummy from "./Dummy.vue";
import Vue from "vue";
export default {
name: "HelloWorld",
props: {
msg: String
},
methods: {
mount: function() {
const DummyClass = Vue.extend(Dummy);
const store = { data: "some data" };
const instance = new DummyClass({ store });
instance.$mount();
instance.$on("dummyEvent", e => console.log("dummy get fired", e));
this.$refs.actions.appendChild(instance.$el);
}
}
};
Child component
export default {
methods: {
fire: function() {
console.log("fired");
this.$emit("dummyEvent", { data: "dummyData" });
}
}
};
Here is the Sandbox
https://v2.vuejs.org/v2/guide/components-custom-events.html
https://v2.vuejs.org/v2/api/#Options-Lifecycle-Hooks
You can use a lifecylce hook (for example: mounted) to emit the event when the child has been created.
you can listen to the events as documented in the documentation.
the store can be reached through this.$store.

VueJS How to access Mounted() variables in Methods

I'm new in Vue and would like assistance on how to access and use variables created in Mounted() in my methods.
I have this code
Template
<select class="controls" #change="getCatval()">
Script
mounted() {
var allcards = this.$refs.allcards;
var mixer = mixitup(allcards);
},
methods: {
getCatval() {
var category = event.target.value;
// I want to access mixer here;
}
}
I can't find a solution anywhere besides this example where I could call a method x from mounted() and pass mixer to it then use it inside my getCatval()
Is there an easier way to access those variables?
I will first suggest you to stop using var, and use the latest, let and const to declare variable
You have to first declare a variable in data():
data(){
return {
allcards: "",
mixer: ""
}
}
and then in your mounted():
mounted() {
this.allcards = this.$refs.allcards;
this.mixer = mixitup(this.allcards);
},
methods: {
getCatval() {
let category = event.target.value;
this.mixer
}
}
like Ninth Autumn said : object returned by the data function and props of your components are defined as attributes of the component, like your methods defined in the method attribute of a component, it's in this so you can use it everywhere in your component !
Here an example:
data() {
return {
yourVar: 'hello',
};
},
mounted() { this.sayHello(); },
method: {
sayHello() { console.log(this.yourVar); },
},
Update
you cannot pass any value outside if it's in block scope - Either you need to get it from a common place or set any common value
As I can see, var mixer = mixitup(allcards); is in the end acting as a function which does some operation with allcards passed to it and then returns a value.
1 - Place it to different helper file if mixitup is totally independent and not using any vue props used by your component
In your helper.js
const mixitup = cards => {
// Do some operation with cards
let modifiedCards = 'Hey I get returned by your function'
return modifiedCards
}
export default {
mixitup
}
And then in your vue file just import it and use it is as a method.
In yourVue.vue
import Helpers from '...path../helpers'
const mixitup = Helpers.mixitup
export default {
name: 'YourVue',
data: ...,
computed: ...,
mounted() {
const mixer = mixitup(allcards)
},
methods: {
mixitup, // this will make it as `vue` method and accessible through
this
getCatval() {
var category = event.target.value;
this.mixitup(allcards)
}
}
}
2- Use it as mixins if your mixitup dependent to your vue and have access to vue properties
In your yourVueMixins.js:
export default {
methods: {
mixitup(cards) {
// Do some operation with cards
let modifiedCards = 'Hey I get returned by your function'
return modifiedCards
}
}
}
And import it in your vue file:
import YourVueMixins from '...mixins../YourVueMixins'
const mixitup = Helpers.mixitup
export default {
name: 'YourVue',
mixins: [YourVueMixins] // this will have that function as vue property
data: ...,
computed: ...,
mounted() {
const mixer = this.mixitup(allcards)
},
methods: {
getCatval() {
var category = event.target.value;
this.mixitup(allcards)
}
}
}

mapGetters function behaves differently from store.getters

as you can see below I have a getter which returns a new method which takes a parameter, according to the documentation I should be able to call this method without doing two method calls. This works great if I use the store.getters directly, however I want to use mapGetters, but in order to get a value and not just the returned function. I need to call the method 'twice' as you would expect in vanilla js. Am I doing something wrong or is this an edge case?
Thanks
export default {
components: { AddRemove, NumberInput },
methods: {
...mapGetters({getExtra: types.GET_EXTRA}),
// Why can I omit the parenthesis when using the store.getters directly?
extraAmountWithoutParenthesis(code) {
return this.$store.getters.getExtra(code) // returns integer
},
// And why do I require them when using a named mapGetters method
extraAmountRequiresParenthesis(code) {
// return this.getExtra(code) // returns function
return this.getExtra()(code) // returns integer
}
}
}
In my getters:
[types.GET_EXTRA]: (state) => (code) => {
let value = state.extras[code]
if (!value) {
value = 0
}
return parseInt(value)
},
mapGetters() result must be merged into computed, not methods.

Resolve Vue component names on the fly

I'm searching for a way to resolve Vue (sfc) component names 'on the fly'. Basically I'm getting kind of component names from a backend and need to translate them into real component names on the frontend. Something like this:
<text/> => /components/TextElement.vue
or
<component v-bind:is="text"></component> => => /components/TextElement.vue
The idea is providing a global function that maps the backend names to the frontend names. I don't want to do this in every place where those components are used, so maybe there is a kind of hook to change the names dynamically?
Thanks a lot,
Boris
You could create a wrapper <component> that maps the component names and globally registers the specified component on the fly.
Create a component with a prop (e.g., named "xIs") for the <component>'s is property:
export default {
props: {
xIs: {
type: String,
required: true
}
},
};
Add a template that wraps <component>, and a data property (e.g., named "translatedName") that contains the mapped component name (from backend to front-end name):
export default {
data() {
return {
translatedName: ''
}
}
};
Add a watcher on the prop that will register a component by the name indicated in the prop value:
import componentNames from './component-names.json';
export default {
watch: {
xIs: {
immediate: true, // run handler immediately on current `xIs`
async handler(value) {
try {
const name = componentNames[value]; // 1. lookup real component name
if (name) {
Vue.component(name, () => import(`#/components/${name}`)); // 2. register component
this.translatedName = name; // 3. set name for
}
} catch (e) {
console.warn(`cannot resolve component name: ${value}`, e);
}
}
}
}
}