How can i include data option in child instances in Vue.js?
<html>
<body>
{{ foo }}
<script>
var root = new Vue({
el: 'body'
});
var child = new Vue({
parent: root,
data: function() {
foo: 'bar'
}
});
</script>
</body>
</html>
Here nothing is getting printed in the body. But if i add the foo variable inside the root instance, it's getting printed. How to make the data option work for child instances?
Following is the syntax is to have data attribute in a child vue instance:
const Foo = {
data: function () {
return {
foo: "I am Foo"
}
},
template: '<div>foo: {{foo}}</div>'
}
and you can use this data property only in the template of child instance. You can see the whole working demo in this fiddle.
Another style of creating a vue component and using it can be found here, with JS code.
Vue.component('child', {
data () {
return {
foo: 'bar'
}
},
template: '#foo'
});
var vm = new Vue({
el: '#root'
});
Related
I need global variables for errors. But I don't want set input variable for every component.
How I can watch $errors in component ABC without input variable?
(without <abc :errors="$errors"></abc>)
index.js:
Vue.prototype.$errors = {};
new Vue({
el: '#app',
render: h => h(App),
}
App.vue:
...
name: 'App',
components: {
ABC
}
...
methods:{
getContent() {
this.$errors = ...from axis...
}
Component ABC:
<template>
<div>{{ error }}</div>
</template>
...
watch: {
???
}
Here's an example of how it could be done:
const errors = Vue.observable({ errors: {} })
Object.defineProperty(Vue.prototype, '$errors', {
get () {
return errors.errors
},
set (value) {
errors.errors = value
}
})
new Vue({
el: '#app',
methods: {
newErrors () {
// Generate some random errors
const errors = {}
for (const property of ['name', 'type', 'id']) {
if (Math.random() < 0.5) {
errors[property] = 'Invalid value'
}
}
this.$errors = errors
}
}
})
new Vue({
el: '#app2',
watch: {
$errors () {
console.log('$errors has changed')
}
}
});
<script src="https://unpkg.com/vue#2.6.10/dist/vue.js"></script>
<div id="app">
<pre>{{ $errors }}</pre>
<button #click="newErrors">New errors</button>
</div>
<div id="app2">
<pre>{{ $errors }}</pre>
</div>
I've created two Vue instances to illustrate that the value really is shared. Clicking the button in the first instance will update the value of $errors and the watch is triggered in the second instance.
There are a few tricks in play here.
Firstly, reactivity can only track the reading and writing of properties of an observable object. So the first thing we do is create a suitable object:
const errors = Vue.observable({ errors: {} })
We then need to wire this up to Vue.prototype.$errors. By defining a get and set for that property we can proxy through to the underlying property within our observable object.
All of this is pretty close to how data properties work behind the scenes. For the data properties the observable object is called $data. Vue then uses defineProperty with get and set to proxy though from the Vue instance to the $data object, just like in my example.
as Estradiaz said:
You can use Vuex and access the value outside of Vue like in this answer: https://stackoverflow.com/a/47575742/10219239
This is an addition to Skirtles answer:
You can access such variables via Vue.prototype.variable.
You can set them directly, or use Vue.set, it works either way.
My code (basically the same as Skirtless):
main.js
const mobile = Vue.observable({ mobile: {} });
Object.defineProperty(Vue.prototype, '$mobile', {
get() { return mobile.mobile; },
set(value) { mobile.mobile = value; }
});
function widthChanged() {
if (window.innerWidth <= 768) {
if (!Vue.prototype.$mobile) Vue.set(Vue.prototype, '$mobile', true);
} else if (Vue.prototype.$mobile) Vue.set(Vue.prototype, '$mobile', false);
}
window.addEventListener("resize", widthChanged);
widthChanged();
Home.vue:
watch: {
'$mobile'(newValue) {
// react to Change in width
}
}
I'd like to get access to vm data from outside the instance like so:
myComponent.vue
export default {
data() {
return {
name: 'Joe'
};
}
}
main.js
var vm = new Vue({
el: '#app',
render: h => h(myComponent)
});
Desired Result
console.log(vm.name); // should return - Joe
For some reason, console returns undefined. What I'm doing wrong?
To access vue.js object datas from inside you can use $property_name. Example
var vm = new Vue({
el: '#app',
data() {
return {
name: "Kapucni",
}
},
template: '<div>{{ name }}</div>'
});
// use $name .property
console.log(vm.$data.name);
console.log(vm.$el);
// calling functions from $method, etc ...
<script src="https://cdn.jsdelivr.net/npm/vue#2.6.10/dist/vue.js"></script>
<div id='app'>
</div>
Thanks to comments from ittus, I realised that I have to look for child components, not the root component.
We can get to child component like so:
vm.$children[0].name
where $children[0] is a direct (and first in this case) child of the root component.
One of my root Vue.js components is using an x-template, displayed in 3 different parts of my app (3 separate Vue instances).
Everything is working if I put the data directly into the component, but I can’t figure out how to pass in data to each individual vm instance. Here’s what I’m using at present:
Vue.component('some-table', {
template: '#some-template',
data() {
return { someArray: [] }
},
methods: {
}
});
let someVm = new Vue({
el: '#some-div',
});
The template:
<script id="some-template" type="text/x-template">
<table v-for="(item, id) in someArray" v-bind:key="id">
<tr>
<td>{{item.someKey}}</td>
</tr>
</table>
</div>
</script>
Within another JavaScript class, I’m attempting to push data to someArray:
class SomeClass {
constructor() {
}
someMethod() {
let newData = [1,2,3];
someVm.someArray = newData; // doesn't work
someVm.someArray.push(...newData); // doesn't work
someVm.someArray.concat(newData); // doesn't work
}
}
Using any of the various lines in someMethod() above, I can see someVm contains someArray, but with no observers.
I can’t seem to figure out how to push new data to each instance, then be accessible within some-template. I’m obviously missing something really simple, but I’ve been stuck on this for a while now, so any pointers would be amazing!
Thanks!
Do not set data attr at component registration (Vue.component(...)). Instead, declare the props that component uses and pass it through markup or js.
Vue.config.productionTip = false;
Vue.config.devtools = false;
Vue.component('some-component', {
template: '#some-component',
props: {
message: {
type: String, // declare the props: https://vuejs.org/v2/api/#props
}
}
});
const cp1 = new Vue({
el: '#cp1',
data() {
return {
msg: "using data"
};
}
});
const cp2 = new Vue({
el: '#cp2'
});
const cp3 = new Vue({
el: '#cp3'
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script id="some-component" type="text/x-template">
<p>{{message}}</p>
</script>
<div id="cp1">
<some-component :message="msg"></some-component>
</div>
<div id="cp2">
<some-component message="using props"></some-component>
</div>
<div id="cp3" is="some-component" message="using props and is"></div>
I'm trying to use a functional component to render multiple elements without having to have a single root element but I can't seem to have access to the component data, see this simplified example:
Trying to access context.data logs an empty object {}:
<div id="app">
<hello />
</div>
Vue.component('hello', {
functional: true,
render: function(h, context) {
console.log(context.data) // This logs {}
return new Array(5).fill(1).map((_, index) => h('p', {}, 'Hello world'))
},
data () {
return {
foo: 'bar'
}
}
})
new Vue({
el: '#app',
data: function() {
return {
foo: '<p>foo</p>',
bar: 'bar'
}
}
})
A working jsfiddle
Thanks in advance
A functional component has no data which is why it does not receive data in the context. You can find additional information in this section of the manual
If you want to render a component without having to use a single root element you might want to give vue-fragment a try.
I am pretty new to VueJs, so I am still figuring things out.
Since our templates are stored in the database, I want my templates to load async. For my components I now use the component-factory approach.
var c = Vue.component('my-async-component', function(resolve, reject){
setTimeout(function(){
resolve({
template: '<div class="loader">loaded asynchronous: {{ pageName }}</div>',
data() {
return {
pageName: 'my Page'
}
}
})
},2000)
})
But is it possible to have some kind of placeholder while loading it? I know I can do something with But in that case I need to have a parent component and I would like this to be independent.
On a Vue-instance you can do stuff in the render function end hook it up to mounted like:
var app = new Vue({
el: '#app',
data: {
finish: false,
template: null
},
render: function(createElement) {
if (!this.template) {
return createElement('div', 'loading...');
} else {
return this.template();
}
},
mounted() {
var self = this;
$.post('myUrl', {foo:'bar'}, function(response){
var tpl = response.data.template;
self.template = Vue.compile(tpl).render;
})
}
})
Is this possible in a component? And is this still working when I have some nested divs (see an other question of mine: here)
Ok, I figured it out. I just needed to reed de VUE guide a little bit bettter.
I just followed the advanced async example from the docs and now I have a working example.
So I have my template like this:
<div id="app">
<my-async-component></my-async-component>
</div>
Then in my JS I declared the template like:
var c = Vue.component('my-async-component', function(){
return {
component: new Promise(function(resolve, reject){
// setTimeout to simulate an asynchronous call
setTimeout(function(){
resolve({
template: '<div class="loader">loaded asynchronous</div>'
})
},3000)
}),
loading: Vue.component('loader', {
template: '<p>loading...</p>'
}),
error: Vue.component('load-error', {
template: '<p>error loading component</p>'
}),
delay: 200,
timeout: 10000
}
})
var vm = new Vue({
el: '#app'
});
The loading and error components could also be globally registered components, so it's easy to reuse.
Hopefully I could help someone with this answer to my own question.