Module: https://github.com/matfish2/vue-tables-2
I'm creating a CRUD app. How can I reload the data fetched via Ajax call in vue-tables-2? I wanted to reload the table after an update statement is executed somewhere in my app.
Vue-tables is using vuex in my setup.
<v-server-table
name="UserAdmin" url="admin/master/?format=json" :columns="columns" :options="options">
</v-server-table>
EDIT: Added Javascript code of the table for data properties.
export default {
data() {
return {
columns: ['ID','NAME', 'POSITION', 'APPLICATIONS','DESCRIPTION','STATUS','ENCODED_BY'],
options: {
responseAdapter: (resp) => {
resp = resp.map(item => ({
ID: item.ID,
FK_ID: item.FK_ID,
NAME: `${item.FIRSTNAME} ${item.LASTNAME}`,
POSITION: item.POSITION,
APPLICATIONS: item.APPLICATIONS,
DESCRIPTION: item.DESCRIPTION,
STATUS: item.STATUS,
ENCODED_BY: item.ENCODED_BY,
TOTAL: item.TOTAL
}));
let count;
if(resp[0] != null) {
count = resp[0]['TOTAL']
}
else {
count = 0
}
return {
data: resp,
count: count
}
},
headings: {
'ID': <span># </span>,
'NAME':'Name',
'POSITION':'Position',
'APPLICATIONS':'Applications',
'DESCRIPTION':'Description',
'STATUS': 'Status',
'ENCODED_BY':'Encoded By',
'edit': 'Options'
},
columnsClasses: {
ID: 'col-md-1',
NAME:'col-md-2 pointer',
POSITION: 'col-md-2',
APPLICATIONS: 'col-md-2',
DESCRIPTION: 'col-md-2',
STATUS: 'col-md-1',
ENCODED_BY: 'col-md-2',
},
templates: {
NAME: (h, row) => {
return <a on-click={ () => this.setUpdateID(row) }>{row.NAME}</a>
},
APPLICATIONS: (h,row) => {
return (<ul>{JSON.parse(row.APPLICATIONS).map((val)=>(<li>{val}</li>))}</ul>);
},
STATUS: (h, row) => {
if(row.STATUS == 1) {
return <span class="label label-success">Active</span>
}
else if(row.STATUS == 0) {
return <span class="label label-danger">Inactive</span>
}
}
},
},
}
},
methods: {
setUpdateID: function(row) {
this.$store.commit('SET_UPDATE_ID', row.FK_ID);
}
}
}
As documented you should use the refresh method.
You can read about refs here
<v-server-table ref="table"
name="UserAdmin" url="admin/master/?format=json" :columns="columns" :options="options">
</v-server-table>
Javascript:
methods:{
onUpdate() {
this.$refs.table.refresh();
}
}
for reloading the vuetable with updated data you have two way:
1.
select the vuetable component with $refs in your code and then calling refresh method , something like this:
html file:
<v-server-table ref="userAdminVueTable"
name="UserAdmin" url="admin/master/?format=json" :columns="columns" :options="options">
js file:
this.$refs.userAdminVueTable.refresh()
2.
When you have your data you can call setData method for your vuetable.
this.vuetable.setData(updatedData);
in below i write an example for you, you can get inspire from that:
this.Service.GetAllRecords().then(result => {
this.vuetable.setData(result);
}).catch(e => {
this.showAlert = true
this.message = ` Exception : ${e}`
})
}
You don't need to refresh it, if you refresh than state would be clear. You can use bellow example
methods:{
onUpdate(rowIndex) {
this.$refs.table.data.splice(rowIndex, 1);
}
}
<v-server-table ref="table"
name="UserAdmin" url="admin/master/?format=json" :columns="columns" :options="options">
</v-server-table>
Related
I have an array of objects, each has a URL that is being loaded in this file:
<template>
<div>
<img :key="id" :src="img" alt="" class="image box" #click="cardClicked" />
</div>
</template>
<script lang="ts">
export default {
props: ["id", "value", "type", "owner", "imgURL"],
data() {
return {
img: require(`./../assets/${this.imgURL}.png`)
};
},
methods: {
cardClicked() {
this.$store.commit("addCardToPlayer", {
id: this.id,
type: this.type,
value: this.value,
owner: this.owner
});
}
}
};
</script>
In the Store mutation I preform filtering and while filtering I add the card to a another player, like so:
addCardToPlayer(state, clickedCard) {
const owner = clickedCard.owner;
const type = clickedCard.type;
const currPlayer = state.currentPlayerName;
if (clickedCard.owner === "deck") {
state.cardOwners[owner].cards[type] = state.cardOwners[owner].cards[
type
].filter(card => {
if (card.id === clickedCard.id) {
state.cardOwners[currPlayer].cards[type].push(card);
return false;
} else return true;
});
}
},
When clicking a card to remove, I see the card being added to the player and displayed correctly, and the number of cards displayed after removal is correct.
But
The card that was removed still shows.
What have I tried:
Forcing to re-render using:
cardClicked() {
this.$store.commit("addCardToPlayer", {
id: this.id,
type: this.type,
value: this.value,
owner: this.owner
});
this.$forceUpdate();
}
Making different components have key, and trying to change the key to cause a re-render:
data() {
return {
componentKey: 0,
};
},
methods: {
forceRerender() {
this.componentKey += 1;
}
}
Tried changing how I change the array by creating a new state.
EDIT: Tried using computed. i get an error:
TypeError: Cannot read property 'imgURL' of undefined
computed: {
getImg: () => {
return require(`./../assets/${this.imgURL}.png`);
}}
EDIT: I wrote the computed function as arrow function, which doesn't preserve context.
SOLVED, by using
computed: {
getImg() {
return require(`./../assets/${this.imgURL}.png`);
}}
How can I make the images update after click (remove).
Thanks.
SOLVED, by using
computed: {
getImg() {
return require(`./../assets/${this.imgURL}.png`);
}}
I used the vue 2. I had a data from ajax, this is my code example:
<template>
<div>
<input type="input" class="form-control" v-model="siteInfo.siteId">
<input type="input" class="form-control" v-model="siteInfo.info.name">
<input type="input" class="form-control" v-model="siteInfo.accountData.name">
</div>
</template>
<script>
export default {
name: 'Site',
data() {
return {
siteInfo: {},
/* siteInfoName: '', */
}
},
/*computed: {
siteInfoName: function() {
return siteInfo.info.name || '';
},
...
},*/
methods: {
getData() {
// do ajax get data
this.$http.post('URL', {POSTDATA}).then(response => {
/*
response example
{ body:
data: {
sitdeId: 1,
info: { name: 'test'},
accountData: { name: 'accountTest'},
}
}
*/
this.siteInfo = response.body.data;
})
}
},
mounted() {
this.getData();
}
}
</script>
I got a warring message
[Vue warn]: Error in render: "TypeError: Cannot read property 'name'
of undefined"
I can use computed to fix it, but if I had a lot model, I should
write a lot computed.
I should create a lot data for those model?
I should not use an object to bind a lot model?
Does it have another solution for this situation? Thanks your help.
Before the data loads siteInfo.info will be undefined, so you can't access name in the v-model:
v-model="siteInfo.info.name"
Likewise for siteInfo.accountData.name.
My suggestion would be to set the initial value of siteInfo to null and then put a v-if="siteInfo" on the main div. Alternatively you could put a v-if on the individual input elements that checks for siteInfo.info and siteInfo.accountData.
You may also want to consider showing alternative content, such as a load mask, while the data is loading.
Don't be worried about too many v-models - you can do an iteration on the Object - like with Object.entries().
Vue.component('list-input-element', {
props: ['siteLabel', 'siteInfo'],
template: '<div><label>{{siteLabel}}<input type="input" class="form-control" v-model="siteInfo"></label></div>'
})
new Vue({
name: 'Site',
el: '#app',
data() {
return {
siteInfo: {},
}
},
methods: {
getData() {
// using mockup data for this example
fetch('https://jsonplaceholder.typicode.com/todos/1')
.then(response => response.json())
.then(json => {
console.log(json)
this.siteInfo = json
})
// do ajax get data
/*this.$http.post('URL', {
POSTDATA
}).then(response => {
this.siteInfo = response.body.data;
})*/
}
},
mounted() {
this.getData();
}
})
div {
display: block;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<list-input-element v-for="siteInfo in Object.entries(siteInfo)" :site-label="siteInfo[0]" :site-info="siteInfo[1]" />
</div>
Rounding up
So, when you do the single file template, use a computed value, and return an Object from that.
Base your v-for on that computed, and you'll have no problems.
Something like this:
<template>
<div>
<input type="input" class="form-control" v-for="infoEl in siteInfoComputed" v-model="infoEl">
</div>
</template>
<script>
export default {
name: 'Site',
data() {
return {
siteInfo: {},
}
},
computed: {
siteInfoComputed: function() {
// you could check for all the keys-values you want here, and handle
// 'undefined' problem here
// so, actually you "create" the Object here that you're going to use
let ret = {}
// checking if this.siteInfo exists
if (Object.keys(this.siteInfo).length) ret = this.siteInfo
return ret
},
},
methods: {
getData() {
// do ajax get data
this.$http.post('URL', {POSTDATA}).then(response => {
/*
response example
{ body:
data: {
sitdeId: 1,
info: { name: 'test'},
accountData: { name: 'accountTest'},
}
}
*/
this.siteInfo = response.body.data;
})
}
},
mounted() {
this.getData();
}
}
</script>
I have a child component in foreach loop. The component has two methods what I initiate with a button on the component.
How can I make these methods unique/ every foreach iteration? Becouse if I don't make them distinct even if I push the last iteration's button the first iteration's method will start.
I tried with methodName+index: function(), but I got , expected error.
Update
My parent component:
<div v-for="(card, index) in cards" v-bind:key="index">
<CardSubComponent
:card="card"
#cardSaveSuccess="cardSuccess"
#cardSaveError="cardError"
></CardSubComponent>
</div>
My subcomponent:
<template>
<div class="box">
<b-select :id="cardSequenceID" v-model="card.sequence" #input="changeSequence">
<option
v-for="sequence in sequenceArray"
:value="sequence"
:key="sequence">
{{ sequence }}
</option>
</b-select>
</div>
</template>
<script>
export default {
props:['card'],
data(){
return {
cardSequenceID = 'sequence'+card.id
sequenceArray: [1,2,3,4,5,6,7,8,9,10],
}
},
methods: {
changeSequence(rule){
axios.post('/api/card/changeSequence', {
cardID: card.id,
weight: document.getElementById('sequence'+cardID).value
},
{
headers: {
Authorization: 'Bearer ' + localStorage.getItem('token')
}
}).then(response => {
if(response.data == 'success'){
this.$emit('cardSaveSuccess')
} else {
this.$emit('cardSaveError')
}
});
}
}
}
</script>
It sounds like you need to add arguments to your method calls. Something like this:
<template>
<div>
<CardSubComponent
v-for="(card, index) in cards"
:key="index"
:card="card"
#cardSaveError="cardError(card)"
#cardSaveSuccess="cardSuccess(card)"
/>
</div>
</template>
export default {
methods: {
cardError(card) {
// TODO: Add error handler here
console.log('error called with:', card.id);
},
cardSuccess(card) {
// TODO: Add success handler here
console.log('success called with:', card.id);
},
},
};
In the child component there are two things wrong:
cardSequenceID was not properly initialized (you were using = instead of :)
You were missing this in this.card.id to identify the id for your axios call
export default {
props: ['card'],
data() {
return {
sequenceArray: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
cardSequenceID: `sequence${card.id}`,
};
},
methods: {
changeSequence(rule) {
axios
.post(
'/api/card/changeSequence',
{
cardID: this.card.id,
weight: document.getElementById(`sequence${cardID}`).value,
},
{
headers: {
Authorization: `Bearer ${localStorage.getItem('token')}`,
},
}
)
.then(response => {
if (response.data === 'success') {
this.$emit('cardSaveSuccess');
} else {
this.$emit('cardSaveError');
}
});
},
},
};
After reading many examples and the documentation from Vue, Vuex and Vue-Router I did this project: https://jsfiddle.net/junihh/30wda1em/5/
Even all goes fine when I try to load a row from Post section, the value come empty from the Vuex store. Here the component:
const PostContent = Vue.component('postcontent', {
data() {
return {
post: this.$store.state.currentPost
// post: {
// title: 'The title',
// content: 'The content for test.'
// }
}
},
template: getTemplate('#tpl-postcontent')
});
Here the component that update the state.currentPost value and call the "postcontent" component.
const Posts = Vue.component('posts', {
data() {
return {
url_path: '/posts/content',
rows: this.$store.state.dataAll
}
},
methods: {
openPost: function(e)
{
let rowID = e.currentTarget.getAttribute('data-rowid');
let dataAll = this.$store.state.dataAll;
let currentPost = dataAll.filter(row => (row.id == rowID))[0];
this.$store.state.currentPost = currentPost;
}
},
template: getTemplate('#tpl-posts')
});
Any help here? I'm stuck on that issue.
You need to use a computed property to gather the information from your store with reactivity:
const PostContent = Vue.component('postcontent', {
computed: {
post() {
return this.$store.state.currentPost
}
},
template: getTemplate('#tpl-postcontent')
});
Also try to avoid mutating state outside mutation handler. You can add a mutation to set your currentPost like this:
<template id="tpl-posts">
...
<li v-for="row in rows" :key="row.id">
<router-link :to="url_path" #click.native="openPost(row.id)">
{{ row.title }}
</router-link>
</li>
...
</template>
const Posts = Vue.component('posts', {
//...
methods: {
openPost: function(id)
{
this.$store.commit('SET_CURRENT_POST', id)
}
},
template: getTemplate('#tpl-posts')
});
const store = new Vuex.Store({
state: {
dataAll: {},
currentPost: {}
},
mutations: {
SET_CURRENT_POST: function(state, id) {
let post = state.dataAll.find(data => data.id === id)
state.currentPost = post
}
}
});
fiddle
I want to display total views for each id (1, 2, 3) by call api use axios like this:
<f7-block>
<f7-col
:key="index"
v-for="(items, index) in list">
Total views: {{countView(items.id)}}
</f7-col>
export default {
data(){
list: [];
// list = [{id: 1}, {id: 2}, {id: 3}]
},
methods(){
async countView(id){
let url = 'xxx';
let filter = {
where: {
quizId: id
}
}
try{
let res = await axios.get(url, filter);
return res.data.countViews;
} catch(error) {
}
}
}
}
How to use vue async data to display number of views instead {} ?
There is a better way, which is creating a custom component for each item. Then calling countView inside each custom component:
TotalView.vue
<template>
<span v-if="count !== null">
{{ count }}
</span>
</template>
<script>
export default {
name: 'TotalView'
props: ['itemId'],
data: () => ({
count: null
}),
created() {
this.countView(this.itemId)
},
methods: {
async countView(id){
let url = 'xxx';
let filter = {
where: {
quizId: id
}
}
try{
let res = await axios.get(url, filter);
this.count = res.data.countViews
} catch(error) {
}
}
}
}
</script>
and use that in your component:
<f7-block>
<f7-col
:key="index"
v-for="(items, index) in list">
Total views: <total-view :itemId="items.id" />
</f7-col>