Vue: Distinct method in foreach component - vue.js

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');
}
});
},
},
};

Related

VueJS - Sorting array of objects coming from the API response

I am trying to assign the variable to the vue-select by response coming from the API.
Here is the scenario i have two components, which is shown below:
FirstPage.vue
<div class="vx-col" v-for="automobile in automobiles" v-bind:key="automobile.id">
<vx-card>
<div class="content">
<vs-button class="btn" #click="$router.push({name: 'Go_to_info', params: {uni_id: automobile.uni_id.toString() }}).catch(err => {})">Touch to see info</vs-button>
</div>
</vx-card>
</div>
....
automobiles: []
....
created () {
this.$http.get('/automobiles')
.then((response) => { this.automobiles = response.data })
}
Info.vue
<div class="select">
<vselect v-model="input1" :options="[{label: [automobiles[$route.params.uni_id].name}]" />
</div>
....
automobiles: []
....
created () {
this.$http.get('/automobiles')
.then((response) => { this.automobiles = response.data })
}
Response from /automobiles:
[
{
"uni_id": 3,
"name": "Benz", <---- This object becomes index[0]
},
{
"uni_id": 1,
"name": "Suzuki", <---- This object becomes index[1]
},
{
"uni_id": 4,
"name": "Audi", <---- This object becomes index[2]
},
{
"uni_id": 2,
"name": "Honda", <---- This object becomes index[3]
}
]
So how can i sort my response using javascript only, and then how can i use the response of FirstPage.vue in the info.vue because i am making the same call in info.vue. Please do help me.
You could create a computed property on your component that sorts the array, and then iterate over that:
created() {
this.fetchAutomobiles();
},
computed: {
automobilesSorted() {
return this.automobiles.sort((a, b) => {
return a.name.localeCompare(b.name);
});
}
},
data() {
return {
automobiles: []
};
},
methods: {
fetchAutomobiles() {
this.$http.get('/automobiles').then((response) => {
this.automobiles = response.data;
});
}
}
<div class="vx-col" v-for="automobile in automobilesSorted" v-bind:key="automobile.id">
<!-- -->
</div>
What you need to do is sort the array by its property after it is received.
For example if you want to sort by automobile's name, you can do this:
created () {
this.$http.get('/automobiles')
.then((response) => {
this.automobiles = response.data.sort((a, b) => a.name - b.name);
})
}

Render named scopedSlot programmatically

I want to move the following template into the render function of my component, but I don't understand how.
This is my template:
<template>
<div>
<slot name="item" v-for="item in filteredItems" :item="item">
{{ item.title }}
</slot>
</div>
</template>
This is my component:
export default {
props: {
items: {
type: Array,
required: true,
},
search: {
type: String,
default: ""
}
},
methods: {
filterByTitle(item) {
if (!("title" in item)) { return false; }
return item.title.includes(this.search);
}
},
computed: {
filteredItems() {
if (this.search.length === "") {
return this.items;
}
return this.items.filter(this.filterByTitle);
}
},
render: function(h) {
// How can I transform the template so that it finds its place here?
return h('div', ...);
}
};
I thank you in advance.
To render scoped slots you can use $scopedSlots. See more here.
Example Code:
...
render(h) {
return h(
'div',
this.filteredItems.map(item => {
let slot = this.$scopedSlots[item.title]
return slot ? slot(item) : item.title
})
)
}
...
JSFiddle

Vue v-model data is from ajax undefined value

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>

[Framework7 Vuejs]Binding asyn data to template with v-for

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>

Reload Data of vue-tables-2 (Vuex)

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>