Vue in laravel 5.8, populate table dynamically from axios response - vue.js

I have a blade where I'm using a multiselect as dropdown, and when a selection is chosen it fires off an axios call which returns a json_encoded data set.
The blade is here:
<div class="uk-width-1-2">
<multiselect
label="name"
track-by="value"
v-model="CategoryValue"
:options="CategoryOptions"
:multiple="false"
:taggable="true"
#tag="getItems"
#input="getItems"
#search-change="val => read(val)"
:preselect-first="false"
:close-on-select="true"
:preserve-search="true"
placeholder="Choose Category..."
></multiselect>
<div style="border:1px solid black; height:80%; margin-top:15px;">
<table>
<thead>
<tr>
<th>Text</th>
</tr>
</thead>
<tbody v-for="build in buildsList">
<tr>
<td>#{{ build.build_code_formatted }}</td>
</tr>
</tbody>
</table>
</div>
</div>
new Vue({
data() {
return{
buildsList: {},
}
},
methods: {
getItems() {
console.log(this.CategoryValue.value);
axios.post('/getItems',{
categoryCode: this.CategoryValue.value,
})
.then(function (response){
this.buildsList = response.data;
})
.catch(function (error) {
console.log(error);
});
}
}
})
And upon the callback I get a 200 and It does indeed log the buildsList so I know it is returning all of my data properly. However, when I get my data back in the console, it's not populating the html.
When I inspect the page elements there is no table body or data rows.
Also, my controller is returning this:
unction getItems(Request $request){
return json_encode($this->itemService->getItems($request->Code));
}
and itemService is doing this:
$results = $pdoStatement->fetchAll();
foreach ($results as &$r)
$r = (object) $r;
return $results;
So my data is coming back upon axios Call and it is formatted properly, but I just need to figure out why my table isn't dynamically populating

Please try to change this part
this.buildsList = response.data;
to
.then((response) => {
let data = response.data;
for (let key in data) {
if(data.hasOwnProperty(key)) {
this.$set(this.buildsList, key, data[key]);
}
}
})

Related

Failed returning data from array

i have a problem which that i cannot see the data inside array that i have pushed the data inside .then from axios
Here are the sample code
Axios from vue.js
export default {
name: 'facts',
data(){
const test = [{id: 'test',name: 'test'}]
const response = [];
const factsData = () => {
axios.get('http://localhost:3000').then(x=>response.push(x.data))
}
factsData();
console.log(response)
return{
test,
response
};
}
};
When i tried to console.log the output data inside the promise(.then) it worked well and display the data that i expected like this
and this is what happen when i tried to push the data from axios to response and show the output data in console.log with my current code above
when i tried to access it (console.log(response[0]), it shows undefined in console.log.
But strangely, when i back to my previous code to not to tried to access the data and i expand the array in console browser, it shows data that i expected which mean i couldn't access it.
The main purpose is i want to dipsplay the data to be rendered in table using v-for
<template>
<div class="about">
<center>
<table>
<thead>
<tr>
<th></th>
<th>ID</th>
<th>Username</th>
</tr>
</thead>
<tbody>
<tr v-for="(user,index) in test" :key="user.id">
<td>{{index + 1}}</td>
<td>{{user.id}}</td>
<td>{{user.name}}</td>
</tr>
</tbody>
</table>
</center>
</div>
</template>
Please tell me what i'm missing. Thank you.
P.S : I'm new to this vue js
your code structure is not correct. use this code:
export default {
name: 'ProfilePage',
data() {
return {
response: []
}
},
created () {
this.getData();
},
methods: {
getData() {
axios.get('http://localhost:3000').then(x => this.response = x.data);
}
}
}

How can I get a result from a POST request into a v-for?

I have something like this:
<table class="table">
<tbody>
<tr v-for="(option, index) in Weapons">
<td>Primary</td>
<td>[[ getWeaponType(option.WeaponType) ]]</td>
</tr>
</tbody>
</table>
In my Vue object, in methods, I have this:
getWeaponType: function(weaponTypeNumber){
axios.get('/path/to/api')
.then(response => {
return response.data
})
}
I send an ID and it returns the name for that ID. But I need for it to show in my table whose rows are being generated by the v-for. This isn't working since it is a Promise and the values are not showing. Is there any way I can achieve getting that value to show in the table? I didn't want to do it server side so I'm trying to see if I have any options before I do that.
May I suggest an alternative method?
data() {
return {
weaponsMappedWithWeaponTypes: [];
}
}
mounted() { // I am assuming the weapons array is populated when the component is mounted
Promise.all(this.weapons.map(weapon => {
return axios.get(`/path/to/api...${weapon.weaponType}`)
.then(response => {
return {
weapon,
weaponType: response.data
}
})
).then((values) => {
this.weaponsMappedWithWeaponTypes = values
})
}
computed: {
weaponsAndTheirWeaponTypes: function () {
return this.weaponsMappedWithWeaponTypes
}
}
And then in your template
<table class="table">
<tbody>
<tr v-for="(option, index) in weaponsAndTheirWeaponTypes">
<td>Primary</td>
<td>option.weaponType</td>
</tr>
</tbody>
</table>

Getting part of the page to display updated data in vue

I'm using vue to create a page where I list all users and if I click on the edit button the details of that user then gets shown
next to the list.
What I'm trying to do is, if I update a user and click save then the user details in the list needs to change.
The problem I'm having is that I'm not able to get the details to change in the list after I've saved.
My vue
<template>
<div class="card">
<div class="card-body">
<div class="row">
<div class="col-md-7">
<table class="table table-striped table-sm mt-2">
<thead>
<tr>
<th>Name</th>
<th></th>
</tr>
</thead>
<tbody>
<tr v-for="user in displayAllUsers">
<td>{{ user.name }}</td>
<td>
<button class="btn btn-sm btn-success" #click="manageUser(user)">Edit</button>
</td>
</tr>
</tbody>
</table>
</div>
<div class="col-md-5" v-if="user != null">
<div class="card">
<div class="card-header">
<h4 class="card-title mb-0">Manage {{ user.name }}</h4>
</div>
<div class="card-body">
<table class="table">
<tr>
<th>Name</th>
<td>
<input type="text" v-model="user.name">
</td>
</tr>
</table>
</div>
<div class="card-footer">
<button #click="updateUser()"class="btn btn-success"><i class="fa fa-save"></i> Save</button>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
components: {
},
data: function () {
return {
users: [],
user: null
}
},
computed: {
displayAllUsers(){
return this.users;
}
},
methods: {
manageUser(user){
axios.get('/admin/user/'+user.id).then((response) => {
this.user = response.data.user;
});
},
updateUser(){
axios.put('/admin/user/'+this.user.id, {
name: this.user.name
}).then((response) => {
this.users = response.data.user;
});
}
},
mounted() {
axios.get('/admin/users').then((response) => {
this.users = response.data.users;
});
}
}
</script>
There are two possible solutions.
The first is to run this code at the end of the updateUser method:
axios.get('/admin/users').then((response) => {
this.users = response.data.users;
});
The second is to use a state manager like Vuex.
The first scenario will fetch again your users data from the remote API and will update your view with all your users.
With the second scenario, you will handle your application state way much better than just using the data attribute of your page module, but in the background, it is more or less the same as the first solution I suggest.
To update the current user only in the table you could do something like that at the end of the updateUser method:
let userIdx = -1;
for(let idx = 0, l = this.users.length; idx < l; idx++) {
if ( this.user.id === this.users[idx].id ) {
userIdx = idx;
break;
}
}
if ( -1 !== userIdx ) {
this.users[userIdx] = this.user;
this.user = {};
}
Other than your problem, it seems like you don't need this code:
computed: {
displayAllUsers(){
return this.users;
}
},
You could remove this code, and instead use this code in the HTML part:
<tr v-for="user in users">
For your updateUser function you could just return the modified user in the same format that you have for all the users in you user list and update the user by index. This is presuming that the user you want to update is in the users array to start with.
updateUser() {
axios.put('/admin/user/'+this.user.id, {
name: this.user.name
}).then((response) => {
const updatedUser = response.data.user;
// Find the index of the updated user in the users list
const index = this.users.findIndex(user => user.id === updatedUser.id);
// If the user was found in the users list update it
if (index >= 0) {
// Use vue set to update the array by index and force an update on the page
this.$set(this.users, index, updatedUser);
}
});
}
This could be a good starting point.
Unrelated Note:
You can add your mounted function code to its own method, for example
getUsers() {
axios.get('/admin/users').then((response) => {
this.users = response.data.users;
});
}
then
mounted() {
this.getUsers()
}
this makes it a little cleaner and easier if you ever need to get the users again (example: if you start having filters the user can change)
As it could get more complex vuex would be a great addition.

Multiple v-for loops to display cities within countries?

I am using vue.js and axios within visualstudiocode I'm very new at coding in principal and so I'm sorry if my question is worded poorly. I have two arrays I have assembled from this API:
'https://docs.openaq.org/#api-_'
One is a list of countries, with data of how many cities is in the region etc, and another is a list of cities within those countries with corresponding data. I display a list of countries, in a table followed by a corresponding button in the same row using a v-for loop.
What I want to happen, is when this button is clicked for each corresponding country in the array, all the cities within that country will be displayed. I've been told this needs seperate v-for loops, but I don't know how I'd write it out.
<template>
<div>
<table>
<tr>
<th>Countries</th>
<th>Cities</th>
</tr>
<tr>
<td>
<ul>
<li v-for="countries in listofcountries" :key="countries.name">
{{countries.name}}
</li>
</ul>
</td>
<ul v-for="countries in listofcountries" :key="countries.name">
<li v-for="cities in lifeofcities" :key="cities.city"></li>
<button>{{countries.name}}</button>
</li>
</ul>
</tr>
</table>
</div>
</template>
<script>
export default {
name: 'HelloWorld',
data () {
return {
listofcountries: [],
listofcities: [],
}
},
mounted () {
const axios = require('axios');
var self = this;
// Make a request for a user with a given ID
axios.get('https://api.openaq.org/v1/cities')
.then(function (response) {
// handle success
console.log(response.data.results);
self.listofcities=response.data.results;
})
.catch(function (error) {
// handle error
console.log(error);
})
.finally(function () {
// always executed
});
axios.get('https://api.openaq.org/v1/countries')
.then(function (response) {
// handle success
console.log(response.data.results) ;
self.listofcountries=response.data.results;
})
.catch(function (error) {
// handle error
console.log(error);
})
.finally(function () {
// always executed
});
},
methods: {
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style>
ul {
list-style-type: none;
}
table,th,td,tr {
border: 1px solid black;
}
</style>
I'm not completely certain this output is what you want but it's a place to start:
<template>
<table>
<tr>
<th>Countries</th>
<th>Cities</th>
</tr>
<tr v-for="country in countries" :key="country.key">
<td>{{ country.name }}</td>
<td v-if="areCitiesVisible(country.code)">{{ citiesInCountry(country.code) }}</td>
<td v-else>
<button #click="onClickCountry(country.code)">show cities</button>
</td>
</tr>
</table>
</template>
<script>
import axios from 'axios';
export default {
data() {
return {
cities: [],
countries: [],
countriesWithVisibleCities: [],
};
},
mounted() {
axios
.get('https://api.openaq.org/v1/cities')
.then(response => (this.cities = response.data.results))
.catch(console.log);
axios
.get('https://api.openaq.org/v1/countries')
.then(response => (this.countries = response.data.results))
.catch(console.log);
},
methods: {
citiesInCountry(code) {
return this.cities
.filter(c => c.country === code)
.map(c => c.city)
.join(', ');
},
onClickCountry(code) {
this.countriesWithVisibleCities.push(code);
},
areCitiesVisible(code) {
return this.countriesWithVisibleCities.includes(code);
},
},
};
</script>
It renders a table with one row per country. Each row has a name and a button. If you press the button, you'll get a list of cities instead of the button. Note that the API call seems to return only the cities for the first few counties in the list.
Other notes:
Your original post had several typos that could have been caught with eslint. I recommend installing it along with prettier. I promise that will save you a lot of time in the future.
I simplified the axios calls - self isn't necessary here.

I cannot display data form an API using v-for in vuejs

I cannot get or display data from an API but the API is working fine when I console.log the issue. but I cannot display data in my table I used v-for I am new to Vue.js I don't know how to solve this issue
I am using Vue.js 2, Axios method to connect to my API
here's my code
import axios from 'axios'
export default{
data(){
return{
errorMessage: "",
successMessage: "",
users: []
}
},
mounted: function(){
this.getAllUsers();
},
methods:{
getAllUsers: function(){
axios.get('http://localhost:8888/vue-and-php/public/api/config.php?action=read', { crossdomain: true })
.then(function(response){
//console.log(response);
if(response.data.error){
app.errorMessage = response.data.message;
}else{
app.users = response.data.users;
}
});
}
}
}
THIS IS WHERE I WANT TO DISPLAY MY DATA
<!--test PHP-->
<button class="fright addNew btn-sm btn-success" data-toggle="modal" data-target="#exampleModalLong">Add New</button>
<p class="errorMessage alert alert-danger" v-if="errorMessage">
{{errorMessage}}
</p>
<p class="successMessage alert alert-success" v-if="successMessage">
{{successMessage}}
</p>
<table class="table">
<tr>
<th>ID</th>
<th>User Name</th>
<th>Name</th>
<th>Email</th>
<th>Edit</th>
<th>Delete</th>
</tr>
<tr v-for="user in users">
<td>{{user.id}}</td>
<td>{{user.username}}</td>
<td>{{user.name}}</td>
<td>{{user.email}}</td>
<td><button class="btn btn-sm btn-success">ADD</button></td>
<td><button class="btn btn-sm btn-danger">DELETE</button></td>
</tr>
</table>
You cannot do
app.users = response.data.users;
Since app is not defined anywhere and your browsers console must be throwing an error, what are trying to do over here is assigning the users you have defined in data function.
That users object is present in current context and can be accessed through 'this' keyword.
Try :
if(response.data.error){
this.errorMessage = response.data.message;
}
else{
this.users = response.data.users;
}
Thank you guys for your time I used your code but with little corrections
this is the final code that works
getAllUsers: function(){
axios.get('http://localhost:8888/vue-and-php/public/api/config.php?action=read', { crossdomain: true })
.then((response) => {
//console.log(response);
if(response.data.error){
this.errorMessage = response.data.message;
}else{
this.users = response.data.users;
}
});
}
You probably want to set some information into your users data field. You need to do something like:
if (response.data.error) {
this.errorMessage = response.data.message;
} else {
this.users = response.data.users;
}
Explanation: Vue reacts to changes in data and props. By doing this.users = response.data.users, you are telling Vue that the users have changed and then Vue will re-render based on how you've used users inside your template.