I have two components:
component 1: recherche
<template>
<button #click='allRecords()'>Search</button>
<table>
<thead>
<th class="center">Date</th>
<th class="center">Statut</th>
</thead>
<tbody>
<tr v-for='contact in contacts' #click="seeContactDetails(contact.data.bid)">
<td>{{ contact.date }}</td>
<td>{{ contact.data.statut }}</td>
</tr>
</tbody>
</table>
</template>
<script lang="js">
import axios from 'axios';
export default {
name: 'recherche',
components: {
},
props: [],
mounted() {
},
data() {
return {
contacts: [],
details:[],
}
},
methods: {
allRecords: function() {
axios.get(`/api/recherche/`)
.then(response => {
this.contacts = response.data.list;
})
.catch(error => {
console.log(error);
});
},
seeContactDetails: (bid) => {
window.location = `/#/detail/${bid}`;
axios.get(`/api/detail/contacts?bid=${bid}`)
.then(response => {
this.details = response.data.list;
})
.catch(error => {
console.log(error);
});
}
},
}
</script>
But I want to display the result of seeContactDetails (bid) in the detail component
Component 2: detail
<template lang="html">
<div v-for='detail in details'>
<!-- ligne -->
<div class="row">
<!-- colonne -->
<div class="col-md-2">org: {{detail.data.org}}</div>
<div class="col-md-2">num:{{detail.data.num}} </div>
</div>
</template>
<script lang="js">
export default {
name: 'detail',
props: ['recherche'],
mounted() {},
data() {
return {
details: []
}
},
methods: {
},
computed: {
}
}
</script>
In summary I want to read the resuts of an axios query, in another component, but I do not know how we do, despite the docs.
I try to put the name of the component recherche in props of detail but it does not work
The way I would recommend doing this (for most cases) is to use vuex.
These are the files I would use
vuex (store, actions, mutators, getters)
apiHandler (a utility function making the actual call)
component 1
component 2
After some action on component 1, a vuex action is triggered. Inside the vuex action the appropriate apiHandler function is invoked. Inside the vuex action the response is handled with a then(), which pushes the response into the vuex mutator. This mutation will update the getter which will update any component that is listening to the change in state.
It's a bit more complex than having the components talk directly, but it makes the architecture scalable.
Related
datalist.js
import axios from "axios";
export const datalist = () => {
return axios.get("myapiurl/name...").then((response) => response);
};
HelloWorld.vue
<template>
<div>
<div v-for="item in items" :key="item.DttID">
<router-link
:to="{
name: 'UserWithID',
params: { id: item.DepaD },
query: { DepaD: item.DepaID },
}"
>
<div class="bt-color">{{ item.DepaName }}</div>
</router-link>
</div>
<br /><br /><br />
<User />
</div>
</template>
<script>
import User from "./User.vue";
import { datalist } from "./datalist";
export default {
name: "HelloWorld",
components: {
User,
},
data() {
return {
items: datalist,
};
},
mounted() {
datalist().then((r) => {
this.items = r.data;
});
},
};
</script>
User.vue
<template>
<div>
<div v-for="(item, key) in user" :key="key">
{{ item.Accv }}
</div>
</div>
</template>
<script>
import { datalist } from "./datalist";
export default {
name: "User",
data() {
return {
lists: datalist,
};
},
computed: {
user: function () {
return this.lists.filter((item) => {
if (item.DepaD === this.$route.params.id) {
return item;
}
});
},
},
};
</script>
Error with the code is,
[Vue warn]: Error in render: "TypeError: this.lists.filter is not a function"
TypeError: this.lists.filter is not a function
The above error i am getting in User.vue component in the line number '20'
From the api which is in, datalist.js file, i think i am not fetching data correctly. or in the list filter there is problem in User.vue?
Try to change the following
HelloWorld.vue
data() {
return {
items: [],
};
},
mounted() {
datalist().then((r) => {
this.items = r.data;
});
},
User.vue
data() {
return {
lists: []
};
},
mounted() {
datalist().then((r) => {
this.lists = r.data;
});
},
At least this suppress the error, but i cant tell more based on your snippet since there are network issues :)
Since your datalist function returns a Promise, you need to wait for it to complete. To do this, simply modify your component code as follows:
import { datalist } from "./datalist";
export default {
name: "User",
data() {
return {
// empty array on initialization
lists: [],
};
},
computed: {
user: function() {
return this.lists.filter((item) => {
if (item.DeploymentID === this.$route.params.id) {
return item;
}
});
},
},
// asynchronous function - because internally we are waiting for datalist() to complete
async-mounted() {
this.users = await datalist() // or datalist().then(res => this.users = res) - then async is not needed
}
};
now there will be no errors when initializing the component, since initially lists is an empty array but after executing the request it will turn into what you need.
You may define any functions and import them, but they wont affect until you call them, in this case we have datalist function imported in both HelloWorld and User component, but it did not been called in User component. so your code:
data() {
return {
lists: datalist,
};
},
cause lists to be equal to datalist that is a function, no an array! where .filter() should be used after an array, not a function! that is the reason of error.
thus you should call function datalist and put it's response in lists instead of putting datalist itself in lists
Extra:
it is better to call axios inside the component, in mounted, created or ...
it is not good idea to call an axios command twice, can call it in HelloWorl component and pass it to User component via props
So i have a Vuex Store setup which returns me headers and desserts. Inside my desserts i have a property setup named display which is initially false. I have a component Row which i am importing inside my parent component named Table. Row Components accepts a couple of props i.e. Name and Display. The display prop is what is returned from desserts from the vuex store. I am trying to add a mutation such that on Click of the icon in my Row component, the display.laptop can be toggled to true and false. I have setup the toggleLaptopDisplay but i keep getting cannot read property laptop of undefined.
Please look at the complete CodeSandbox.
Here is the complete code:
The Vuex Store:-
export default new Vuex.Store({
state: {
headers: [{ text: "Dessert (100g serving)", value: "name" }],
desserts: [
{ name: "Lollipop", display: { laptop: true } },
{ name: "Marshamallow", display: { laptop: false } }
]
},
getters: {
getHeaders: state => state.headers,
getDesserts: state => state.desserts
},
mutations: {
toggleLaptopDisplay(state) {
state.desserts.display.laptop = !state.desserts.display.laptop;
}
}
});
This is the Table Component:-
<template>
<v-data-table :headers="getHeaders" :items="getDesserts" hide-actions select-all item-key="name">
<template v-slot:headers="props">
<tr>
<th v-for="header in props.headers" :key="header.text">{{ header.text }}</th>
</tr>
</template>
<template v-slot:items="props">
<tr>
<Row :name="props.item.name" :display="props.item.display"/>
</tr>
</template>
</v-data-table>
</template>
<script>
import { mapGetters } from "vuex";
import Row from "./Row";
export default {
components: {
Row
},
data() {
return {};
},
computed: {
...mapGetters({
getHeaders: "getHeaders",
getDesserts: "getDesserts"
})
}
};
</script>
This is the Row component:-
<template>
<div>
{{name}}
<v-icon #click="toggleLaptopDisplay" :color="display.laptop ? 'info': '' ">smartphone</v-icon>
</div>
</template>
<script>
import { mapMutations } from "vuex";
export default {
props: {
name: String,
display: Object
},
methods: {
...mapMutations({
toggleLaptopDisplay: "toggleLaptopDisplay"
})
}
};
</script>
Any help will be appreciated. Thank you :)
Few things to achieve what you want:
In your Row.vue add a method to select appropriate element:
<v-icon #click="toggleChange" :color="display.laptop ? 'info': '' ">smartphone</v-icon>
then in methods, create a method that will pass name of the element as payload:
methods: {
toggleChange() {
this.toggleLaptopDisplay(this.name)
},
...mapMutations({
toggleLaptopDisplay: "toggleLaptopDisplay"
})
}
finally, in store.js, use the payload and mutate selected element:
mutations: {
toggleLaptopDisplay(state, payload) {
state.desserts.find(dessert => dessert.name === payload).display.laptop = !state.desserts.find(dessert => dessert.name === payload).display.laptop
}
}
Edited Example
I was wondering how can I move my items -> book, from one component to another. I took this books from api and I show them in the list, so I have an ID.
<template>
<v-flex v-for="(book, index) in allBooks">
<div>Title: {{ book.title }}</div>
<i #click="markAsFavorite(book)" :class="{isActive: isMark}" class="fas fa-heart"></i>
</template>
//component books
<script>
name: 'Books',
data () {
return {
allBooks: [],
isMark: false,
favouriteBooks: []
}
},
mounted() {
axios.get("https://www.googleapis.com/books/v1/volumes?q=id:" + this.id)
.then(response => {
console.log(response)
this.allBooks = response.data.items.map(item => ({...item, isMark: false}))
console.log(this.allBooks)
})
.catch(error => {
console.log(error)
})
},
methods: {
markAsFavorite(book) {
this.isMark = !this.isMark
let favouriteAllBooks = this.favouriteBooks.push(book => {
book.id = // i dont know what?
})
},
}
</script>
//component favourite
<template>
<div class=showFavouriteBook>
<p></p>
</div>
</template>
I tried to compare this marked book ID to something, and then this array with marked books show in second template favourite. But I have no idea how to do this. Maybe somebody can prompt me something?
You should use a global eventBus for that. An 'eventBus' is another instance of Vue which is used to pass data via components tied to the main application.
At the root script of your application append the following:
const eventBus = new Vue({
data: function() {
return {
some_var: null,
}
}
});
You can use Vue mixin to have your event bus accessible globally easily:
Vue.mixin({
data: function() {
return {
eventBus: eventBus,
}
}
});
Now when you want to pass data between components you can use the bus:
Component 1
// for the sake of demo I'll use mounted method, which is invoked each time component is mounted
<script>
export default {
mounted: function() {
this.eventBus.some_var = 'it works!'
}
}
</script>
Component 2
<template>
<div>
{{ eventBus.some_var }} <!-- it works -->
</div>
</template>
In addition you can use $emit and $on.
Component 1
// for the sake of demo I'll use mounted method, which is invoked each time component is mounted
<script>
export default {
mounted: function() {
// emit 'emittedVarValue' event with parameter 'it works'
this.eventBus.$emit('emittedVarValue', 'it works!')
}
}
</script>
Component 2
<template>
<div>
{{ some_var }} <!-- "it works" once eventBus receives event "emittedVarValue" -->
</div>
</template>
<script>
export default {
data: function() {
return {
some_var: null
}
},
mounted: function() {
// waiting for "emittedVarValue" event
this.eventBus.$on('emittedVarValue', (data)=>{
this.some_var = data;
})
}
}
</script>
Hope this answer helps you.
Good day developers ... I'm trying to fill a data table dynamically using information already fetched through a fetch and previously stored in a variable in my vuex instance and now called as computed property in my applets say I have this:
<script>
import { mapActions, mapGetters } from "vuex";
export default {
name: "Games",
data() {
return {
search: "",
headers:[
{text:'Game#', value:'Game#'},
{text:'Players in Game',value:'Players inGame'},
{text:'Permissions',value:'Permissions'},
{text:'Results',value:'Results'},
],
};
},
components: {
},
props: ["gameid"],
methods: {
...mapActions(["fetchingJsonEvents", "joinToGame","logOut", "createGame"]),
},
computed: {
...mapGetters(["getGamesAll", "getUserLogged"]),
getGamesAll(){
return this.$store.getters.getGamesAll.games-------->here is where the json is stored
}
},
created() {
this.fetchingJsonEvents();
}
};
</script>
and my html tag calling this computed and all else is like this:
<v-data-table :search="search" :headers="headers" v-bind:items="getGamesAll">
<template>
<tbody>
<tr v-for="(general, index) in getGamesAll.games" v-bind:key="index">
<td>Game {{general.id}}:</td>
<td>xxxxxxxx</td>
<td>xxxxxxxx</td>
</tbody>
</template>
</v-data-table>
but the table doesn't show any result , it worked with a v-simple-table, but once i try to do it in this way it doesn't....
Any advice .....thanks in advance
this is unnecessary:
getGamesAll(){
return this.$store.getters.getGamesAll.games
}
because you already have it using mapGetters:
...mapGetters(["getGamesAll", "getUserLogged"]),
if your getter getGamesAll have data then this should simply work:
<tr v-for="(general, index) in getGamesAll.games" v-bind:key="index">
I have two components, a parent and child one, the
parent data attribute I've set up it like this...
data () {
return {
users: [],
}
}
the users array is populated by a button click, i share this array with the child component.
The child component is trying to add a user to this list which works(adding value to passed in props), but because the users array is declared under data the parent component refreshes and i lose my users...
is there a pattern to keep the users array values and add to them via a child...
sorry if this is obvious but as i said i'm just starting...
edit : adding code (parent component)
<template>
<div id="app">
<div>
<button v-on:click="display()">Display users</button>
<button v-on:click="displaySingleUserInput =
!displaySingleUserInput">Add user</button>
<div>
</div>
<ul v-if="errors && errors.length">
<li v-for="error of errors">
{{error.message}}
</li>
</ul>
</div>
<add-user v-on:submit_user="addUser" v-show="displaySingleUserInput"></add-user>
<user-list v-bind:users="users"></user-list>
</div>
</template>
<script>
import axios from 'axios';
import UserList from './components/UserList';
import AddUser from './components/AddUser';
export default {
components: {
'user-list': UserList,
'add-user': AddUser
},
name: 'app',
data () {
return {
users: [],
errors: [],
displaySingleUserInput: false
}
},
methods: {
display: function(string)
{
axios.get(`users.json`)
.then(response => {
// JSON responses are automatically parsed.
this.users = response.data.users
})
.catch(e => {
this.errors.push(e)
})
},
addUser: function(id) {
this.users.push({firstname: "john", lastName: 'jones'})
},
}
}
</script>
child component
<template>
<div id="singleUserAdd">
<form id=addUser aria-label="single user add">
<button v-on:click="submit()">Submit</button>
<button>Cancel</button>
</form>
</div>
</template>
<script>
export default {
data() {
return {
}
},
methods: {
submit: function() {
this.$emit('submit_user', 1)
}
}
}
</script>
I assume that you have a method called addUser in your child component :
addUser(){
this.$emit("addusr",this.newuser);
}
In the parent one :
<child-comp #addusr="addNewUser" />
...
addNewUser(newuser){
this.users.push(newuser);
}
looks like i was missing
<form v-on:submit.prevent="onSubmit"
to stop my page from refreshing