Vue instance's $on method is not work - vue.js

I just created an event bus in the main.js file like this:
main.js
Vue.prototype.$bus = new Vue()
After that, I just wrote some code to test the event bus like this:
TestComponent
<template>
<div>
<div class="account-modal_form">
<form action="" #submit.prevent="formSubmit">
<div class="account-modal_form__group" :class="{ warning: errors.has('password') }">
<div class="account-modal_form__input">
<input name="password" :type="passwordType" placeholder="" class="width-316" v-validate="'required'" v-model="password">
<i class="account-modal_form__viewpass" #click="togglePassword"></i>
</div>
<span class="account-modal_form__warning" v-show="errors.has('password')">
{{ errors.first('password') }}
</span>
</div>
{{ errors }}
<div class="account-modal_form__group">
<button type="submit" class="btn btn--primary btn--large">next</button>
<button type="button" class="btn btn--default" #click="cancelAction">cancel</button>
</div>
</form>
</div>
</div>
</template>
<script>
import { API } from '#/api'
export default {
data() {
return {
passwordType: 'password',
password: ''
}
},
methods: {
created() {
this.$bus.$on('test', () => console.log('test'));
},
nextStep() {
this.$bus.$emit('test');
},
formSubmit() {
this.nextStep();
}
}
}
</script>
When I click submit button I want to submit form first and call nextstep to emit an event, but the $on event output nothing.

You're running $emit before $on, so when you fire the event there are no listeners at that point, and it's better to register your listeners on the component created life cycle event, otherwise whenever you run your test method you'll register a new listener:
Vue.prototype.$bus = new Vue();
Vue.component('spy-component', {
template: '<p>{{this.text}}</p>',
data() {
return {
text: '',
}
},
created() {
this.$bus.$on('sendOriginPassword', (text) => {
this.text = text;
});
}
})
Vue.component('test-component', {
template: '<button #click="test">Click me</button>',
created() {
this.$bus.$on('sendOriginPassword', () => {
console.log('I am listening event')
});
},
methods: {
test() {
this.$bus.$emit('sendOriginPassword', 'Can you hear me?');
}
}
});
new Vue({
el: "#app",
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.min.js"></script>
<div id="app">
<spy-component></spy-component>
<test-component></test-component>
</div>

Related

function not updating vue property

I have this component:
<template>
<div class="hello">
<div>
My prop: {{ myprop }}?
</div>
<div>
<button class="fas fa-lock-open lock" #click="changeText()">Click</button>
</div>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
name: 'StartPage',
props: {
myprop: {
type: String
}
},
model: {
prop: 'myprop',
event: 'click'
},
methods: {
changeText () {
this.$emit('click', 'sometext')
console.log('this.myprop', this.myprop)
}
}
})
</script>
Im using vue v3. Everytime I click on the button, I still see the text "My prop: ?" in the browser.
And in the console I can see: "this.myprop undefined" every time I click on the button.
What am I doing wrong?
As per my understanding, You are trying to update the prop text on click of button from the child component. If Yes, you can achieve it simply by emitting a new text and updating that in the parent component.
Live Demo :
const ShowPropText = {
template: `<div class="hello">
<div>
My prop: {{ myprop }}
</div>
<div>
<button class="fas fa-lock-open lock" #click="changeText()">Click</button>
</div>
</div>`,
props: ['myprop'],
methods: {
changeText() {
this.$emit('click-event', 'sometext')
}
}
}
const app = Vue.createApp({
components: {
'show-prop-text': ShowPropText
},
data() {
return {
text: 'This is default text'
}
},
methods: {
methodCall(e) {
this.text = e;
}
}
})
app.mount('#app')
<script src="https://cdn.jsdelivr.net/npm/vue#next"></script>
<div id="app">
<show-prop-text :myprop="text" #click-event="methodCall"></show-prop-text>
</div>

Get newest data after submit data without page reload using vue script setup + axios

Please kindly help me with this, because I'm new in vue.js.
So, I have 2 Vue files :
Greetinglist.vue, It calls data from api using axios.get
Greeting.vue, It posts data using axios.post
After submit data, how to refresh the data without reload the page ?
I'm using <script setup> tag.
(greetinglist.vue)
<div class="p-2" v-for="(message, index) in messages" :key="index">
<div>
<h3>{{ message.name }}<span>{{message.date}}</span></h3>
<p class="text-slate-300 text-lg">{{ message.greeting }}</p>
</div>
<hr/>
</div>
<script setup>
import {ref, onMounted } from 'vue'
import axios from 'axios'
let messages = ref([]);
function getMessages() {
axios.get('http://127.0.0.1:8000/api/messages')
.then((result) => {
messages.value = result.data
}).catch((err) => {
console.log(err.response.data)
})
}
onMounted(() => {
getMessages()
});
</script>
(Greeting.vue)
<form #submit.prevent="store()">
<div class="">
<input type="text" placeholder="" v-model="messages.name">
<textarea name="" id="" cols="28" rows="10" placeholder="" v-model="messages.greeting"></textarea>
</div>
<button>Submit</button>
</form>
<div>
<GreetingList/>
</div>
<script setup>
import { reactive } from 'vue'
import axios from 'axios'
import GreetingList from './GreetingList.vue'
const messages = reactive({
name: '',
greeting: '',
});
function store() {
axios.post('http://127.0.0.1:8000/api/messages', messages)
.then((result) => {
messages.name = ''
messages.greeting = ''
})
.catch((err) => {
})
}
</script>
You can move getMessages function to greeting.vue, and change greetinglist.vue to use props
greeting.vue
<form #submit.prevent="store()">
<div class="">
<input type="text" placeholder="" v-model="messages.name">
<textarea name="" id="" cols="28" rows="10" placeholder="" v-model="messages.greeting"></textarea>
</div>
<button>Submit</button>
</form>
<div>
<GreetingList :messages="messageList" />
</div>
<script setup>
import { reactive } from 'vue'
import axios from 'axios'
import GreetingList from './GreetingList.vue'
const messages = reactive({
name: '',
greeting: '',
});
let messageList = ref([])
function store() {
axios.post('http://127.0.0.1:8000/api/messages', messages)
.then((result) => {
messages.name = ''
messages.greeting = ''
getMessages()
})
.catch((err) => {
})
}
function getMessages() {
axios.get('http://127.0.0.1:8000/api/messages')
.then((result) => {
messageList.value = result.data
}).catch((err) => {
console.log(err.response.data)
})
}
onMounted(() => {
getMessages()
});
</script>
greetinglist.vue
<script setup>
defineProps(['messages'])
</script>
If I understood your requirement correctly, I am assuming you have both the components in a single page and you want to get the details of newly added greeting from Greeting.vue in the GreetingList.vue reactively (without refreshing the route). If Yes, You can achieve that by calling a getMessages() method on successfully promise of axios.post and then pass the results in the GreetingList.vue.
Demo (I just created it using Vue 2, You can change it accordingly as per Vue 3) :
Vue.component('child', {
// declare the props
props: ['msglist'],
// just like data, the prop can be used inside templates
// and is also made available in the vm as this.message
template: `<div>
<div v-for="(message, index) in msglist" :key="index">
<h3>{{ message.name }}</h3>
<p>{{ message.greeting }}</p>
<hr/>
</div>
</div>`
});
var app = new Vue({
el: '#app',
data: {
messages: {
name: null,
greeting: null
},
// For demo, I am just mock data for initial greeting listing.
messageList: [{
name: 'Alpha',
greeting: 'Hi !'
}, {
name: 'Beta',
greeting: 'Hello !'
}]
},
methods: {
store() {
// Post API call will happen here.
// on success, make a call to get list of greetings.
if (this.messages.name && this.messages.greeting) {
this.getMessages();
}
},
getMessages() {
// Get API call will happen here. For demo, I am just using the mock data and pushing the newly submitted greeting in a exisiting messageList.
this.messageList.push({
name: this.messages.name,
greeting: this.messages.greeting
})
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<input type="text" placeholder="Name" v-model="messages.name"/>
<textarea cols="28" rows="10" placeholder="Greeting" v-model="messages.greeting"></textarea>
<button #click="store">Submit</button>
<child :msglist="messageList">
</child>
</div>
<template>
<div>
<h1>Post component</h1>
<form #submit="submitData" method="post">
<input type="text" placeholder="Enter Title" name="tilte" v-model="userdata.title" /><br />
<br />
<input type="text" placeholder="Enter Author" name="author" v-model="userdata.author" /><br /><br />
<button type="submit">Submit Data</button>
</form>
</div>
</template>
<script>
import axios from "axios";
export default {
name: "postcomponent",
data() {
return {
userdata: {
title: null,
author: null,
},
};
},
methods: {
submitData(e) {
axios.post(
"http://localhost:3000/posts",
this.userdata
).then((result) => {
console.warn(result)
});
e.preventDefault();
},
},
};
</script>

can't display vuejs data property inside vue template

I'm trying to use eventbus to send data from component A:
<template>
<div v-for="(user, index) in users" :key="index" class="col-lg-6">
<div class="card card-primary card-outline">
<div class="card-body d-flex">
<h1 class="mr-auto">{{ user.name }}</h1>
Afficher
</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
users: {},
}
},
methods: {
envoyerDetails($data){
Fire.$emit('envoyer_details_projet', $data);
this.$router.push('details-projet');
},
loadUser() {
if(this.$gate.isAdmin()){
axios.get("api/user").then(({ data }) => (this.users = data.data));
}
}
},
mounted() {
this.loadUser()
}
}
</script>
In component B, i receive the data and i want to display it inside the template this way:
<template>
<div class="right_col text-center" role="main">
<h5><b>name: {{ user.name }}</b> </h5>
</div>
</template>
export default {
data() {
return {
user: {},
}
},
methods: {
afficherDetails (args) {
this.user = args;
console.log(this.user.name);
}
},
mounted() {
Fire.$on('envoyer_details_projet', this.afficheDetails);
}
}
The data is not displayed in the template but it is displayed in the console. What am i missing?
Maybe when you emit the event envoyer_details_projet in component A, but component B is not mounted yet so that it can't receive the data.

Vuejs passing function from parent to child

I have a beginner question about passing function from parent to child. In my example, I want to to use the child more times and sometimes it should to do someting else v-on:focus. How can i do that? There are options to pass it with prop but i don't know how and i think it's not good to do it ? Maybe with EventBus and if yes then how ? I want to know the right way how to do it in VueJs.
Here is the Parent Component:
import Child from "./child.js";
export default {
name: "app",
components: {
Child
},
template: `
<div>
<child></child>
<child></child>
<child></child>
</div>
`
};
And here is the child Component:
export default {
name: "test",
template: `
<div class="form-group">
<div class="input-group">
<input v-on:focus="functionFromChild">
</div>
</div>
`,
methods: {
functionFromChild() {
//run the function from parent
}
}
};
You can pass the function as any other prop
import Child from "./child.js";
export default {
name: "app",
components: {
Child
},
methods: {
calledFromChild(id){
console.log(id)
}
},
template: `
<div>
<child :callback="calledFromChild" id="1"></child>
<child :callback="calledFromChild" id="2"></child>
<child :callback="calledFromChild" id="3"></child>
</div>
`
};
And then in the child
export default {
name: "test",
props: ["callback", "id"],
template: `
<div class="form-group">
<div class="input-group">
<input v-on:focus="() => this.calledFromChild(this.id)">
</div>
</div>
`,
}
I'm also adding an id to the child so you know which child is making the call.
But this is not a good idea. You should use emit from your child to send an event, and listen to it from the parent.
In the child
export default {
name: "test",
template: `
<div class="form-group">
<div class="input-group">
<input v-on:focus="handleFocus">
</div>
</div>
`,
methods: {
handleFocus() {
this.$emit('focusEvent')
}
}
};
And in the parent
<child #focusEvent="handleFocusFromChild"></child>
A working example here
This should work:
const Child = {
template: `
<div class="form-group">
<div class="input-group">
<input v-on:focus="functionFromChild">
</div>
</div>
`,
props: {
functionFromParent: Function
},
methods: {
functionFromChild: function() {
this.functionFromParent();
}
},
data() {
return {
message: 'Oh hai from the component'
}
}
}
const App = {
template: `
<div>
<h1>Quick test</h1>
<p>{{ message }}</p>
<Child :functionFromParent="functionOnParent"/>
<Child :functionFromParent="functionOnParent"/>
<Child :functionFromParent="functionOnParent"/>
</div>
`,
components: {Child},
methods: {
functionOnParent: function(){
console.log("there we go");
}
},
data() {
return {
message: 'Hello'
}
}
}
new Vue({
render: h => h(App),
}).$mount('#app')
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
</div>
If you're trying to call a function in the parent from the child component, then try
this.$parent.parentMethod()
This will invoke the method in parent component.

export default issue with VueJS ES5

I am developing a website, partially in Vue using ES5.
I need to get data from a Vue Child component to be sent back up to the parent, the way explained to me was via using export default {} However I am constantly recieving syntax errors and I am not sure why because from what i see, I am following Mozillas recomendations.
My question component:
var question = Vue.component('question', {
props: {
scenarios: Array,
scenario: Object,
post: Boolean
},
data: function () {
return ({
choice: 0,
counter: 0,
finished: false
});
},
export default {
methods: {
onClickButton: function (event) {
this.$emit('clicked', 'someValue')
},
postResponse: function () {
var formData = new FormData();
formData.append(this.choice);
// POST /someUrl
this.$http.post('Study/PostScenarioChoice', formData).then(response => {
// success callback
}, response => {
// error callback
});
},
activatePost: function () {
if (this.counter < this.scenarios.length) {
this.counter++;
}
else {
this.finished = true;
}
}
}
},
template:
`<div>
<div v-if="this.finished === false" class="row justify-content-center">
<div class="col-lg-6">
<button class="btn btn-light" href="#" v-on:click="this.onClickButton">
<img class="img-fluid" v-bind:src="this.scenarios[this.counter].scenarioLeftImg" />
</button>
</div>
<div class="col-lg-6">
<button class="btn btn-light" href="#" v-on:click="this.onClickButton">
<img class="img-fluid" v-bind:src="this.scenarios[this.counter].scenarioRightImg" />
</button>
</div>
</div>
<finished v-else></finished>
</div >
});
I get the error in the browser console Expected ':' which is on the line export default {
Any assistance will be greatly appreciated.
You have written completely wrong syntax of JavaScript. What you are trying to do here is to put "export default" in the place where object key should go. I will provide correct code here, but I strongly suggest you go and learn the fundamentals of JavaScript including arrays and objects in order to be able to read and write correct and valid JavaScript. Here is some good learning material for beginners:
http://eloquentjavascript.net/
https://maximdenisov.gitbooks.io/you-don-t-know-js/content/
And here is the fixed Vue component:
export default Vue.component("question", {
props: {
scenarios: Array,
scenario: Object,
post: Boolean
},
data: function () {
return ({
choice: 0,
counter: 0,
finished: false
});
},
methods: {
onClickButton: function (event) {
this.$emit("clicked", "someValue");
},
postResponse: function () {
var formData = new FormData();
formData.append(this.choice);
// POST /someUrl
this.$http.post("Study/PostScenarioChoice", formData).then(response => {
// success callback
}, response => {
// error callback
});
},
activatePost: function () {
if (this.counter < this.scenarios.length) {
this.counter++;
}
else {
this.finished = true;
}
}
},
template:
`<div>
<div v-if="this.finished === false" class="row justify-content-center">
<div class="col-lg-6">
<button class="btn btn-light" href="#" v-on:click="this.onClickButton">
<img class="img-fluid" v-bind:src="this.scenarios[this.counter].scenarioLeftImg" />
</button>
</div>
<div class="col-lg-6">
<button class="btn btn-light" href="#" v-on:click="this.onClickButton">
<img class="img-fluid" v-bind:src="this.scenarios[this.counter].scenarioRightImg" />
</button>
</div>
</div>
<finished v-else></finished>
</div>`
});
The actual answer was a simple misunderstanding of the information presented here. The use of export default was irrelevant in my implementation however this was not easily visible until I started noticing the emit posting the parent element later on.
The actual implementation was as follows:
var question = Vue.component('question', {
props: {
scenarios: Array,
scenario: Object,
post: Boolean,
counter: Number
},
data: function () {
return ({
choice: 0,
finished: false
});
},
methods: {
onClickButton: function (event) {
this.$emit('clicked', 'someValue');
},
postResponse: function () {
var formData = new FormData();
formData.append(this.choice);
// POST /someUrl
this.$http.post('Study/PostScenarioChoice', formData).then(response => {
// success callback
}, response => {
// error callback
});
}
},
template:
`<div>
<div v-if="this.finished === false" class="row justify-content-center">
<div class="col-lg-6">
<button class="btn btn-light" href="#" v-on:click="this.onClickButton">
<img class="img-fluid" v-bind:src="this.scenarios[this.counter].scenarioLeftImg" />
</button>
</div>
<div class="col-lg-6">
<button class="btn btn-light" href="#" v-on:click="this.onClickButton">
<img class="img-fluid" v-bind:src="this.scenarios[this.counter].scenarioRightImg" />
</button>
</div>
</div>
<finished v-else></finished>
</div >`
});
The receiving method in the parent element is as follows:
onClickChild: function (value) {
console.log(value) // someValue
this.showTimer = true;
this.countdownTimer();
if (this.counter < this.scenarios.length) {
this.counter++;
}
else {
this.finished = true;
}
}