How to set data to other Component in Vuejs - vue.js

I want to set data from a component to another component with vuejs. Here's my code:
http://quangtri.herokuapp.com/s/ByNN4FE-b
Where am I wrong? Please help me! Thank you very much!

You are using a function in your bus.$on, so this is not what you think it is. Use an arrow function instead and it works.
const bus = new Vue();
Vue.component('coupon', {
data() {
return {
name: 'tri'
}
},
template: `
<div>
<p>{{ name }}</p>
<button type="button" #click="batdau">Go</button>
</div>
`,
methods: {
batdau(name) {
this.name = 'Maria';
}
},
created() {
},
mounted() {
bus.$on('applied', (name) => {
alert(name);
this.name = 'Romeo';
})
}
});
Vue.component('couponmore', {
template: `
<button type="button" #click="nosukien">Let Set Name</button>
`,
methods: {
nosukien() {
bus.$emit('applied', 'John');
}
}
});
new Vue({
el: '#root',
data: {
couponApplied: false
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.3.3/vue.min.js"></script>
<div id="root">
<coupon></coupon>
<couponmore></couponmore>
</div>

That's because in that piece of code, this is window, not the component.
bus.$on('applied', function(name){
alert(name);
this.$data.name ='Romeo';
})
Also you are setting data using this.$data.name instead of simply this.name, I'm not sure about it, but this could result in reactive data issues.
Solution:
If you are using ES6, you can do this:
bus.$on('applied', (name) => {
alert(name);
this.name ='Romeo';
})
This is called arrow function, and when using an arrow function instead of a normal function, the value of this inside the function will be the same as in the parent scope (so, the component instance who contains data).
If you are using vanilla javascript, use .bind(this) at the end:
bus.$on('applied', function (name) {
alert(name);
this.name ='Romeo';
}.bind(this))

Related

How do I trigger a recalculation in a Vue app?

I'm working on a project with Vue and VueX. In my component, I have a calculated method that looks like this:
...mapState([
'watches',
]),
isWatched() {
console.log('check watch');
if (!this.watches) return false;
console.log('iw', this.watches[this.event.id]);
return this.watches[this.event.id] === true;
},
And in my store, I have the following:
addWatch(state, event) {
console.log('add', state.watches);
state.watches = {
...state.watches,
[event]: true,
};
console.log('add2', state.watches);
},
However, this doesn't trigger a recalculation. What's going on?
Try changing return this.watches[this.event.id] === true;
to
return this.$store.commit("addWatch", this.event.id);
The code you have shown is correct, so the problem must be elsewhere.
I assume by 'calculated method' you mean computed property.
Computed properties do not watch their dependencies deeply, but you are updating the store immutably, so that is not the problem.
Here is a bit of sample code to give you the full picture.
Add event numbers until you hit '2', and the isWatched property becomes true.
Vue.use(Vuex);
const mapState = Vuex.mapState;
const store = new Vuex.Store({
state: {
watches: {}
},
mutations: {
addWatch(state, event) {
state.watches = { ...state.watches, [event]: true };
}
}
});
new Vue({
el: "#app",
store,
data: {
numberInput: 0,
event: { id: 2 }
},
methods: {
addNumber(numberInput) {
this.$store.commit("addWatch", Number(numberInput));
}
},
computed: {
...mapState(["watches"]),
isWatched() {
if (!this.watches) return false;
return this.watches[this.event.id] === true;
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vuex/3.1.0/vuex.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div>Watches: {{ watches }}</div>
<div>isWatched: {{ isWatched }}</div>
<br>
<input v-model="numberInput" type="number" />
<button #click="addNumber(numberInput)">
Add new event
</button>
</div>

VueJS Trying to capture enter press on child component before parent component handles enter

I have an auto complete input that when I press enter in this input, it is to emit its value to the parent component and then the parent submit action should be handled. However it appears that the parent is first receiving the enter key submiting the form and then the child component will finally emit the value meaning the data doesnt get updated until after it is needed.
I have an example code pen I made up
codepen
Vue.component('child', {
data () {
return {
someData: ""
}
},
template: `
<div>
<input #keyup.enter.capture="enterPressed" v-model="someData" />
</div>
`,
methods: {
enterPressed(){
this.$emit('updateData',this.someData)
console.log('CHILD: enter pressed')
}
}
});
Vue.component('parent', {
data () {
return {
lastGo: null,
parentData: "init"
}
},
template: `
<form v-on:submit.prevent="go">
<child #updateData="updateData"></child>
<button #click="go">Go</button>
<p>Parent data: <b>{{parentData}}</b></p>
<p>Last go: <b>{{lastGo}}</b></p>
</form>
`,
methods: {
updateData(data){
this.parentData = data;
},
go(){
this.lastGo = this.parentData;
console.log("go: "+this.parentData)
}
}
});
new Vue({
el: '#app'
});
I'm not sure how to resolve this, I feel maybe that my pattern just isn't going to work, is there a better way?
There is a way to work around is using #input event in child component
Vue.component('child', {
data () {
return {
someData: ""
}
},
template: `
<div>
<input #input="onInput" v-model="someData" />
</div>
`,
methods: {
onInput(){
console.log('CHILD: enter pressed')
this.$emit('updateData',this.someData)
}
}
});
Demo

vue.js – get new data information

I'm building a chrome extension using vue.js. In one of my vue components I get tab informations of the current tab and wanna display this information in my template. This is my code:
<template>
<div>
<p>{{ tab.url }}</p>
</div>
</template>
<script>
export default {
data() {
return {
tab: {},
};
},
created: function() {
chrome.tabs.query({ active: true, windowId: chrome.windows.WINDOW_ID_CURRENT }, function(tabs) {
this.tab = tabs[0];
});
},
};
</script>
The Problem is, that the template gets the data before it's filled through the function. What is the best solution for this problem, when the tab data doesn't change after it is set once.
Do I have to use the watched property, although the data is only changed once?
// EDITED:
I've implemented the solution, but it still doesn't work. Here is my code:
<template>
<div>
<div v-if="tabInfo">
<p>set time limit for:</p>
<p>{{ tabInfo.url }}</p>
</div>
<div v-else> loading... </div>
</div>
</template>
<script>
export default {
data() {
return {
tabInfo: null,
};
},
mounted() {
this.getData();
},
methods: {
getData() {
chrome.tabs.query({ active: true, windowId: chrome.windows.WINDOW_ID_CURRENT }, function(tabs) {
console.log(tabs[0]);
this.tabInfo = tabs[0];
});
},
},
};
</script>
The console.log statement in my getData function writes the correct object in the console. But the template only shows the else case (loading...).
// EDIT EDIT
Found the error: I used 'this' in the callback function to reference my data but the context of this inside the callback function is an other one.
So the solution is to use
let self = this;
before the callback function and reference the data with
self.tab
You could initialize tab to null (instead of {}) and use v-if="tabs" in your template, similar to this:
// template
<template>
<div v-if="tab">
{{ tab.label }}
<p>{{ tab.body }}</p>
</div>
</template>
// script
data() {
return {
tab: null,
}
}
new Vue({
el: '#app',
data() {
return {
tab: null,
}
},
mounted() {
this.getData();
},
methods: {
getData() {
fetch('https://reqres.in/api/users/2?delay=1')
.then(resp => resp.json())
.then(user => this.tab = user.data)
.catch(err => console.error(err));
}
}
})
<script src="https://unpkg.com/vue#2.5.17"></script>
<div id="app">
<div v-if="tab">
<img :src="tab.avatar" width="200">
<p>{{tab.first_name}} {{tab.last_name}}</p>
</div>
<div v-else>Loading...</div>
</div>

How to build a bridge of events between different components in Vue?

I need to solve it
1) click mainMidLeft component
2) after clicked, to move slideLeftTop component
http://joxi.ru/ZrJBvERH1JVa8r
The problem I dont quite understand how to do this in right way..
Is it okay to create in mainMidLeft a method where I will do somethik like this:
move: () => {
document.querySelector(`.slideLeftTop`).style.position .....
}
The best practice is to use Vuex State manager with computed methods (getters) and watchers
I have made a working example for you on jsfiddle.
https://jsfiddle.net/n4e_m16/wujafg5e/4/
For more info on how vuex works please go to Here
Please let me know if you need more help :)
const store = new Vuex.Store({
state: {
mainMidLeftState: false,
},
getters: {
mainMidLeftState: state => state.mainMidLeftState,
},
mutations: {
toggleMainMidLeft: (state, payload) => {
state.mainMidLeftState = !state.mainMidLeftState
},
},
})
Vue.component('main-mid-left', {
data() {
return {
}
},
computed: {
mainMidLeftState() {
return this.$store.state.mainMidLeftState
},
},
methods: {
toggleMainMidLeft() {
this.$store.commit('toggleMainMidLeft')
// alert(this.mainMidLeftState)
},
}
})
Vue.component('slide-left-top', {
data() {
return {
}
},
computed: {
mainMidLeftState() {
return this.$store.state.mainMidLeftState
},
},
watch: {
mainMidLeftState: function(val) {
alert("YES, computed property changed and alert have been triggered by watcher in slide top left component")
}
},
methods: {
}
})
const app = new Vue({
el: '#app',
store,
})
div {
color: black;
}
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="https://unpkg.com/vuex#3.0.1/dist/vuex.js"></script>
<div id="app">
<!-- inlining the template to make things easier to read - all of below is held on the component not the root -->
<main-mid-left inline-template>
<div>
<h4>
main mid left
</h4>
<button v-on:click="toggleMainMidLeft()">toggle Main Mid Left State</button>
<div v-show="mainMidLeftState == true">State is true</div>
<div v-show="mainMidLeftState == false">State is false</div>
</div>
</main-mid-left>
<slide-left-top inline-template>
<div>
<h4>
slide left top
</h4>
<div v-show="mainMidLeftState == true">State is true</div>
<div v-show="mainMidLeftState == false">State is false</div>
</div>
</slide-left-top>
</div>
If you don't want to use vuex, you can create a new Vue instance as an event bus (I believe this is mentioned somewhere in the Vue tutorial):
const EventBus = new Vue()
Import EventBus to where you need it and you can send an event by:
EventBus.$emit('event-name', data)
And add the following script in created() of your receiver component:
EventBus.$on('event-name', ($event) => {
// Do something
})
I hope this helps |´・ω・)ノ

How to display data in template from Vue.extend?

Here is my code:
var guestContent = Vue.extend({
template: `
<p>Guest content</p>
`,
data: function () {
return {
qs: getQuestionsContent(); // here I would like to get in qs data from function
}
}
});
Here is my App.js:
App = new Vue ({ /
el: '#app',
data: {
topMenuView: "guestmenu",
contentView: "guestcontent",
}
});
I need to display qs instead of Guest content and do for example v-for or some kind of other iteration. How can I do it?
Why:
var guestContent = Vue.extend({
template: `
<p>Guest content</p>
<p>{{foo}}</p> // foo not display
`,
data: function () {
foo: "dddddddddd";
}
});
Why is this not working?
Try something like this:
html
<body>
<guest-content></guest-content>
</body>
<template id="guest-content-template">
Guest Content
<ul>
<li v-for="question in questions">
{{question}}
</li>
</ul>
</template>
js
var guestContent = Vue.extend({
template: "#guest-content-template",
data: function (){
return {
questions: []
}
},
ready(){
this.getQuestionsContent();
},
methods:{
getQuestionsContent(){
this.questions = [
"Why?",
"What?",
"When?"
];
}
}
});
Vue.component('guest-content',guestContent);
new Vue({
el:'body'
});
Anything in a {{}} is assumed to be within the current Vue scope, so you just use foo or questions not guestContent.foo.
The data attribute is a function that has to return the data for the app. If you don't return anything, the component wont have any data.