Vue data vue object variables from component - vue.js

I am very new to Vue and I am having difficulty accessing data in main Vue instance from component. In the Vue instance I have:
var vm = new Vue({
computed: {},
data: {
show: true
},
and in the component, I want to do something like:
<button v-show="vm.show" #click="setDefaults(styleguide)">Set Default</button>
My goal is when 'show' value changes, I want to display/hide the button. It is little difficult/weird because I create template in the component, not in the html. When I try this code, it doesn't understand 'vm.show'. I feel like I need to create data in the component and tie the data to the 'show' variable, or create computed in the component or something (I believe computed is like watcher?). If there is easy way to handle this, please help.

I'm also new to VueJs, but I believe the issue is you haven't provided the el argument to the Vue instance, and in this case assigning the Vue instance to a variable doesn't do anything.
I think you want something like
<div id="app">
<button v-show="show" #click="setDefaults(styleguide)">Set Default</button>
</div>
<script>
new Vue({
el: '#app',
computed: {},
data: {
show: true
},
...
);
</script>

So I guess my question wasn't very clear, but I got it to figure it out. In the component code, I needed to add:
Vue.component('styleguide', {
computed: {
show: function () {
return vm.show
}
},
props: ['styleguide'],
template: `
<div>
<div>
<p>
<button v-show="show" #click="setDefaults(styleguide)">Set Default</button>
This allows me to access 'show' in the main Vue instance from the component template. Whenever other component modifies 'show' variable in the main Vue instance, the button disappears/reappears. I am not sure if this makes sense, but this is how I got it to work.

Two things, in the template all variables are already scoped from the component so you don't need the vm. in there. The second thing is that the data property of a component expects a function which returns an object.
var vm = new Vue({
computed: {},
data: () => ({
show: true
}),
<button v-show="show" #click="setDefaults(styleguide)">Set Default</button>
If you need to access data from a parent component you will need to pass it on using props. you can also try to do it using provide/inject if that suits your usecase better

Related

Props passed to dynamic component in Vue are not reactive

I am using Vue's dynamic component to load a component depending on what is to be displayed. As these components all have different props I build an object and pass it in via v-bind depending on what I need to use from the original state model.
However, when I do this I lose the reactive nature of Vue's props data flow. The code sample below shows an example of this with the name changing on the standard component but not on the dynamic version.
I expect this is something to do with the string value being copied into the new object, rather than a reference to the original reactive property. Can anyone advise on how I can make this work as expected?
Vue.config.productionTip = false;
Vue.component("example-component", {
props: ["name"],
template: '<span style="color: green;">{{name}}</span>'
}
);
var app = new Vue({
el: "#app",
data: {
person: {
name: "William"
},
component: null
}
});
// Load the dynamic component
setTimeout(function() {
app.component = {
is: 'example-component',
props: { name: app.person.name }
}
// Change the name
setTimeout(function() {
app.person.name = "Sarah";
}, 2000);
}, 2000);
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<strong>Standard Component</strong><br />
<example-component :name="person.name"></example-component><br /><br />
<div v-if="component">
<strong>Dynamic Component</strong><br />
<component :is="component.is" v-bind="component.props"></component>
</div>
</div>
There's two different objects here, person and component, which are declared in the app component's data property.
It is app.component.props.name which is bound to the component's prop, but you are modifying app.person.name which is separate and not bound to the component.
app.component.props and app.person are two separate object instances, and so modifying one of these object's properties will have no effect on the other.
It's difficult to suggest a suitable solution to your problem because your example is too simple (and a little contrived). What you want will not work so long as you are copying the name value between different objects.
I'd redo all of the code, and perhaps use a computed property instead. But with the least amount of changes you can do this:
app.component = {
is: 'example-component',
props: {
get name() { return app.person.name; }
}
}
Now app.component.props.name is actually a getter function which returns app.person.name. Vue can observe this, and will react when app.person.name changes.

Best way to a vue instance (app1) call another vue instance (app2)

Example:
<div id="app1"> <button #click="etc...">run APP1</button> ..</div>
<div id="app2">...
<button #click...>run APP1</button><!-- HERE! need a reference--> ...
<button #click...>run APP2</button>
</div>
How to say to Vue in APP2 that I need to call APP1?
I need that both buttons "run APP1" do exactly the same thing. Supposing that, in the illustration, the first is working, and the second is a copy/paste... But the second will not work because is into app2.
Concrete case: see "GOOD1" and "GOOD2" buttons at this code similar to the used in the last question...
Every Vue instance implements an events interface. This means that to communicate between two Vue instances (app1 and app2) you can use Custom Events.
So, in your example, before anything, give the first instance (that will emit events) a name so the other instance can reference it:
var app1 = new Vue({
el: '#app1'
})
And emit events from it (this code is at app1's template):
<button #click="$emit('go-modal', {header: 'HEL<big>LO2</big>', showModal: true})">GOOD1</button>
<button #click="$emit('go-modal', {header: 'BYE2', showModal: true})">GOOD2</button>
In the second instance (app2), listen to those events using $on:
new Vue({
el: '#app2',
// ...
mounted() {
app1.$on('go-modal', this.handleApp1);
},
methods: {
handleApp1(data) {
this.header = data.header;
this.showModal = data.showModal;
}
}
})
See demo JSFiddle here.

Load default value from HTML input to vue.js data

I wonder if it is possible to load value of tag value as default value for vue.js data?
Works fine with
coupon: $('#coupon').val(),
but I really do not want to use jQuery.
Code is available here: https://jsfiddle.net/4a80dnfo/1/
So I found a way to get this done using the beforeMount() hook.
Assuming this HTML:
<div id="coupon-code">
<input ref="coupon" v-model="coupon" value="99PI_ORG" />
</div>
This Javascript should get the job done:
new Vue({
el: '#coupon-code',
data: {
coupon: ''
},
beforeMount: function() {
this.coupon = this.$el.querySelector('[ref="coupon"]').value;
}
// methods and such here.
}
});
I ended up using the beforeMount() hook after some trial an error. Some things I've learned which may be relevant:
this.$refs does not exist in the created() hook.
this.$refs.coupon.value returns an empty string in the mounted() hook.
The ref attribute of an element is deleted before the mounted() hook, so something like this.$el.querySelector('input[ref=coupon]') does not work.
You can use this.$el.querySelector('input[data-ref=coupon]').attributes['value'].value inside mounted(), but this seems worse to me than using beforeMount().
If anyone has suggestions on how to do this better, I'm very open to feedback. This solution still feels sub-optimal to me.
In your jsfiddle you are using v-model, so in your data() you just have to set your desired value
Edit: updated code to use v-bind
new Vue({
el: '#app',
data: {
coupon: 'You value here',
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.3.0/vue.js"></script>
<div id="app">
<input id="coupon" type="text" name="coupon" :value="coupon" />
</div>
<input name="coupon" v-model="coupon" value="def val">
window.app = new Vue({
el: '#xxx',
data: {
coupon: document.querySelector("input[name=coupon]").value
}
...
});

Vuejs modal component cannot reference method

I have a modal dialog which is trying to use a method on the Vue app instance but getting the error
app.js:32117 [Vue warn]: Property or method "calcFees" is not defined on the instance but referenced during render. Make sure to declare reactive data properties in the data option.
The app declaration
Vue.component('sale', require('./components/Sale.vue'));
const app = new Vue({
el: '#app',
data: {
showModal: false
},
methods: {
calcFees: function (event) {
alert('GOOD');
}
}
});
Sale.vue component minimised for now
<template name="sale">
<input type="text" placeholder="Sale Price" class="form-control" #blur="calcFees">
</template>
The sale component is simply included in the main page here
<sale v-if="showModal"></sale>
The modal dialog works fine, displays the above text input however the above error is shown in the console and the blur event doesn't call the method.
It seems it has something to do with the component template, because i tested the blur event successfully by putting a text input in the main blade page directly.
Any ideas why it doesn't work in this way? I saw a comment somewhere about something to do with it being a <template> but it didn't explain how to fix.
Components cannot access methods declared in other components or the root Vue directly.
The problem with this code is that the calcFees method is declared in the root Vue, but you are trying to call it from the Sale.vue component.
There are several ways to make this work. One is you can move calcFees to the component. Another is you can $emit an event to the parent with whatever it needs to use in calcFees.
Sale.vue
<template name="sale">
<input type="text" v-model="price" placeholder="Sale Price" class="form-control" #blur="onSale">
</template>
<script>
export default {
data(){
return {
price: null
}
},
methods: {
onSale(){
this.$emit('sale', this.price)
}
}
}
</script>
Vue
<sale v-if="showModal" #sale="calcFees"></sale>
const app = new Vue({
el: '#app',
data: {
showModal: false
},
methods: {
calcFees: function (price) {
alert(price);
}
}
});

Can't stop Vue JS (v2) from reusing components

I have a Vue app that conditionally displays sets of the same component, I'd like to tie in to the created or mounted methods, but Vue keeps re-using earlier components and not triggering the methods. I've tried wrapping the components in keep-alive tags to no effect.
The code below shows what you would expect on the page: changing someVariable to 'test1' or 'test2' shows the relevant components, but the created/mounted methods only fire a maximum of 2 times (e.g. changing someVariable to 'test1' logs creation and mounting of 1 component with the label type1, then changing it to 'test2' only logs 1 more component with the label type3).
Currently using v2.1.10
HTML
<div id="app">
<div v-if="someVariable === 'test1'">
<keep-alive>
<test-component data-type="type1"></test-component>
</keep-alive>
</div>
<div v-if="someVariable === 'test2'">
<keep-alive>
<test-component data-type="type2"></test-component>
</keep-alive>
<keep-alive>
<test-component data-type="type3"></test-component>
</keep-alive>
</div>
</div>
JS/Vue
Vue.component('test-component', {
props: ['data-type'],
template: '<div><p>In the component: {{ dataType }}</p></div>',
created: function () {
console.log('component created: ', this.dataType);
},
mounted: function () {
console.log('component mounted: ', this.dataType);
}
});
var app = new Vue({
el: '#app',
data: {
someVariable: ""
}
});
You should use a watcher on your someVariable instead of trying to hook on created and mounted hooks.
Components are created, and mounted the first time they are visible (rendered). There are NO "shown" or "hidden" hooks.
See https://v2.vuejs.org/v2/guide/computed.html#Watchers:
watch: {
someVariable: function (newValue) {
// test newValue and do what you have to do!
}
}
For your specific example removing keep-alive from the second if should do the trick https://jsfiddle.net/z11fe07p/464/
An interesting thing is that vue seems to re-use the previously rendered component. So if you have one component in the first if when switching to the next if with 2 components it will re-use one component and create a new one for the second component from the block. When getting back to the first if block it will re-use one of the 2 already rendered components.
As mentioned above, a watcher is more suited for such cases, thus getting you rid of handling logic in places where you don't have full control. You can see this tip right here in the docs https://v2.vuejs.org/v2/api/#updated