Add components to Vue instance after instance is created - vue.js

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
}

Related

How import components?

i'm developing my first vue app.
I decided to add it to my project using the html <script> tag (so not using the CLI).
Can I use the import statement for importing componente stored in .vue file?
You can't import .vue files unless you are using a module bundler like webpack.
However, there are multiple ways of defining a vue component which don't require you to use .vue files.
For example you can define a component in a file like this:
helloWorld.js
export default {
template : `<div>{{ message }}</div>`
data : () => ({
message : 'Hello World'
})
}
and then import it like this:
app.js
import HelloWorld from 'helloWorld.js';
new Vue({
el: '#app',
components: {
HelloWorld
}
});
Just remember to add type="module" when you import your js files into your html:
<body>
<div id="app">
<hello-world></hello-world>
</div>
<script type="module" src="helloWorld.js"></script>
<script type="module" src="app.js"></script>
</body>

Issues using vue-js-modal component

I followed directions on how to use the component from their documentation however I get TypeError: show is not a function
In my main JS file (app.js) I added the following and adding to my project using npm
import VModal from 'vue-js-modal'
Vue.use(VModal)
The documentation states that I can now call a modal from anywhere in the app, so I created a page specific JS file and included the following to hide/show a modal with name="hello-world" on the page that included vue, app.js and the page specific profile.js file.
export default {
methods: {
show() {
this.$modal.show('hello-world');
},
hide() {
this.$modal.hide('hello-world');
}
}
}
When I load the page, I don't see the modal content, however when I click the link Modal I get an error about the show method TypeError: show is not a function
I am using laravel mix and verified that everything is being compiled as expected. Below is a how I am including JS files on the profile page:
<script type='text/javascript' src='/assets/js/manifest.js?ver=5.2.3'></script>
<script type='text/javascript' src='/assets/js/vendor.js?ver=5.2.3'></script>
<script type='text/javascript' src='/assets/js/app.js?ver=1569678574'></script>
<script type='text/javascript' src='/assets/js/profile.js?ver=1569678574'></script>
I am trying to "level up" my Vue and JavaScript experience, previously I just stuck to writing ES5 and my Vue was written without components and bound to a page specific Vue instance, so any help would be greatly appreciated!
EDIT
app.js
window.Vue = require('vue');
require('./global/header.js');
Vue.component('tabs', require('./components/Tabs.vue'));
Vue.component('tab', require('./components/Tab.vue'));
import VModal from 'vue-js-modal'
Vue.use(VModal)
new Vue({
el: '#app'
});
profile.js
export default {
methods: {
show() {
this.$modal.show('hello-world');
},
hide() {
this.$modal.hide('hello-world');
}
}
}
webpack.mix.js that compiles profile.js
mix
.js("resources/js/pages/home.js", "assets/js/home.js")
.js("resources/js/pages/teams.js", "assets/js/teams.js")
.js('resources/js/pages/profile.js', 'assets/js/profile.js')
The error doesn't specify if its the $modal.show() function or your profile.js show() function that is undefined. I suspect that it's your profile.js show() function because it looks like everything is in order with regards to vue-js-modal.
You need to add profile.js as a vue mixin (https://v2.vuejs.org/v2/guide/mixins.html) in order for its functions to be added to the vue instance. So in your app.js add:
import profile from '/assets/js/profile'
Vue.mixin(profile);

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.

After laravel-mix upgrade app no longer sees global vars

I'm upgrading a project from laravel-mix v2.0 to v4.0 and I'm seeing an issue now where at runtime my components can't see globally scoped variables like they did before. How can upgrading the build tool impact the runtime?
I see I can add instance properties to the vue prototype, but is that really the approach I need to take? Seems like it should still be able to read global variables like it did before.
html
<script type="text/javascript">
var games = [
// a bunch of objects
];
</script>
<script src="{{ mix('js/app.js') }}"></script>
app.js
import ChannelSubscriptionSlider from './components/guild-subscriptions/ChannelSubscriptionSlider.vue';
Vue.component('channel-subscription-slider', ChannelSubscriptionSlider);
ChannelSubscriptionSlider.vue
import Vue from 'vue';
import VueResource from 'vue-resource';
Vue.use(VueResource);
export default {
data: function () {
return {
games: games, // undefined when used within this component, but used to work before upgrade
}
},
Edit 2
Use `window.games, this would "register" your variables globally.
Although, what i do, is the following, consider a MPA not a SPA:
In app.js i just leave the following lines:
require('./bootstrap');
window.Vue = require('vue');
In a separate file, called main.js that i made, i put this, as an example:
import Sidebar from './components/layouts/Sidebar.vue'
import Topnav from './components/layouts/Topnav.vue'
new Vue({
el: '#sidebar',
render: h => h(Sidebar)
});
new Vue({
el: '#topnav',
render: h => h(Topnav)
});
at the end of app.blade.php i put:
<script src="{{ asset('js/app.js') }}"></script>
<script type="text/javascript">
const user_props = {
fullName : {!! json_encode(Auth::user()->fullName) !!},
username : {!! json_encode(Auth::user()->username) !!},
}
user_props.install = function(){
Object.defineProperty(Vue.prototype, '$userProps', {
get () { return user_props }
})
}
Vue.use(user_props);
</script>
<script src="{{ asset('js/main.js') }}"></script>
This works because i mount vue in app.js but the components that use user_props are loaded after i declare and install the prototype... Also, since vue is mounted in app.js i can use Vue.use(user_props); after loading it...
And forgot to mention that in webpack.mix.js you should add the main.js:
mix.js('resources/js/app.js', 'public/js')
.sass('resources/sass/app.scss', 'public/css')
.js('resources/js/main.js', 'public/js/')
Edit 1
Based on your comments, and the docs: https://v2.vuejs.org/v2/cookbook/adding-instance-properties.html#The-Importance-of-Scoping-Instance-Properties
The $ is just a convention:
... We scope instance properties with $ to avoid this. You can even use your own convention if you’d like, such as $_appName or ΩappName, to prevent even conflicts with plugins or future features.
So with that in mind you could set it up as:
Vue.prototype.games = games;
then you can access it on every component as this.games
As the documentation implies, when doing this you've got to be careful to not overwrite it. So if you have it declared on the data section of your Vue components i think you should delete those lines...

How to Access Vue-Loader Components in an HTML File

I would like to use the modular style and file format of Vue Loader (i.e., where I have a template section, script section and style section in each .vue file).
What I can't figure out how to do (or if it is even possible to do) is use my custom templates in an html file.
For instance, in the App.vue file I can use the following code:
<template>
<div id="app">
<message>Hello there</message>
</div>
</template>
This will work to display a custom message component on the home page.
What I would like to do instead is use my custom components in html files. For instance, in the index.html file to use the following code:
<div id="app">
<message>Hello there</message>
</div>
Any idea how I can do this? Thanks.
NOTE: I am new to Vue Loader and semi-new to Vue (so I apologize in advance if the answer to this question is obvious).
There are many ways you can compile a single file component and then use that component in a web page.
Use vue-cli
Vue released a command line interface tool called vue-cli that can initialize projects and build components with zero configuration. One option to build a component that you can use in your page is to use vue build.
vue build MyComponent.vue --prod --lib MyComponent
This will compile a script that exposes MyComponent. If you include that script in your page and then add it globally,
Vue.component(MyComponent)
That component will be available to you in any of your Vues.
Make a plugin
Here is a sample of a very basic framework for making a plugin.
myPluginDefinition.js
window.MyPlugin= {};
MyPlugin.install = function (Vue) {
Vue.component('my-component', require('./my-component.vue'));
}
webpack.config.js
module.exports = {
entry: "./myPluginDefinition.js",
output: {
path: __dirname+'/dist',
filename: "MyPlugin.js"
},
module: {
loaders: [
{
test: /\.vue$/,
loader: 'vue-loader',
}
]
}
};
This will build a file called MyPlugin.js that will contain each of the single file components that you include in the install function. Include the script on your page and then call
Vue.use(MyPlugin)
and you will have all of your components.
Use a custom webpack configuration
There are many ways you could configure webpack to build your single file components. You could build them all into a single file or build them separately. I suggest if you want to use one of these options you ask a separate question.
Actually you can do this easily by:
register your component :
Vue.component('message', {
template: '<div>A custom component!</div>'
});
then comment the render function in your Vue instance like so:
new Vue({
el: '#app',
// render: h => h(App)
})
after that you will be able to render your message Tag like this:
<div id="app">
<message></message>
</div>
Edit :
if you don't want to use this way you can define it in your view instance:
new Vue({
el: '#app',
// render: h => h(App)
components: {
message: {
template: `
<h1>Hello World</h1>
`
}
}
})
Import desired component definition object and pass it to options.components
<template>
<some-component></some-component>
</template>
<style>...</style>
<script>
import SomeComponent from 'path/to/some-component.vue';
export default {
components: {
// ES2015 shorthand for SomeComponent: SomeComponent
SomeComponent
}
}
</script>
That leverages local component registration
Both the default export and SomeComponent are component definition objects.