Data transfer problem between two siblings components (Vue js) - javascript

I have three components and one of those is the parent of the others I'm trying to pass an object called talk between siblings emiting it inside an event from FollowedBrowser to LeftBar and then passing it via prop from LeftBar to TalksList component, after that another event is emited by TalksList and listened one more time for LeftBar and finally this component redefine the talk object as an empty object.
This is my parent component LeftBar.
<template>
<v-navigation-drawer width="25%" permanent clipped app light>
<talks-list v-if="inRoute('messages')" :talk="talk" #talkAdded="talkAdded()"/>
<template v-if="inRoute('messages')" v-slot:prepend>
<followed-browser #newTalk="addTalk($event)"/>
</template>
</v-navigation-drawer>
</template>
<script>
import FollowedBrowser from "./FollowedBrowser";
import TalksList from "./TalksList";
import { mapGetters } from "vuex";
export default {
data(){
return {
talk: {}
}
},
components: {
FollowedBrowser,
TalksList
},
methods: {
addTalk(talk){
this.talk = talk;
},
talkAdded(){
this.talk = {};
}
}
}
</script>
And this is my two children:
TalksList.vue
<template>
<v-container class="my-0 px-5">
<v-list flat>
<v-list-item-group class="my-0">
<div class="ma-0 pa-0" v-for="(talk, index) in talks" :key="index">
<v-divider v-if="talk.divider"></v-divider>
<v-list-item v-else class="px-2" style="cursor: pointer">
<template>
<v-list-item-avatar>
<v-img :src="correctedImageUrl(talk.recipient)"></v-img>
</v-list-item-avatar>
<v-list-item-content>
<v-list-item-title>
<span class="blue--text text--lighten-1">{{ completeName(talk.recipient) }}</span>
</v-list-item-title>
<v-list-item-subtitle>
<span>{{ talk.recipient.username }}</span>
</v-list-item-subtitle>
</v-list-item-content>
</template>
</v-list-item>
</div>
</v-list-item-group>
</v-list>
</v-container>
</template>
<script>
import axios from "axios";
export default {
data(){
return {
talks: []
}
},
props: {
talk: {
type: Object,
default: null,
required: true
}
},
watch: {
talk(val){
if(val){
this.talks.splice(0, 1, val);
this.$emit("talkAdded");
}
}
}
}
</script>
FollowedBrowsed.vue
<template>
<div style="display: inline">
<v-dialog scrollable v-model="dialog" max-width="400px" max-height="500px">
<v-card :loading="loading">
<v-text-field dense outlined color="blue lighten-1" label="Nombre de usuario" class="px-5" append-icon="mdi-magnify" v-model="browsedUsername"/>
<v-divider></v-divider>
<v-card-text style="height: 300px;" class="px-2">
<v-list>
<v-list-item class="px-2" style="cursor: pointer" v-for="listUser in filteredFollowed" :key="listUser.id" #click.prevent="newTalk(listUser)">
<v-list-item-content>
<v-list-item-title>
<span class="blue--text text--lighten-1">{{ completeName(listUser) }}</span>
</v-list-item-title>
<v-list-item-subtitle>
<span>{{ listUser.username }}</span>
</v-list-item-subtitle>
</v-list-item-content>
</v-list-item>
</v-list>
</v-card-text>
</v-card>
</v-dialog>
</div>
</template>
<script>
import { mapGetters } from "vuex";
import axios from "axios";
export default {
data(){
return {
browsedUsername: "",
loading: false,
dialog: false,
skeleton: true,
followed: []
}
},
watch: {
dialog(dialog){
if(!dialog){
this.browsedUsername = "";
this.item = null;
}
}
},
computed: {
...mapGetters({
authenticated: "auth/authenticated",
user: "auth/user"
}),
filteredFollowed(){
return this.followed.filter((user) => {
return user.username.toLowerCase().indexOf(this.browsedUsername.toLowerCase()) !== -1;
})
}
},
mounted(){
axios.get("all_followers_followed/followed")
.then((response) => {
if(response.data){
this.followed = response.data;
this.skeleton = false;
}
})
.catch((error) => {
console.log(error)
});
},
methods: {
async newTalk(user){
this.loading = "blue lighten-1";
await axios.post("messages/new_talk", {recipient_id: user.id})
.then((response) => {
if(response.data){
this.dialog = false;
this.$emit("newTalk", {
messages_number: 0,
recipient: user,
sender: this.user
});
}
})
.catch((error) => {
console.log(error);
});
}
}
}
When the newTalk method is called inside FollowedBrowser component newTalk event is emited but after that my screen freezes like the app was inside infinite loop and I don't know why. I omitted some code that I thought was irrelevant.
Can anybody help me.
Thanks in advance.

I solved... So simple, I just had to get a copy of talk prop inside TalksList, inside watch just put this:
watch: {
talk(val){
if(val){
if(this.talks.length){
this.talks.unshift({ divider: true });
}
let buffer = new Object();
let talk = new Object();
buffer.data = val;
talk = buffer.data;
this.talks.unshift(talk);
}
}
},

Related

After calling add method from vuex second argument doesn't apply

I create vuex with addNews method Inside of it and pass two arguments, title and body of item. But after calling that method inside my component it only prints my first argument title in HTML. Also i try to console.log body and i get data
This is my method in vuex
async addNews({ commit }, title, body) {
const response = await axios.post(
`https://jsonplaceholder.typicode.com/posts`,
{ title: title, body: body }
);
commit("addNews", response.data);
},
And here is component where I call it
<template>
<div class="text-center">
<v-dialog v-model="dialog" width="500">
<template v-slot:activator="{ on, attrs }">
<v-btn class="mb-5" fab dark color="primary" v-bind="attrs" v-on="on">
<v-icon dark> mdi-plus </v-icon>
</v-btn>
</template>
<v-card>
<v-card-title class="text-h5 blue lighten-2">
ADD NEW POST
</v-card-title>
<v-text-field v-model="title" required></v-text-field>
<v-text-field v-model="body" required></v-text-field>
<v-divider></v-divider>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn
color="primary"
class="mr-4"
#click="
onSubmit();
dialog = false;
"
>
Do It
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</div>
</template>
<script>
import { mapActions } from "vuex";
export default {
name: "AddNews",
data() {
return {
title: "",
body: "",
dialog: false,
};
},
methods: {
...mapActions(["addNews"]),
onSubmit() {
this.addNews(this.title, this.body);
this.title = "";
this.body = "";
},
},
};
</script>
Try passing an object with arguments to your action:
async addNews({ commit }, { title, body }) {
const response = await axios.post(
`https://jsonplaceholder.typicode.com/posts`,
{ title, body }
);
commit("addNews", response.data);
},

Pass $route as parameter in axios post request

So I have a vue project with a dashboard that contains many tests and i want to pass the test name as a parameter in an axios request when the user clicks on a button and gets redirected on another page.
I already did the first part and passed the name of the test in route as a parameter name and now i'm trying to fetch the corresponding item in the collection.When using $route.params.name I get an error that says $route is not defined
here's my code so far
<template>
<v-app>
<app-navbar />
<v-main>
<div class="text-center">
<h3>
test {{ $route.params.name }}, {{ $route.query.status }},{{
$route.query.tag
}}
</h3>
<h3 v-if="this.loadAPI">{{failreason()}}</h3>
</div>
<v-data-table
:headers="headers"
:items="imagesref"
:items-per-page="5"
class="elevation-1"
>
<template v-slot:[`item.index`]="{ index }">
{{index+1}}
</template>
<template v-slot:[`item.status`]="{ index }">
{{imagesresult[index][2]}}
</template>
<template v-slot:[`item.ref`]="{ index }">
<v-img :src="imagesref[index]" max-width="750" max-height="750" #click="expref[index] = !expref[index]"/>
<v-overlay :value="expref[index]"><v-img :src="imagesref[index]" max-width="1300" max-height="900" #click="expref[index] = !expref[index]"/> </v-overlay>
</template>
<template v-slot:[`item.test`]="{ index }">
<v-img :src="imagestest[index]" max-width="750" max-height="750" #click="exptest[index] = !exptest[index]"/>
<v-overlay :value="exptest[index]"><v-img :src="imagestest[index]" max-width="1300" max-height="900" #click="exptest[index] = !exptest[index]"/> </v-overlay>
</template>
<template v-slot:[`item.res`]="{ index }">
<v-img :src="imagesresult[index][0]" max-width="750" max-height="750" #click="expres[index] = !expres[index]"/>
<v-overlay :value="expres[index]"><v-img :src="imagesresult[index][0]" max-width="1300" max-height="900" #click="expres[index] = !expres[index]"/> </v-overlay>
</template>
<template #[`item.mis`]="{ index }">
{{Math.round(imagesresult[index][1]*100)/100}}
</template>
<template #[`item.Scrubber`]="{ index }">
<nuxt-link :to="{ path: 'scrubber', query: { imageref: imagesref[index],imagetest:imagestest[index],imageres:imagesresult[index] }}">Show Scrubber</nuxt-link>
</template>
</v-data-table>
</v-main>
</v-app>
</template>
<script>
import appNavbar from "../../../components/appNavbar.vue"
import axios from "axios"
export default {
components: { appNavbar },
name: "App",
data() {
return {
loadAPI:false,
dialog:false,
expref:[],
exptest:[],
expres:[],
items: [],
imagesref: [],
imagestest: [],
imagesresult: [],
headers: [
{ text: 'index',value: 'index',sortable:false},
{ text: 'Status',value: 'status',sortable:false},
{ text: 'Imagesref', value: 'ref',sortable:false },
{ text: 'Imagestest', value: 'test',sortable:false },
{ text: 'Imagesresult', value: 'res',sortable:false },
{ text: 'Mismatch percent', value: 'mis',sortable:false },
{ text: 'Scrubber', value: 'Scrubber',sortable:false },
]
}
},
async created() {
try {
const res = await axios({
method: 'post',
url: 'http://localhost:3002/backend/gettestbyname',
data: {name: $route.params.name}
})
this.items = res.data.data;
this.imagesref = res.data.data[0].refimages;
this.imagestest = res.data.data[0].testimages;
this.imagesresult = res.data.data[0].testresults;
for (let i of this.imagesref){
this.expref.push(false);
this.exptest.push(false);
this.expres.push(false);
}
this.loadAPI=true;
} catch (error) {
console.log(error);
}
},
methods:{
failreason()
{
if (this.items[0].status=="failed"){
let index=0;
for (let i of this.items[0].testresults)
{ console.log(i);
index++;
if (i[2]=="failed")
{
return 'Visual test failed at step number '+index;
}
}
return 'Test set missing screenshots';
}
}
}
}
</script>
<style scoped>
</style>
Globally injected properties in Vue are available from the Vue context, not the global javascript context (like window).
So, in the <script> tag, you have to use this.$router to access it.
In your created hook:
// replace
data: {name: $route.params.name}
//by
data: {name: this.$route.params.name}
From the vue-router docs:
By calling app.use(router), we get access to it as this.$router as well as the current route as this.$route inside of any component:

Two way data binding in Vue: Unable to update the Parent component from the child component

I got the following two components:
Parent:
<template>
<v-container>
<v-row class="text-center">
<v-col cols="12" class="parent">
<p>Ich bin der Parent component</p>
<button #click="changeDetail" :name.sync="name">Change Details</button>
<Child v-bind:name="name"></Child>
</v-col>
</v-row>
</v-container>
</template>
<script>
import Child from "./Child";
export default {
name: "Parent",
data: () => ({
name: "test"
}),
methods: {
changeDetail() {
this.name = "Updated from Parent";
}
},
components: {
Child
}
};
</script>
Child:
<template>
<v-container>
<v-row class="text-center">
<v-col cols="12">
<p>My name is: {{ name}}</p>
<button #click="resetname">Reset the name</button>
</v-col>
</v-row>
</v-container>
</template>
<script>
export default {
// props: ["name"],
props: {
name: {
type: String,
required: true
}
},
data: () => ({
newname: "Updated from Child"
}),
methods: {
resetname() {
this.$emit("update:name", this.newname);
}
}
};
</script>
As far as I read here: https://v2.vuejs.org/v2/guide/components-custom-events.html#sync-Modifier, I should use update and sync to pass props from the child back to the parent. However it does not work. I don´t understand what´s wrong here. What am I missing?
It is usually best to not bind your template to the prop but a computed property instead to ensure the data is accessed and modified externally. It will also simplify your code a bit so that you don't have to trigger updates.
Parent:
<template>
<v-container>
<v-row class="text-center">
<v-col cols="12" class="parent">
<p>Ich bin der Parent component</p>
<button #click="changeDetail">Change Details</button>
<Child v-bind:name.sync="name"></Child>
</v-col>
</v-row>
</v-container>
</template>
<script>
import Child from "./Child";
export default {
name: "Parent",
data: () => ({
name: "test"
}),
methods: {
changeDetail() {
this.name = "Updated from Parent";
}
},
components: {
Child
}
};
</script>
Child:
<template>
<v-container>
<v-row class="text-center">
<v-col cols="12">
<p>My name is: {{ currentName }}</p>
<button #click="resetname">Reset the name</button>
</v-col>
</v-row>
</v-container>
</template>
<script>
export default {
// props: ["name"],
props: {
name: {
type: String,
required: true
}
},
data: () => ({
//Be careful with fat arrow functions for data
//Because the value of *this* isn't the component,
//but rather the parent scope.
}),
computed: {
currentName: {
get() { return this.name },
set(value) { this.$emit("update:name", value); }
}
},
methods: {
resetname() {
this.currentName = "updated from child";
}
}
};
</script>

Vue Application Using Chartkick Doesn't update chart correctly

I know my mount and computed methods work since I can send that out to be displayed, but my graph only renders after I change the data value in the template to the correct thing that one time and doesn't render it again. Should I be using method so it calculates it everytime or do I need to use Vuex for this issue? Thanks
<template>
<v-app>
{{getDates}}
<v-card class="mt-3 mx-auto" max-width="500">
<v-sheet
class="v-sheet--offset mx-auto"
color="cyan"
elevation="12"
min-width="125%"
max-width="calc(100% - 32px)"
>
<line-chart :data="dates"></line-chart>
</v-sheet>
<v-card-text class="pt-0">
<div class="title font-weight-light mb-2">Lead</div>
<div class="subheading font-weight-light grey--text">TEST TEXT</div>
<v-divider class="my-2"></v-divider>
<v-icon class="mr-2" small>mdi-clock</v-icon>
<span class="caption grey--text font-weight-light">TEST TEXT</span>
</v-card-text>
</v-card>
<p>108J View</p>
</v-app>
</template>
<script>
import axios from "axios";
export default {
name: "E108J",
components: {},
data() {
return {
value: [246, 446, 675, 510, 590, 610, 760],
datas: [],
dates: {},
lead: [],
datesnlead: {}
};
},
mounted() {
axios
.get("http://localhost:3000/E108J")
.then(response => (this.datas = response.data));
},
computed: {
getDates() {
for (let i = 0; i < this.datas.length; i++) {
this.dates[this.datas[i].date] = this.datas[i].lead;
}
return this.dates;
},
getLead() {
for (let i = 0; i < this.datas.length; i++) {
this.lead[i] = this.datas[i].lead;
}
return this.lead;
}
}
};
</script>

How to access a mapAction method using Vuejs?

Regards,
I would like to know how I can access the logOut function that comes from mapActions,
What I do not understand is how it is possible, that with the logIn function when I refer to it, it works for me but with the function of logOut no.
Here is the error: Uncaught ReferenceError: logOut is not defined
Here I leave the code:
<template>
<div class="home">
<v-card height="200px" flat dark app>
<div class="headline text-xs-center pa-5">
Active: {{ bottomNav }}
</div>
<v-bottom-nav :active.sync="bottomNav" :value="true" absolute color="transparent">
<v-btn color="teal" flat value="messages">
<span>Messages</span>
<v-icon>chat</v-icon>
</v-btn>
<v-btn color="teal" flat value="notifications">
<span>Notifications</span>
<v-icon>notifications</v-icon>
</v-btn>
<v-btn color="red" #click.prevent="logIn()" flat value="logIn">
<span>LogIn</span>
<v-icon>whatshot</v-icon>
</v-btn>
<v-menu offset-y origin="center center" :nudge-bottom="10" transition="scale-transition">
<v-btn icon large flat slot="activator">
<v-avatar size="30px">
<img src="../assets/logo.png" alt="user-logo"/>
</v-avatar>
</v-btn>
<v-list class="pa-0">
<v-list-tile v-for="(item , index) in items"
:to="!item.href ? { name: item.name } : null"
:href="item.href"
#click.prevent="item.click"
ripple="ripple"
:disabled="item.disabled"
:target="item.target"
rel="noopener"
:key="index"
ref="myBtn"
>
<v-list-tile-action v-if="item.icon">
<v-icon>{{ item.icon }}</v-icon>
</v-list-tile-action>
<v-list-tile-content>
<v-list-tile-title>{{ item.title }}</v-list-tile-title>
</v-list-tile-content>
</v-list-tile>
</v-list>
</v-menu>
</v-bottom-nav>
</v-card>
<script>
import { mapActions, mapState } from 'vuex';
export default {
name: 'home',
computed: mapState(['data' , 'loading']),
data(){
return{
bottomNav: 'LogIn',
items: [
{
icon: 'account_circle',
href: '#',
title: 'Profile',
click: ''
},
{
icon: 'settings',
href: '#',
title: 'Settings',
click: ''
},
{
icon: 'fullscreen_exit',
href: '#',
title: 'Logout',
click: () =>{
this.logOut();
}
}
]
}
},
methods:{
...mapActions(['logIn' , 'logOut']),
},
}
</script>
Actions.js
Im getting this error: Uncaught TypeError: Cannot set property 'data' of undefined this error is pointing to the mutation file in the UPDATE_DATA.
import { GROUP_ID } from '../config/env';
export const actions = {
logIn({state , commit}){
FB.login((res) =>{
console.log(res);
if(res.status == "connected"){
state.user.id = res.authResponse.userID;
state.user.accessToken = res.authResponse.accessToken;
console.log("user:%s\ntoken:%s" , state.user.id , state.user.accessToken);
FB.api('/me' , (res) =>{
this.user.username = res.authResponse.name;
});
FB.api(`/${GROUP_ID}/feed`,'GET', (res) =>{
if(res && !res.error){
res['data'].forEach((data) =>{
console.log("api connection => " , data);
commit('UPDATE_DATA' , data);
commit('IS_LOADING_DATA' , false);
});
}
});
}
},{scope:'public_profile, email, groups_access_member_info'});
},
logOut({state}){
console.log(state.user.accessToken);
try{
if(FB.getAccessToken() != null) {
FB.logout(function(res) {
console.log("User is logged out");
console.log(res);
});
}else{
console.log("User is not logged in");
}
}catch(err){
console.log(err);
}
}
}
Mutations.js
export const mutations = {
UPDATE_DATA({state} , payload){
state.data = payload;
},
IS_LOADING_DATA({state} , payload){
state.loading = payload;
}
}
If you change to use a non-arrow function in your data click, you'll have access to the correct Vue instance. By using an arrow function, this is not the Vue instance you're expecting and this.logOut will be undefined.

Categories

Resources