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

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>

Related

How to fix Laravel Blade, Vuejs and Handlebars.js delimiter (mustache) issue?

Handlebars.js file that done not load if it is placed VueJs container (#app).
<script id="details-template" type="text/x-handlebars-template">
<div class="label label-info"> #{{ domain }}'s Email</div>
</script>
I get the error message raised by Vue.
[Vue warn]: Property or method "domain" is not defined on the instance but referenced during render. Make sure that this property is reactive, either in the data option, or for class-based components, by initializing the property. See: https://v2.vuejs.org/v2/guide/reactivity.html#Declaring-Reactive-Properties.
When I remove the #app id from the container, handlebar template works fine. I have tried to change the VueJs delimiter in app.js file
const app = new Vue({
el: '#app',
delimiters: ["<%","%>"]
});
It did not work.
What could be the solution?
It's because your domain variable is not defined and you are trying to use it in your HTML. You should initialize the variable and then use it. try this:
const app = new Vue({
el: '#app',
delimiters: ["<%","%>"],
domain: null
});

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.

nested vue instance inside vue instance

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

Add components to Vue instance after instance is created

If you've created a VueJS instance like this...
var app = new Vue({
el: '#app',
components: {...}
})
Is it possible to add components to this instance it's instantiated?
The reason I ask is that we have a multi-page app which stares a template. We want the instantiation of the Vue app to be in the shared template code but we want each page to use different components so for example the code on the contact page would be split between two files...
Master.js
Contact.js
The contact.js file would need to tell the main app that it wanted to use the conract-form component, but that component is not used on other pages so we don't want to add it to the initial app declaration in the master.js
Any help would really be appreciated.
Thanks to #thanksd
It seems as though components only have to be registered when instantiating Vue if you want the registered "locally", which means you don't have to register them at all as long as the component code comes before Vue is instantiated.
So, my master template and master.js can contain this...
<div id="app">
<header>Master header</header>
<contact-page inline-template>
Contents of contact page
</contact-page>
<footer>Master Footer</footer>
</div>
var app = new Vue({
el: '#app'
})
Then, my contact.js can contain this....
Vue.component('contact-page', {
... Contact page specific code here...
});
We had a similar trouble, with multiple pages, a layout et multiple components. Our system isn't a SPA. Each page reload. The page content is insert in a global layout with some global options by server code.
We have global components and some more especific by page, our solution is use window to catch Vue and initialize vue after charge the components by page.
IMPORTANT: follow this order declarations: windowVue / code specific for the page / startVue
EX:
layout file:
<!doctype html>
<html>
<head>
<script type="text/javascript" src="../js/windowVue.js"></script>
<!-- all header content -->
<!-- depend your system here call your js specific by page ex: productPage.js -->
<script type="text/javascript" src="../js/productPage.js"></script>
</head>
<body>
<div id="vueApp">
<!-- Your page content-->
</div>
<script type="text/javascript" src="../js/startVue.js"></script>
</body>
</html>
windowVue.js
import Vue from 'vue';
window.Vue = Vue;
productPage.js
// Here two options declare the external components only
import ComponentA from '../js/components/component-a.js';
import ComponentB from '../js/components/component-b.vue';
window.Vue.component('component-a', ComponentA)
window.Vue.component('component-b', ComponentB)
// Or if you use a page component with more logic and options you can declare here
// and include the other components as usual
window.Vue.component('product-page', {
components: {
ComponentA,
ComponentB
},
// rest of the code as usual
})
startVue.js
import GlobalA from '../js/components/global-a.js';
import GlobalB from '../js/components/global-B.js';
new window.Vue({
el:"#vueApp",
delimiters: ['${', '}'],
components: {
GlobalA,
GlobalB
}
})
That's all now each page has their owns components and we have some shared components too.
some remarks:
build the 3 js part separately
Only windowVue.js use import Vue from 'vue' the rest use window.Vue.
.js files components are declared as an object.
component-a.js
import ComponentB from '../js/components/component-b.vue';
import ComponentC from '../js/components/component-c.vue';
import ComponentD from '../js/components/component-d.vue';
export default {
name:'component-a',
components: {
ComponentB,
ComponentC,
ComponentD
},
data() {
return {
variableExample: 'example'
}
} // more of your Vue code
}

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.