Vue component not updated when props are async - vue.js

I have this component
<template>
<div class="list-group">
<div v-for="user in data">
<!-- omitted for brevity -->
</div>
</div>
</template>
<script>
export default ("users-list", {
props: ['users']
,
data() {
return {
data: this.users
}
}
});
</script>
This component is used from another component witch get data with $.ajax (Promise) and set the data
<template>
<div>
<users-list v-bind:users="users"></users-list>
<div>
</template>
<script>
import UsersService from './users.service.js';
import UsersList from './users.list.vue';
export default ('users-main', {
components: {
'users-list': UsersList
},
mounted() {
this.refresh();
},
data() {
return {
data: null,
message: null,
user: null
}
},
methods: {
refresh() {
let service = new UsersService();
service.getAll()
.then((data) => {
this.data = data;
})
.catch((error) => {
this.message = error;
})
},
selected(user) {
this.user = user;
}
}
});
</script>
And this is the UsersService
import $ from 'jquery';
export default class UsersService {
getAll() {
var url = "/Api/Users/2017";
return new Promise((resolve, reject) => {
$.ajax({
url: url,
success(data) {
resolve(data);
},
error(jq, status, error){
reject(error);
}
});
});
}
}
As you can see the service get data with Promise, if I change
<div v-for="user in data">
into the property, I can see the users
<div v-for="user in users">
Question: How I can pass async props value to the components ?

You set data once "onInit" in users-list.
For reactivity you can do
computed:{
data(){ return this.users;}
}
or
watch: {
users(){
//do something
}
}

What do you mean by:
As you can see the service get data with Promise, if I change
<div v-for="user in data">
into the property, I can see the users
<div v-for="user in users">
Use <div v-for="user in users"> should work, so what's the problem?

Related

Why Vue doesn't refresh list using props?

On my App, on mounted() method, I call an API, which give to me a JSON with a list of items; than, I update the prop I've set in my target Homepage component:
Homepage.pages = resJSON.data.pages;
Here's the App code:
<template>
<div id="app">
<Homepage title="PWA Test"/>
</div>
</template>
<script>
import Homepage from './components/Homepage.vue'
export default {
name: 'App',
components: {
Homepage
},
mounted() {
let endpoint = "http://localhost:5000/api/graphql?query={pages(orderBy:{contentItemId:asc}){contentItemId,createdUtc,author,displayText,description%20}}";
fetch(endpoint, {
method:'GET',
headers: {
'Accept':'application/json',
'Content-Type':'application/json'
}
})
.then((response) => {
// check for HTTP failure
if (!response.ok) {
throw new Error("HTTP status " + response.status);
}
// read and parse the JSON
return response.json();
})
.then((res) => {
Homepage.pages = res.data.pages;
})
.catch((error) => {
console.log(error);
});
}
}
</script>
<style>
</style>
Here's the Homepage component:
<template>
<div id="homepage">
<h1>{{ title }}</h1>
<ul>
<li v-for="page in pages" :key="page.description">#{{ page.description }}</li>
</ul>
</div>
</template>
<script>
export default {
name: 'Homepage',
props: {
title: String,
pages: []
}
}
</script>
<style scoped>
</style>
But the ul doesn't update after receiving the JSON and updating the props pages. Where's my error?
you need to get the response.json(); in a data property of the App and then pass it down to the Homepage component. So your code should you look like this,
App code:
<template>
<div id="app">
//binding page into data property
<Homepage title="PWA Test" :pages="pages"/>
</div>
</template>
<script>
import Homepage from './components/Homepage.vue'
export default {
name: 'App',
data: function () {
return {
//data propety
pages : []
}
},
components: {
Homepage
},
mounted() {
let endpoint = "http://localhost:5000/api/graphql?query={pages(orderBy:{contentItemId:asc}){contentItemId,createdUtc,author,displayText,description%20}}";
fetch(endpoint, {
method:'GET',
headers: {
'Accept':'application/json',
'Content-Type':'application/json'
}
})
.then((response) => {
if (!response.ok) {
throw new Error("HTTP status " + response.status);
}
// assign the result to the data property
this.page = response.json();
})
.then((res) => {
Homepage.pages = res.data.pages;
})
.catch((error) => {
console.log(error);
});
}
}
</script>
Do you pass the props in a template after this.pages = res.data.pages?
<Homepage :pages="pages" />
I think there are some mistakes that you have done in your code, if you want change update prop value then you have to initialized your props values in script.
<template>
<div id="homepage">
<h1>{{ title }}</h1>
<ul>
<li v-for="page in currentPages" :key="page.description">#{{ page.description }}
</li>
</ul>
</div>
</template>
<script>
export default {
name: 'Homepage',
props: {
title: String,
pages: []
}
data: function () {
return {
currentPages: this.pages
}
}
}
</script>
I hope this will help you to solve your issue- thanks!

Vue.js prop sync modifier not updating parent component

I have a property that I need to pass to a child component, but the child component needs to be able to modify the value that was passed. It seems like the .sync modifier is built for this, but I can't seem to get it to work. Here is my code (simplified for this question):
Profile.vue
<template>
<div>
<Avatar :editing.sync="editing"></Avatar>
click to change
...
</div>
</template>
<script>
import Avatar from './profile/Avatar'
export default {
components: { Avatar },
data() {
return {
...,
editing: false,
}
},
methods: {
editAvatar() {
this.editing = true;
}
}
}
</script>
Avatar.vue
<template>
<div>
<template v-if="!editing">
<img class="avatar-preview" :src="avatar">
</template>
<template v-else>
<label v-for="image in avatars">
<img class="avatar-thumb" :src="image">
<input type="radio" name="avatar" :checked="avatar === image">
</label>
<button class="btn btn-primary">Save</button>
</template>
</div>
</template>
<script>
export default {
props: ['editing'],
data() {
return {
avatar: '../../images/three.jpg',
avatars: [
'../../images/avatars/one.jpg',
'../../images/avatars/two.jpg',
'../../images/avatars/three.jpg',
...
]
}
},
methods: {
save() {
axios.put(`/api/user/${ user.id }/avatar`, { avatar: this.avatar }
.then(() => { console.log('avatar saved successfully'); })
.catch(() => { console.log('error saving avatar'); })
.then(() => { this.editing = false; }); // ← this triggers a Vue warning
}
}
}
</script>
You are correct - the .sync modifier is built for cases just like this. However, you are not quite using it correctly. Rather than directly modifying the prop that was passed, you instead need to emit an event, and allow the parent component to make the change.
You can resolve this issue by changing the save() method in Avatar.vue like this:
...
save() {
axios.put(`/api/user/${ user.id }/avatar`, { avatar: this.avatar }
.then(() => { console.log('avatar saved successfully'); })
.catch(() => { console.log('error saving avatar'); })
.then(() => { this.$emit('update:editing', false); });
}
}
...

Vue.js Component emit

I have some problem about component $emit
This is my child component:
<template>
<div class="input-group mb-3 input-group-sm">
<input v-model="newCoupon" type="text" class="form-control" placeholder="code">
<div class="input-group-append">
<button class="btn btn-outline-secondary" type="button" #click="addCoupon">comfirm</button>
</div>
</div>
</template>
<script>
export default {
props: ["couponcode"],
data() {
return {
newCoupon: this.couponcode
};
},
methods: {
addCoupon() {
this.$emit("add", this.newCoupon);
}
}
};
</script>
This is parent component
<template>
<div>
<cartData :couponcode="coupon_code" #add="addCoupon"></cartData>
</div>
</template>
<script>
import cartData from "../cartData";
export default {
components: {
cartData
},
data() {
return {
coupon_code: ""
}
},
methods:{
addCoupon() {
const api = `${process.env.API_PATH}/api/${
process.env.CUSTOM_PATH
}/coupon`;
const vm = this;
const coupon = {
code: vm.coupon_code
};
this.$http.post(api, { data: coupon }).then(response => {
console.log(response.data);
});
},
}
}
</script>
When I click the 'confirm' button,the console.log display 'can't find the coupon' 。 If I don't use the component,it will work 。
What is the problem? It's about emit?
addCoupon() {
this.$emit("add", this.newCoupon); // You emitted a param
}
// then you should use it in the listener
addCoupon(coupon) { // take the param
const api = `${process.env.API_PATH}/api/${
process.env.CUSTOM_PATH
}/coupon`;
const coupon = {
code: coupon // use it
};
this.$http.post(api, { data: coupon }).then(response => {
console.log(response.data);
});
},

Can't find out why vuex getters ById doensim get a company by id

i am still new to VUEX and i am following this tutorial vuex store
The only thing i am doing differently is i am using sequelize instead of mLab like he does.
This is what my getters look like
export const companyGetters = {
allCompanies: (state, getters) => {
return state.companies
},
companyById: (state, getters) => id => {
if (getters.allCompanies.length > 0) {
return getters.allCompanies.filter(c => c._id === id)[0]
} else {
return state.company
}
}
Exactly like what he did.
My action looks like this
companyById ({commit}, payload) {
commit(COMPANY_BY_ID)
axios.get(`${API_BASE}/companies/${payload}`).then(response => {
console.log(payload, response.data)
commit(COMPANY_BY_ID_SUCCESS, response.data)
})
}
Next in my details i have
<template>
<div>
<company-details :company="company" ></company-details>
</div>
</template>
<script>
import CompanyDetails from '../components/company/CompanyDetails'
export default {
created () {
if (!this.company.companyName) {
this.$store.dispatch('companyById', this.$route.params['id'])
}
},
computed: {
company () {
return this.$store.getters.companyById(this.$route.params['id'])
}
},
components: {
'company-details': CompanyDetails
}
}
</script>
and then finally my companyDetails looks like this
<template>
<v-ons-col style="width: 350px; float:left;">
<v-ons-card>
<h1>{{company}}</h1>
</v-ons-card>
</v-ons-col>
</template>
<script>
export default {
props: ['company']
}
</script>
here is the mutations
export const companyMutations = {
[ALL_COMPANYS] (state) {
state.showLoader = true
},
[ALL_COMPANYS_SUCCESS] (state, payload) {
state.showLoader = false
state.companies = payload
},
[COMPANY_BY_ID] (state) {
state.showLoader = true
},
[COMPANY_BY_ID_SUCCESS] (state, payload) {
state.showLoader = false
state.company = payload
}
and here is my actions
allCompanies ({commit}) {
commit(ALL_COMPANYS)
axios.get(`${API_BASE}/companies`).then(response => {
commit(ALL_COMPANYS_SUCCESS, response.data)
})
},
companyById ({commit}, payload) {
commit(COMPANY_BY_ID)
axios.get(`${API_BASE}/companies/${payload}`).then(response => {
console.log(payload, response.data)
commit(COMPANY_BY_ID_SUCCESS, response.data)
})
My CompanyList looks like this
<template>
<div>
<div class="companies">
<div class="container">
<template v-for="company in companies" >
<company :company="company" :key="company.id"></company>
</template>
</div>
</div>
</div>
</template>
<script>
import Company from './Company.vue'
export default {
name: 'companies',
created () {
if (this.companies.length === 0) {
this.$store.dispatch('allCompanies')
}
},
computed: {
companies () {
return this.$store.getters.allCompanies
}
},
components: {
'company': Company
}
}
</script>
Imported Company looks like this
<template>
<v-ons-col style="width: 350px; float:left;">
<router-link :to="'/details/'+company.id" class="company-link">
<v-ons-card>
<img :src="company.imageURL" style="width:100% ;margin: 0 auto;display: block;">
<div class="title">
<b>{{company.companyName}}</b>
</div>
<div class="description">
{{company.description}}
</div>
<div class="content">
<!-- <template v-for="company in companies">
{{company}}
</template> -->
</div>
</v-ons-card>
</router-link>
</v-ons-col>
</template>
<script>
export default {
name: 'company',
props: ['company']
}
</script>
So when i click on one of these "companies" its suppose to get it by id and show the details however in the getters this return getters.allCompanies.filter(c => c._id === id)[0] returns undefined, when i refresh the page then it gets the correct company and displays it, what is going on please help. If you need more info please ask

Api route returning empty array

Api route returning empty array evenif table contains data.
I want to list the users.Route model binding fails
This is my api route
Route::middleware('auth:api')->get('/users',function(){
return App\User::get();
});
This is my vue component used in the blade template
<template>
<div class="row" style="min-height:250px;">
<ul>
<li v-for="user in users">
#{{user.name}}
</li>
</ul>
</div>
</template>
<script>
export default{
data() {
return {
users : [],
}
},
ready: function(){
this.getUsers();
},
methods: {
getUsers: function(response){
axios.get('http://localhost:8000/api/users')
.then(response => {
this.users = response.data;
})
.catch(error => {
});
},
},
}
</script>