vue js passing data through global event bus not working - vuejs2

Using vue cli I have created a simple vue app with two nested components. I want to pass data between them clicking the h1 tag in my component 1 (a more structured approach suggests to use vuex but this is a very easy app passing simple data using for test).
Clicking the h1 I receive an error but I'm not getting the point. The error says
[Vue warn]: Error in event handler for "titleChanged": "TypeError: Cannot read property 'apply' of undefined"
(found in <Root>)
My code is below
main.js
import Vue from 'vue'
import App from './App.vue'
import Axios from 'axios'
Vue.config.productionTip = false
Vue.prototype.$http = Axios
export const bus = new Vue();
new Vue({
render: h => h(App),
}).$mount('#app')
app.vue
<template>
<div>
<comp-1 v-bind:title="title"></comp-1>
<comp-2 v-bind:title="title"></comp-2>
</div>
</template>
<script>
import comp-1 from './components/Comp-1.vue'
import comp-2 from './components/Comp-2.vue'
export default {
components: {
'comp-1': comp-1,
'comp-2': comp-2
},
data() {
return {
title: "my title"
}
}
}
</script>
<style scoped>
</style>
comp-1.vue
<template>
<header>
<h1 v-on:click="changeTitle">{{ pTitle }}</h1>
</header>
</template>
<script>
import {bus} from '../main'
export default {
props: {
title: {
Type: String
}
},
data() {
return {
pTitle: ''
}
},
created: function() {
this.pTitle = this.title
},
methods: {
changeTitle: function() {
this.pTitle = 'I have changed my title!'
bus.$emit('titleChanged', this.pTitle)
}
}
}
</script>
<style scoped>
</style>
comp-2.vue
<template>
<footer>
<p>{{ title }}</p>
</footer>
</template>
<script>
import {bus} from '../main'
export default {
props: {
title: {
Type: String
}
},
data() {
return {
pTitle: ''
}
},
created() {
this.pTitle = this.title;
bus.$on('titleChanged'), (data) => {
this.title = data
}
}
}
</script>
<style scoped>
</style>

In created of comp-2 component, there is a mistake in event handler
Change it like this:
bus.$on("titleChanged", data => {
this.title = data;
});

Related

Why isn't this list render working in Vue 3?

My App.vue looks like this:
<template>
<HelloWorld />
</template>
<script setup>
import HelloWorld from './components/HelloWorld.vue'
// This starter template is using Vue 3 experimental <script setup> SFCs
// Check out https://github.com/vuejs/rfcs/blob/script-setup-2/active-rfcs/0000-script-setup.md
</script>
As you can see, I've imported the Component HelloWorld.
And HelloWorld.vue looks like this:
<template>
<ul>
<li v-for="item in items" :key="item.message">
{{ item.message }}
</li>
</ul>
<h1>OK</h1>
</template>
<script>
const Viewer = {
data() {
return {
items: [{ message: 'Foo' }, { message: 'Bar' }]}
},
}
</script>
The header element "OK" is rendering, but the list messages are not.
I do not know what that Viewer object is. Your template cannot access the items object.
You can use the script setup :
<script setup>
import { ref } from 'vue'
const items = ref([{ message: 'Foo' }, { message: 'Bar' }])
</script>
or the default script :
<script>
import { ref } from 'vue'
export default {
setup (props) {
const items = ref([{ message: 'Foo' }, { message: 'Bar' }])
return { items }
}
}
</script>
In HelloWorld.vue, your script block should be like
<script setup>
import { ref } from 'vue'
export const items = ref([{ message: 'Foo' }, { message: 'Bar' }])
</script>
As you are using <script setup> syntax, you only need this code to make it works:
<script setup>
import { ref } from 'vue'
const items = ref([{ message: 'Foo' }, { message: 'Bar' }])
</script>
No data or return statements are required

Vuejs build/render component inside a method and output into template

I have a string (example, because it's an object with many key/values, want to loop and append to htmloutput) with a component name. Is it possible to render/build the component inside a method and display the html output?
Is that possible and how can i achieve that?
<template>
<div v-html="htmloutput"></div>
</template>
<script>
export default {
component: {
ComponentTest
},
data() {
return {
htmloutput: ''
}
},
methods:{
makeHtml(){
let string = 'component-test';//ComponentTest
//render the ComponentTest directly
this.htmloutput = ===>'HERE TO RENDER/BUILD THE COMPONENTTEST'<==
}
},
created(){
this.makeHtml();
}
</script>
You might be looking for dynamic components:
https://v2.vuejs.org/v2/guide/components-dynamic-async.html
Example:
<template>
<component :is="changeableComponent">
</component>
</template>
<script>
import FirstComponent from '#/views/first';
import SecondComponent from '#/views/second';
export default {
components: {
FirstComponent, SecondComponent
},
computed: {
changeableComponent() {
// Return 'first-component' or 'second-component' here which corresponds
// to one of the 2 included components.
return 'first-component';
}
}
}
</script>
Maybe this will help - https://forum.vuejs.org/t/how-can-i-get-rendered-html-code-of-vue-component/19421
StarRating is a sample Vue component. You can get it HTML code by run:
new Vue({
...StarRating,
parent: this,
propsData: { /* pass props here*/ }
}).$mount().$el.outerHTML
in Your method. Remember about import Vue from 'vue'; and of course import component.
What you're trying to do really isn't best practice for Vue.
It's better to use v-if and v-for to conditionally render your component in the <template> section.
Yes you can use the render function for that here is an example :
Vue.component('CompnentTest', {
data() {
return {
text: 'some text inside the header'
}
},
render(createElement) {
return createElement('h1', this.text)
}
})
new Vue({
el: '#app',
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<Compnent-test />
</div>
Or :
if you are using Vue-cli :
on your componentTest component :
export default {
render(createElement) {
return createElement('h1', 'sometext')
}
// Same as
// <template>
// <h1>sometext</h1>
// </template>
//
}
and on your root element (App.vue as default) :
export default {
....
component: {
ComponentTest
}
}
<template>
<div>
....
<Component-test />
</div>
</template>
example : codesandbox
you can read more about
Render Functions & JSX

Vuejs DOM not updating if the event is fired from $on method

I'm guessing that it has to do something with Vue not detecting the changes in the tracks object.
mounted(){
Event.$emit('requestCurrentTrack');
Event.$on('currentSong', (data) => this.fetchAlbum(data)); //Data from this method won't output on the screen.
this.fetchAlbum(); // Data from this method out will output to the screen
},
methods:{
fetchAlbum(){
axios.get('/api/album/'+this.id).then((response)=>{
this.album = response.data[1][0];
this.tracks = response.data[0];
this.artistName = this.tracks[0].artist;
});
},
play(data, index){
if(data){
Event.$emit('playTrack', data, index);
}
}
}
You have to learn more about event bus.Take a look to this article
To use event bus take a look below:
In main.js you have to create the event bus
import Vue from 'vue'
import App from './App.vue'
export const eventBus = new Vue() //creating the event bus
new Vue({
el: '#app',
render: h => h(App)
})
A rendered component,lets name it childOne.vue:
<template>
<div>
<button #click="clicked">Click Me</button>
</div>
</template>
<script>
import { eventBus } from '../main'
export default {
name: 'child-one',
methods: {
clicked () {
eventBus.$emit('eventName', 'text passed through event bus') //creating the event with the name eventName and pass a text
}
}
}
</script>
Another rendered component lets name it childTwo.vue
<template>
<div>
<!-- some html here -->
</div>
</template>
<script>
import { eventBus } from '../main'
export default {
name: 'child-two',
created() {
eventBus.$on('eventName', dataPassed => { //listening to event with name eventName
console.log(dataPassed)
})
}
}
</script>
Note the eventBus will work only if your components are rendered.So to make this example to work you can do it by importing the two components in App.vue and registrering them

Property or method "msg" is not defined on the instance but referenced during render

While trying out Vue and Vuex, i stumbled upon the following error message:
[Vue warn]: Property or method "msg" 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.
I fail to understand and solve this issue, mainly because msg is defined in the code under data. It's probably doesn't related directly to Vuex, but i faced it only when i started using Vuex.
Here is my code:
main.js:
import Vue from 'vue'
import App from './App.vue'
import { store } from './store.js'
Vue.component('app', App);
var vApp = new Vue({
el: '#app',
store,
render: h => h(App),
})
App.vue:
<template>
<div id="app">
<div v-text="msg"></div>
<input id="name-b" class="input" v-model="nameB" type="text" placeholder="Name B">
</div>
</template>
<script type = "text/javascript">
module.exports = {
name: 'app',
data() {
return {
msg: 'boooo'
}
},
computed: {
return {
nameB: {
get() {
this.$store.state.nameB
},
set(value) {
this.$store.commit('setName', value);
}
},
}
</script>
<style>
</style>
store.js:
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
export const store = new Vuex.Store({
state: {
nameB: '',
},
mutations: {
setName: function(state, name) { state.locationName = name},
},
});
Thanks.
Problem solved.
It was a curly brackets issue and possibly the return in computed that is not needed...
This is a confusing error message.

How to access an Vue component within another component?

I'm using the single file components, but I can't access a component via another component, follow what I've tried...
<template>
<div id="containerPrincipal" #click.stop="teste">
...
<template>
<script>
/*Other component*/
import flex_div from './elementos/Div.vue'
export default {
name: 'containerPrincipal',
methods : {
teste () {
componente = new flex_div().$mount();
console.log(componente);
}
},
components: {
flex_div
}
}
</script>
Error
_Div2.default is not a constructor
How can I fix this?
To resolve I had to instantiate the Vue again and reference the component...
<template>
<div id="containerPrincipal" #click.stop="teste">
...
<template>
<script>
/*Other component*/
import Vue from 'vue'
import flex_div from './elementos/Div.vue'
let flexdiv = Vue.component('divFlex', flex_div);
export default {
name: 'containerPrincipal',
methods : {
teste () {
let componente = new flexdiv().$mount();
console.log(componente);
}
},
components: {
flexdiv
}
}
</script>
I might be wrong but isn't this what you want to do?
<template><
<div id="containerPrincipal" #click.stop="teste">
<flex-div ref="flex_div"></flex-div>
</div>
<template>
<script>
/*Other component*/
import Vue from 'vue'
import flexdiv from './elementos/Div.vue'
export default {
name: 'containerPrincipal',
methods : {
teste () {
let componente = this.$refs.flex_div
console.log(componente);
}
},
components: {
flexdiv
}
}
</script>