nested vue instance inside vue instance - vue.js

I want to put vue instance inside a vue instance so i can use the the dom element as my template and not write the template in the javascript code
ie
{ template : 'template for the component' }
example :
main.js
var v2 = new Vue({
el: '#child',
data : {
msg1 : 'child msg'
}
});
Vue.component(v2);
var v = new Vue({
el: '#parent',
data : {
msg : 'parent msg'
}
});
index.html
<div id="parent">
<h1>{{msg}}</h1>
<div id="child">
{{msg1}}
</div>
</div>
as you can see i dont use template i use the el property so my template come from the html on the page the element #parent and #child in this case also as you can see i dont give a name to the component and i just register the vue instance as a component Vue.component(v2);
and not Vue.component('child',v2); for example
now this is working and everything is good but there is no documentation
to this all the examples show how you can register a component with a template to a vue instance
because there is no documentation I am a afraid that in the next version of vue may be it will not work any more
someone know if this is the case or not

Related

Trying to use ref inside a Vue single file component. Got undefined

I'm beginning with vuejs and I try to figure what could be done about reference of child component instance in root instance. I used ref attribute and it works pretty well, except if I use it in a single file component (in the template tags). In this specific case, I get 'undefined'.
So, I try to understand why, because it could be very useful for establishing dynamic references. I could probably bypass that situation easily, but I would like to understand the problem instead of run away.
So if someone have an idea ;)
I am using webpack to import my single file component in my app.js and compiled it. However the template compilation isn't done by webpack, but by the browser at runtime (maybe it's the beginning of an explanation ?).
My app is very simple, and I log my references on click on the header, so I don't think it's lifecylce callback related.
Here is my files :
app.js
import Vue from 'Vue';
import appButton from './appButton.vue';
import appSection from './appSection.vue';
var app = new Vue({
el: '#app',
components:
{
'app-button' : appButton
},
methods:
{
displayRefs: function()
{
console.log(this.$refs.ref1);
console.log(this.$refs.ref2);
console.log(this.$refs.ref3);
}
}
});
my component appButton.vue
<template>
<div ref="ref3" v-bind:id="'button-'+name" class="button">{{label}}</div>
</template>
<script>
module.exports =
{
props: ['name', 'label']
}
</script>
my index.html body
<body>
<div id="app">
<div id="background"></div>
<div id="foreground">
<img id="photo" src="./background.jpg"></img>
<header ref="ref1">
<h1 v-on:click="displayRefs">My header exemple</h1>
</header>
<nav>
<app-button ref="ref2" name="presentation" label="Qui sommes-nous ?"></app-button>
</nav>
</div>
</div>
<script src="./app.js"></script>
</body>
ref1 (header tag) and ref2 (app-button tag) are both found. But ref3 (in my single file component) is undefined. Also
Thanks for all the piece of answer you could give me, hoping it's not a silly mistake.
A ref you set is only accessible in the component itself.
If you try to console.log(this.$refs.ref3); into a method from appButton.vue, it will work. But it won't work in the parent.
If you want to access that ref from the parent, you need to use the $ref2 to access the component, and then use the $ref3. Try this:
var app = new Vue({
el: '#app',
components:
{
'app-button' : appButton
},
methods:
{
displayRefs: function()
{
console.log(this.$refs.ref1);
console.log(this.$refs.ref2);
console.log(this.$refs.ref2.$refs.ref3); // Here, ref3 will be defined.
}
}
});
Accessing some child ref from the parent is not supposed to be a good practice tho.

Show value in template in VueJS

I have some code below, I want show "header" in template and show or hide when header not null.
<div id="hung">
<cmx-test v-bind:header="${properties.header}"></cmx-test>
</div>
<script>
Vue.component('cmx-test', {
props: ['header'],
template: '<h1 class=\"fillColor\" data={{this.header}}></h1>'
});
// create a new Vue instance and mount it to our div element above with the id of app
var vm = new Vue({
el: '#hung'
});
</script>
There is some syntax mistake in your code, to insert text into tag you can achieve with v-text attribute not data={{this.header}}.
And if you wanna hide some tag or component based on data value, you can use v-if.
And the last thing is if you wanna pass value intro component you can achieve that with this way v-bind:header="value", and value is variable which hold value you wanna pass.
<div id="hung">
<cmx-test v-if="header" v-bind:header="value"></cmx-test>
<button #click="header = true">Display</button>
</div>
<script>
Vue.component('cmx-test', {
props: ['header'],
template: '<h1 class=\"fillColor\" v-text="header"></h1>'
});
// create a new Vue instance and mount it to our div element above with the id of app
var vm = new Vue({
el: '#hung',
data: {
header: null,
value: "Hello World!"
}
});
</script>
Your question is not totally clear. You have the component receiving the prop header but header is not defined on the main Vue instance. You need to be passing that prop from the main data object / Vue instance into the component to use it there.
<div id="hung">
<cmx-test v-if="header" :header="header"></cmx-test>
</div>
<script>
Vue.component('cmx-test', {
props: ['header'],
template: '<h1 class=\"fillColor\" :data="header">{{header}}</h1>'
});
// create a new Vue instance and mount it to our div element above with the id of app
var vm = new Vue({
el: '#hung'
data:{
header: null,
}
});
</script>
Also using the same data object header to control whether the component is rendered or not using v-if. So if header is null or false it wont be rendered. When it becomes true or contains a value it will be rendered and the value of header will be passed to component through binding it (e.g. :header="header")

Nested Vue instances

Is there anyway to have nested Vue instances? I know about Vue components and I use them extensively in my applications but in this use case I have different applications (I mean different projects) that are loading inside each other in a page.
For example when I do something like the following:
<div id="parent">
{{msg}}
<div id="child">
{{msg}}
</div>
</div>
...
new Vue({
el: '#parent',
data: { msg: 'sth' },
})
new Vue({
el: '#child',
data: { msg: 'sth else' },
})
But both msg's uses msg data of parent Vue instance. Here I just want to show an example but in my use case these instances are not next to each other and just somehow relate to each other through Django framework (which is not important to notice here).
Update
It's not a duplicate question. Person who asked that question doesn't need nested Vue instances and just needs components. But I explicitly said that I know about components but need nested Vue instances.
Issue
According to this issue, they are not going to implement such behavior.
If you really need to have nested instances, use VueJS v-pre directive. You can add v-pre to the child app. More details about it here.
<div id="parent">
{{msg}}
<div id="child" v-pre>
{{msg}}
</div>
</div>
...
new Vue({
el: '#parent',
data: { msg: 'sth' },
})
new Vue({
el: '#child',
data: { msg: 'sth else' },
})
{{ msg }} for child instance will be "sth else". Note that the nested instance (#child element) is not compiled by vue parent instance because of the v-pre directive.

How do I use a vue component without instantiating a root instance?

Details
I am working on a website with different page setups.
My setup is not an SPA and so I do not have the privalige of one single root instance.
Problem
This means that if I create a component I have to register a root vue instance every time I want to use my component.
Example of the issue
I create my custom component as a global component:
Vue.component('mycomponent', { /* options */ });
According to the vue docs I have to register a root instance in order to use my component
new Vue({ el: '#root-instance' });
<div class="header" id="root-instance">
<mycomponent></mycomponent>
</div>
Then in a different section I want to use the same component but I have to create another root instance:
new Vue({ el: '#other-root-instance' });
<div class="sidebar" id="other-root-instance">
<mycomponent></mycomponent>
</div>
I tried using a class for instantiating, something like:
new Vue({ el: '.root-instance' });
But view only loads this once.
Question
Is there any way to load a component but not instantiate a root instance every time I use it?
Note: I have several root instances on the page and therefore can not declare a single root instance for the page. Effectively I do not want to make my page a Single Page App.
You don't have to wrap your component in a root instance div, you can make the component tag the root instance.
Vue.component('myComponent', {
props: ['custom'],
template: '<div>Hi there, I am the {{custom}}</div>'
});
new Vue({
el: '#sidebar'
});
new Vue({
el: '#topmenu'
});
<script src="//cdnjs.cloudflare.com/ajax/libs/vue/2.2.4/vue.min.js"></script>
<my-component id="sidebar" custom="sidebar"></my-component>
<div>
Some stuff that is not under Vue control {{custom}}
</div>
<my-component id="topmenu" custom="top menu"></my-component>

How to make Vue js directive working in an appended html element

I have a Vue directive added in an appended html element like v-on directive but it's not working on my end. Basically I want to know the equivalent of .on() in jquery.
"Vue.js compilation happens when you instantiate/mount the root instance. It doesn't detect new DOM being injected unless it is a result of a directive (e.g. v-repeat, v-partial will dynamically create new DOM fragments)."
https://github.com/vuejs/Discussion/issues/77
You have to compile your newly added element like this:
html:
<div id="app">
<button v-on="click: addNewElement()">Add Element</button>
<br />
</div>
JavaScript
new Vue({
el: '#app',
data: {
sampleElement: '<button v-on="click: test()">Test</button>'
},
methods:{
addNewElement: function(){
var element = $('#app').append(this.sampleElement);
/* compile the new content so that vue can read it */
this.$compile(element.get(0));
},
test: function(){
alert('Test');
}
}
});
See this working Fiddle on Firefox: http://jsfiddle.net/chrislandeza/syewmx4e/
Update
$compile has been removed on Vue 2.x
I've seen people suggesting Vue.compile or
var tmp = Vue.extend({
template: 'Content'
})
new tmp().$mount(' id or refs ')
But those 2 does not behave like the old $compile.
For Vue 2.x, the new solution is referenced here in the doc : https://v2.vuejs.org/v2/api/#vm-mount (see 'Example').
Custom example :
var MyComponent = Vue.extend({
template: '<div v-on:click="world">Hello!</div>',
methods : {
world : function() {
console.log('world')
}
}
})
var component = new MyComponent().$mount()
document.getElementById('app').appendChild(component.$el)
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.3.4/vue.min.js"></script>
<div id="app"></div>
Works like a charm!
You need to register the html as a template of the component.