How to get data from JSON? My v-for loop is not working - vue.js

I'm trying to get data from a external json but the v-for loops does not work. Previously, with the same code I was able to show the data I don't know whats happens now.
This is the code i'm using:
`
<div class="row">
<div class="col-sm-4">
<h1>Llista Valors</h1>
<div class="card" style="margin-bottom: 2%" v-for="dada in list">
<div class="card-header">
Hora: {{dada.lists.hora}}
Preu: {{dada.lists.preu}}
</div>
</div>
</div>
<div class="col-sm-8">
<h1>JSON</h1>
<pre>
{{ $data }}
</pre>
</div>
</div>
</div>`
`
var url = "http://172.17.100.3/api/tarifa2.php";
new Vue({
el: '#main',
created: function () {
this.getApi();
},
data: {
lists: []
},
methods: {
getApi: function () {
this.$http.get(url).then(function (response) {
this.lists = response.body.valors;
});
}
}
});
</script>`
This is what i get:

Your data should be called list:[], not lists:[] to match your template. Or rename your template to in lists.

For each dada in lists, there is no dada.lists.hora or dada.lists.preu. There is only dada.preu or dada.hora.
Each dada in lists is like saying lists[0] (or whatever index).
So try changing the template to just {{data.hora}} etc as needed (after doing the list/lists adjustment per above).

Related

How can I add a task to a list in my Vue 2 to-do app?

I am trying to add a task to a tasklist in Vue based on the input and add task button, but I keep getting the error "taskList is not defined". Does anybody see how to fix this problem? The code is as following:
<template>
<div id="input">
<form>
<input v-model="task.name">
<button v-on:click="addTask" v-bind:value="task.name">+</button>
</form>
<ol>
<div v-for="task in taskList" :key="task.id">
{{ task.name }}
<div v-if="task.completed">
<h2> Done </h2>
</div>
<div v-else>
<h2> Not done</h2>
</div>
</div>
</ol>
</div>
</template>
<script>
export default {
name: 'AddTask',
data: function() {
return {
taskList: [
{
name: 'task', completed: false, id: 3
}
] }
},
methods: {
addTask: function (task) {
taskList.push(task);
alert('test');
}
}
}
</script>
Ps. any other Vue tips are welcome as well.
You need to separate out your taskList and the current task you're adding, decouple it as a new object, then add it to your taskList array.
When referring to items in your data object you need to use the this keyword – e.g this.taskList rather than taskList:
new Vue({
el: "#app",
data: {
id:1,
taskList: [],
currentTask:{
completed:false,
name:'',
id:this.id
}
},
methods: {
addTask: function() {
let newTask = {
completed:this.currentTask.completed,
name:this.currentTask.name,
id:this.currentTask.id
}
this.taskList.push(newTask);
this.id++;
//alert('test');
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div id="input">
<ol>
<li v-for="task in taskList" :key="task.id">
{{ task.name }}
<input type="checkbox"
:checked="task.completed"
#change="task.completed = !task.completed">
<span v-if="task.completed">
Done
</span>
<span v-else>
Not Done
</span>
</li>
</ol>
<input type="text" v-model="currentTask.name">
<button v-on:click="addTask">+</button>
</div>
</div>
From what I see in your template you use tasklist but you define it as taskList You will want to make sure your names are in the same case. Usually you'll see camelCase in vue, but other popular ones are snake_case and PascalCase

Dynamic computed property in html

I'm using php for creating set of computed properties in my app based on ID property of each object in my main array of data stored in property deals. So i have now computed properties like list_10_deals_cnt, list_20_deals_cnt, list_30_deals_cnt etc. Now, how can I create these dynamic created properties in span with class dials__cnt while looping my array of data? {{'list_'+el.id+'_deals_cnt'}} is not working as i wish, its display just a string like list_10_deals_cnt instead to display a computed value.
P.S. sorry about my english.
<div class="dials" id="app">
<div class="dials__column" v-for="(el, index) in deals">
<div class="dials__header">
<div>{{el.title}}</div>
<div>сделок: <span class="dials__cnt">{{`'list_'+el.id+'_deals_cnt'`}}</span>, <span></span> руб</div>
</div>
<draggable drag-class="draggable-ghost__class" class="dials__block" :list="el.items" group="deal" #change="log(el.id, $event)">
<div
class="dials__card"
v-for="(el, index) in el.items"
:key="el.title"
>
<div class="card__header">
<div class="card__title">{{ el.customer_name }}, {{ el.customer_company_name }}</div>
<div class="card__date">{{ el.created_at }}</div>
</div>
<div class="card__body">
<a :href="'/deal/detail/'+el.id" class="card__link">{{ el.title }}</a>
</div>
<div class="card__footer">
<span class="card__price">{{ el.price }} руб </span>
<span v-for="(tag, index) in el.tags" class="card__tag">{{ tag }}</span>
</div>
</div>
</draggable>
</div>
</div>
<script>
new Vue({
el: '#app',
data: {
deals: <?php echo json_encode($deals, JSON_UNESCAPED_UNICODE); ?>
},
computed: {
<?php
foreach ($deals as $k=>$v) {
echo 'list_'.$v->id.'_deals_cnt: function() {return this.deals['.$k.'].items.length},';
}
?>
},
methods: {
log: function(id, evt) {
if (evt.hasOwnProperty('added')) {
let dealID = evt.added.element.id;
console.log('сделка ID '+dealID+' перемещена в статус '+id+'. Отправляем аякс запрос на обновление статуса в бд');
// ajax for update
}
}
}
});
</script>
Hi
Problem 1
you will not be able to get the value of computed property by using this
{{`'list_'+el.id+'_deals_cnt'`}}
for the same reason as console.log('vari' + 'able') doesn't print out value of variable to the console.
( Vue evaluates whatever is in between {{ }} as an expression ).
Solution
I suppose, you can either use the deals property directly in html as shown below without using a computed property
<div class="dials__column" v-for="(el, index) in deals">
<div class="dials__header">
<div>{{el.title}}</div>
<div>сделок: <span class="dials__cnt">{{ el.items.length }}</span>, <span></span> руб</div>
</div>
----------- rest of the code
or you can create a computed property based on deals data property and use that to loop in html using v-for.
Problem 2
I don't think below code is valid php string. although it becomes irrelevant if you use first solution above.
<?php
foreach ($deals as $k=>$v) {
echo 'list_'.$v->id.'_deals_cnt: function() {return this.deals['.$k.'].items.length},';
}
?>
the ' inside ['.$k.'] should be escaped.
Solution
echo 'list_'.$v->id.'_deals_cnt: function() {return this.deals[\'.$k.\'].items.length},';

Pagination for dynamically generated content

Consider the following code.
<template>
<div class="card-container">
<div class="row">
<div class="col s12">
<a #click="addCard()">Add Card</a>
</div>
</div>
<div class="row">
<div v-for="(card, index) in cards" :key="index">
<div class="card-panel">
<span class="card-title">Card Title</span>
<div class="card-action">
<a #click="deleteCard(index)">Delete</a>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
data: function() {
return {
cards: []
}
},
methods: {
addCard: function() {
this.cards.push({
description: "",
});
},
deleteCard: function(index) {
this.cards.splice(index,1);
}
},
}
</script>
How to make the cards be grouped so that there are 4 rows and each row contains 4 cards? Upon reaching the fourth row the new cards go to the next page.
I thought I could use something like this codepen.io/parths267/pen/bXbWVv
But I have no idea how to get these cards organized in a pagination system.
The view would look something like this
My solution is calculate all cards of current page ahead.
Uses computed property to calculate the relate values which the pagination needs.
In below simple example (it is only one example, you need to add necessary validations as your actual needs, like boundary conditions) :
pages is the page count
cardsOfCurPage is the cards in current page
Then add one data property=pageIndex save the index of current page.
Anyway, keep data-driven in your mind.
List all arguments your pagination needs,
then declare them in data property or computed property.
execute the necessary calculations in computed property or methods.
PS: I don't know which css framework you uses, so I uses bootstrap instead.
Vue.component('v-cards', {
template: `<div class="card-container">
<div class="row">
<div class="col-12">
<a class="btn btn-danger" #click="addCard()">Add Card</a><span>Total Pages: {{pages}} Total Cards: {{cards.length}} Page Size:<input v-model="pageSize" placeholder="Page Size"></span>
</div>
</div>
<div class="row">
<div v-for="(card, index) in cardsOfCurrPage" :key="index" class="col-3">
<div class="card-panel">
<span class="card-title">Card Title: {{card.description}}</span>
<div class="card-action">
<a #click="deleteCard(index)">Delete</a>
</div>
</div>
</div>
</div>
<p><a class="badge" #click="gotoPrev()">Prev</a>- {{pageIndex + 1}} -<a class="badge" #click="gotoNext()">Next</a></p>
</div>`,
data: function() {
return {
cards: [],
pageSize: 6,
pageIndex: 0
}
},
computed: {
pages: function () {
return Math.floor(this.cards.length / this.pageSize) + 1
},
cardsOfCurrPage: function () {
return this.cards.slice(this.pageSize * this.pageIndex, this.pageSize * (this.pageIndex+1))
}
},
methods: {
addCard: function() {
this.cards.push({
description: this.cards.length,
});
},
deleteCard: function(index) {
this.cards.splice(index,1);
},
gotoPrev: function() {this.pageIndex -=1},
gotoNext: function() {this.pageIndex +=1}
},
})
new Vue({
el: '#app',
data() {
return {
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css" rel="stylesheet" />
<div id="app">
<v-cards></v-cards>
</div>

Bind class item in the loop

i want to bind my button only on the element that i added to the cart, it's working well when i'm not in a loop but in a loop anything happen. i'm not sure if it was the right way to add the index like that in order to bind only the item clicked, if i don't put the index every button on the loop are binded and that's not what i want in my case.
:loading="isLoading[index]"
here the vue :
<div class="container column is-9">
<div class="section">
<div class="columns is-multiline">
<div class="column is-3" v-for="(product, index) in computedProducts">
<div class="card">
<div class="card-image">
<figure class="image is-4by3">
<img src="" alt="Placeholder image">
</figure>
</div>
<div class="card-content">
<div class="content">
<div class="media-content">
<p class="title is-4">{{product.name}}</p>
<p class="subtitle is-6">Description</p>
<p>{{product.price}}</p>
</div>
</div>
<div class="content">
<b-button class="is-primary" #click="addToCart(product)" :loading="isLoading[index]"><i class="fas fa-shopping-cart"></i> Ajouter au panier</b-button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
here the data :
data () {
return {
products : [],
isLoading: false,
}
},
here my add to cart method where i change the state of isLoading :
addToCart(product) {
this.isLoading = true
axios.post('cart/add-to-cart/', {
data: product,
}).then(r => {
this.isLoading = false
}).catch(e => {
this.isLoading = false
});
}
You can change your isLoading to an array of booleans, and your addToCart method to also have an index argument.
Data:
return {
// ...
isLoading: []
}
Methods:
addToCart(product, index) {
// ...
}
And on your button, also include index:
#click="addToCart(product, index)"
By changing isLoading to an empty array, I don't think isLoading[index] = true will be reactive since index on isLoading doesn't exist yet. So you would use Vue.set in your addToCart(product, index) such as:
this.$set(this.isLoading, index, true)
This will ensure that changes being made to isLoading will be reactive.
Hope this works for you.
add on data productsLoading: []
on add to cart click, add loop index to productsLoading.
this.productsLoading.push(index)
after http request done, remove index from productsLoading.
this.productsLoading.splice(this.productoading.indexOf(index), 1)
and check button with :loading=productsLoading.includes(index)
You can create another component only for product card,
for better option as show below
Kindly follow this steps.
place the content of card in another vue component as shown below.
<!-- Product.vue -->
<template>
<div class="card">
<div class="card-image">
<figure class="image is-4by3">
<img src="" alt="Placeholder image">
</figure>
</div>
<div class="card-content">
<div class="content">
<div class="media-content">
<p class="title is-4">{{product.name}}</p>
<p class="subtitle is-6">Description</p>
<p>{{product.price}}</p>
</div>
</div>
<div class="content">
<b-button class="is-primary" #click="addToCart(product)" :loading="isLoading"><i class="fas fa-shopping-cart"></i> Ajouter au panier</b-button>
</div>
</div>
</div>
</templete>
<script>
export default {
name: "Product",
data() {
return {
isLoading: false
}
},
props: {
product: {
type: Object,
required: true
}
},
methods: {
addToCart(product) {
this.isLoading = true
axios.post('cart/add-to-cart/', {
data: product,
}).then(r => {
this.isLoading = false
}).catch(e => {
this.isLoading = false
});
}
}
}
</script>
Change your component content as shown below.
<template>
<div class="container column is-9">
<div class="section">
<div class="columns is-multiline">
<div class="column is-3" v-for="(product, index) in computedProducts">
<product :product="product" />
</div>
</div>
</div>
</div>
</templete>
<script>
import Product from 'path to above component'
export default {
components: {
Product
}
}
</script>
so in the above method you can reuse the component in other components as well.
Happy coding :-)

Vue UI not updating using axios after closing semantic modal

I have a vue that displays an html table based on an axios call to get a list of points from a db table during mounted. I also have a semantic modal that I use in the same vue to add records to the points db table. During the modal's onHidden, I make the same axios call to update the html table in order to display the new record. However, the html table isn't updating.
<template>
<div>
<h1 class="ui header">Points</h1>
<button class="ui icon button" v-on:click="showModal()">
<i class="add icon"></i>
</button>
<div class="ui modal">
<div class="header">
Header Text
</div>
<div class="content">
<div class="ui form">
<div class="field">
<label>Date</label>
<datepicker v-model="newPoint.earnedDate" id="earned_date"></datepicker>
</div>
<div class="ui grid">
<div class="four wide column">
<div class="ui dropdown" id="name_dd">
<div class="text">Name</div>
<i class="dropdown icon"></i>
</div>
</div>
<div class="eight wide column">
<div class="ui dropdown" id="rule_dd">
<div class="text">Rule</div>
<i class="dropdown icon"></i>
</div>
</div>
</div>
</div>
</div>
<div class="actions">
<div class="ui black deny button">
Cancel
</div>
<div class="ui positive right labeled icon button">
Save
<i class="checkmark icon"></i>
</div>
</div>
</div>
<table class="ui celled table">
<thead>
<tr>
<th>Date</th>
<th>Name</th>
<th>Points</th>
</tr>
</thead>
<tbody>
<tr v-for="point in points">
<td>{{point.earnedDate}}</td>
<td>{{point.name}}</td>
<td>{{point.points}}</td>
</tr>
</tbody>
</table>
</div>
</template>
<script>
import axios from "axios";
import Datepicker from 'vuejs-datepicker';
export default {
name: 'earnings',
components: {
Datepicker,
},
data() {
return {
points: [],
newPoint: {
earnedDate: "1/1/1970",
name: "",
points: ""
},
earners: [],
errors: [],
}
},
methods: {
showModal: function() {
$('.ui.modal')
.modal('show');
},
},
mounted () {
//THIS CALL UPDATES THE HTML TABLE
axios
.get('api/earnings')
.then(response => (this.points = response.data));
//Set the modal approve and deny callbacks
$('.ui.modal')
.modal({
closable: true,
onDeny: function () {
return;
},
onApprove: function () {
/*****************************************
* Add the new points using web API
*****************************************/
//Get the modal values
var earned_date = $('#earned_date').val();
var earner_id = $('#name_dd').dropdown('get value');
var rule_id = $('#rule_dd').dropdown('get value');
//Call the post route
axios
.post('api/earnings', { earnedDate: earned_date, earnerId: earner_id, ruleId: rule_id})
.then(response => {})
.catch(e => {
console.log(e)
})
return;
},
onHidden: function () {
//THIS CALL DOES NOT UPDATE THE HTML TABLE
axios
.get('api/earnings')
.then(response => (this.points = response.data));
}
});
//Load the name dropdown on the add new points modal
$('.four.wide.column .ui.dropdown')
.dropdown({
apiSettings: {
// this url just returns a list of tags (with API response expected above)
url: 'api/earners/semantic_dd',
cache: false
},
});
//Load the rule dropdown on the add new points modal
$('.eight.wide.column .ui.dropdown')
.dropdown({
apiSettings: {
// this url just returns a list of tags (with API response expected above)
url: 'api/rules/semantic_dd',
cache: false
},
});
},
created: function () {
// Remove the modal so it doesn't duplicate when the user navigates away from the component
// and then returns to it
$('.ui.modal').remove();
}
}
</script>
It looks like the this is not available during the axios call using =>. At the start of mounted I set var self = this and used self.points instead of this.points during onHidden. The binding to the template is working now.
Edit after-the-fact
In Vue.js, use this.$data.property-name which points to the component's properties. See documentation at https://v2.vuejs.org/v2/api/#data