How to pass props to Vue from another js module? - javascript

Is it possible to pass props from any js module to vue?
Props are passing fine between components for me, but not from the actual Vue app itself:
main.js
import Vue from 'vue'
import App from './App.vue'
var myVue = new Vue({ export to other files
el: '#entry',
components: {App},
render: h => h(App),
data: function(){
return{
testSuccess:'this test was successful!'
}
},
})
window.myVue = myVue // we use window.myVue because if we can export to window, we can export to other js modules.
App.vue
<template>
<div ref="app">
{{ testSuccess ? testSuccess : 'prop not imported!' }}
</div>
</template>
<script>
export default = {
name: "app",
props: ["testSuccess"]
}
</script>
index.html
<div id="entry" >
<app :testSuccess="testSuccess"></app>
</div>
<script src="/dist/build.js"></script>
What am I missing?
I understand how to do this with components.
I want to be able to export the Vue module into other js modules and pass meaningful information to it.

This is the render function for your root Vue instance:
render: h => h(App)
You aren't passing any props to h, so the App will be created without props.
The template inside #entry will be ignored because you're providing an explicit render function.
So either:
Remove the render function from the root Vue instance. Note that the reason most examples use a render function is so that they can use the runtime-only build of Vue, which can't compile templates.
Remove the template from inside #entry and pass the props to App within the render function.
The latter would look like this:
render (h) {
return h(App, { props: { testSuccess: this.testSuccess } })
}
Note this can't use an arrow function because it requires access to this.
Once you're passing the props correctly you should be able to update the value using myVue.testSuccess = '...' no problem.

As you have discovered, you cannot pass props to your $root Vue app. However, you can modify the properties of the Vue instance and Vue will react to those changes.
In your example above, you could write anywhere (including the console):
window.myApp.testSuccess= "I've been changed!";
and the HTML should update.
However, the way you have written your components above mean that the testSuccess property is not being passed into the App.vue component. Instead of making your App.vue a component of the root Vue instance, create them like this:
index.html
<div id="app" >
</div>
<script src="/dist/build.js"></script>
main.js
import Vue from 'vue'
import App from './App.vue'
var myVue = new Vue({ // export to other files
el: '#app',
...App,
})
window.myVue = myVue // we use window.myVue because if we can export to window, we can export to other js modules.
App.vue
<template>
<div>
{{ testSuccess || 'testSuccess is blank!' }}
</div>
</template>
<script>
export default {
data: { // doesn't need to be a function in the root Vue instance
testSuccess: "this is the default text",
...
}
</script>
AN EVEN BETTER WAY
Despite all the above, an even better way is to use proper state management. By placing all your shared state into a dedicated state object (or VueX), any module which has access to the state object can manipulate the state.
Have a read of https://v2.vuejs.org/v2/guide/state-management.html

Related

How to expose a custom attribute added to the window object in Vue 3 using Vue Router

I have exposed a custom attribute added to the Window object to the client browser via the onMounted hook of a child component. Since the component was rendered inside the App.vue component it worked just fine, I was able to access the custom attribute (an object) from the browser, etc. But since the app needed more views I have implemented Vue Router and views so the way my app is rendering is different and now I'm trying to find a way to expose the object with the new folder structure:
Before Implementing vue router:
APP.vue Template:
<template>
<WidgetContainer />
</template>
WidgetContainer Component onMounted Lifecycle Hook:
onMounted(async () => {
window.myWidget = {
load: retrieveEndUserJWT,
};
});
And then I can do something in the WidgetContainer method:
const retrieveEndUserJWT = async (callback) => {
//do something
};
Now after implementing the Vue router, I have changed my project structure a little bit so instead of just functional components I have views:
So now the App.vue template looks like this:
<template>
<router-view></router-view>
</template>
The problem with this is that the exposed custom attribute added to the Window object (window.myWidget) never reaches the DOM because is on the OnMounted lifecycle hook of the WidgetContainer child component. Is there any way I can still have that object without compromising the logic of my widgetContainer child component ? maybe emitting an event from there to the app.vue component ?
Actually the answer was pretty simple. I just moved the implementation of the new attribute to the mounted hook of the App.vue and then call the child component method from this component.
<script setup>
import { onMounted } from "vue";
onMounted(() => {
window.myWidget = {
load: retrieveEndUserJWT,
};
});
const retrieveEndUserJWT = async () => {
console.log("Hello World");
};
</script>
<template>
<router-view></router-view>
</template>

Vue props is undefined when print component dynamically

My problem is on the dynamically creation of the TAG "galeriaimages".
Vue works fine but the props are always undefined
thanks for all.
main.js
import Vue from 'vue'
import Gi from './components/galeriaimages.vue'
import vuetify from './plugins/vuetify';
Vue.config.productionTip = false
document.addEventListener('DOMContentLoaded', function() {
new Vue({vuetify, render: h => h(Gi) }).$mount('galeriaimages');
});
HTML
<galeriaimages p1="awesome" /> <!-- I create it dinamically-->
Vue component
<script>
export default {
props: ['p1'] ,
data: function() {
return {
}
},
created: function() {
alert(this.p1); //this is always undefined
}
}
Thanks to #skirtle for give me the answer :-)
I added this line in my vue.config.js
runtimeCompiler: true
...and all works fine
The bit where you write h(Gi) is creating a galeriaimages component but not passing any props to it.
To pass the prop you would need to write:
new Vue({
vuetify,
render: h => h(Gi, {props: {p1: 'awesome'}})
}).$mount('galeriaimages');
However, I suspect that isn't what you're really trying to do.
You currently seem to be mounting directly to the <galeriaimages> element, which is a bit odd but if you remove the render function it should work. You can also use el instead of $mount:
new Vue({
vuetify,
components: {galeriaimages: Gi},
el: 'galeriaimages'
});
I would add that the reason most examples use a render function for the root Vue instance is that it avoids the need to include the template compiler in the Vue build. This only works if all your other Vue components are pre-built .vue files. If you have any templates at runtime, including those in your HTML, then you'll need to include the template compiler anyway. In that scenario there's no benefit to using a render function on the root instance.
You need to provide the component matching the tag <galeriaimages>. Your custom render function is overriding the template parsing, so it is not parsing the <galeriaimages> as a component tag.
new Vue({vuetify, components: {galeriaimages: Gi} }).$mount('galeriaimages');
Also your components are not creating any elements. They are not able to mount.

How to call all Custom Directive globally instead of calling each component?

I'm setting up a directive for my Vue project on separate individual files.
So far I have succeeded separating files and calling it globally but only possible to do it individually instead of exporting the entire directive and make it global.
**directive.js
const highlight ={
bind(el, binding, vnode) {
... some code
}
const highlight2 ={
... some code
}
export default {
highlight,
highlight
}
**main,js
import Vue from 'vue'
import App from './App.vue'
import * as directive from './directive.js'
Vue.directive(directive);
new Vue({
el: '#app',
render: h => h(App),
directive
})
so far I have been able to call this directive on my main.js but stuck on how to make it globally without calling every single component. like
import { highlight, highlight2} from './directive
Edit:
Found my way by looping through with forEach function.
Object.keys(directive).forEach(function(name){
Vue.directive(name, directive[name])
})

Vue - Passing same data from one component to many component

I have the following code.
main.js
new Vue({
el: '#app',
template: '<App/>',
components: { App }
})
App.vue
<template>
<div id="app">
// this does not pass the data to the component
<basics :resume="resume"></basics>
<education :resume="resume"></education>
// this gets the value from the json file
{{resumeData.name}}
{{resumeData.education}}
</div>
</template>
<script>
import Basics from './components/Basics.vue'
import Education from './components/Education.vue'
import Resume from '../resume.json'
export default {
name: 'app',
data() {
return {
resumeData: Resume
}
},
components: {
Basics,
Education
}
}
</script>
/components/Basics.vue
<template>
<div>
<p>Basics</p>
// this does not get the value from the json file
{{resumeData.name}}
</div>
</template>
/components/Education.vue
<template>
<div>
<p>Education</p>
{{resumeData.education}}
</div>
</template>
How do I pass the data from one component to another such that all the different vue components is reading data from the same json file without inserting the code import Resume from '../resume.json in each component?
I hope you understand my question.
A more common / standard way is just use props. Or you have to import the json in all your components.
If you have many components and really don't want to pass the same prop several times, there is tricky solution: inject the data to the Vue.prototype globally:
Vue.prototype.$resume = {
name: 'foo',
education: 'bar',
...
}
with this, all your components can access it via this.$resume. But use it wisely.
If you have other similar cases, you probably should go for vuex.
In vue.js course you can solve this in 3 different ways.
Use props to pass data in Basics and Education
Vuex
Event box

Using Vueify for components with the runtime-only build in Vue.js

I've been working on porting a vue.js component from vue 1.0 to Vue 2.0 using Vueify. In the Starter resources it states:
When you use vue-loader or vueify to import *.vue files, their parts are automatically compiled into render functions. It is therefore recommended to use the runtime-only build with *.vue files.
However, this does not appear to be the case. If I have a simple component like:
<template>
<div>
{{ msg }}
</div>
</template>
<script type="text/javascript">
export default {
props: {
msg: {
default: "Child Message"
}
}
}
</script>
And in my main.js file I do:
import Vue from 'vue'
import MyComponent from './my-component.vue';
Vue.component('my-component', MyComponent);
new Vue({
el: '#app',
render: function(createElement) {
return createElement(MyComponent)
}
});
Then compile with Gulp using:
browserify('./main.js')
.transform(vueify)
.bundle()
.pipe(fs.createWriteStream("bundle.js"))
I cannot do anything at all with the component except get it to render. In fact, it will actually render the component as soon as it finds the div with the id "app":
<div id="app">
<!-- my-component renders even though I didn't ask it to -->
</div>
And any props added to the component are not received, so:
<div id="app">
<!--
Message displays as default "Child Message" rather than "Parent Message". The prop wasn't passed
-->
<my-component msg="Parent Message"></my-component>
</div>
Similarly, if I add data to main.js, it's not accessible from the web page:
import Vue from 'vue'
import MyComponent from './my-component.vue';
Vue.component('my-component', MyComponent);
new Vue({
el: '#app',
render: function(createElement) {
return createElement(MyComponent)
},
data() {
return {
msg: "Parent Message"
}
}
});
in HTML:
<div id="app">
{{ msg }} // This will not print
</div>
And anything inside "#app" doesn't render at all (remember "my-component" is rendered even if I don't add it):
<div id="app">
<!-- This doesn't render-->
<strong>A component </strong>
<my-component></my-component>
</div>
So it looks to me like you can render a component, without any control over it, and you cant do anything further with the view model, so is it really the case that I should be using the runtime-only build as suggested?
In the end I've used the standalone build using aliasify and everything works fine, but I'd really like to know what it is I am missing when using vueify with the runtime build. Surely the behavior I'm describing isn't what is supposed to happen, so I can only assume I have misunderstood something somewhere.
Doing some tests the problem is in your default render function:
render: function(createElement) {
return createElement(MyComponent)
}
It's overriding the main Vue file's render function and creating a base MyComponent inserting it into the body.
When I removed the render function the prop fired.
jsFiddle Try that, just uncomment the render function to see what I mean.
The render function is meant to replace html templating by allowing you to have more control and utilize other template options.

Categories

Resources