Get lowest price from vue js array - javascript

I have this array which loops through all the trips for a listing.
<span v-for="price in listing.trips">
<div class="price">{{ price.cost }} </div>
</span>
Is it possible to apply a filter, just to take the lowest priced trip?
I tried:
<span v-for="price in listing.trips">
<div class="price">{{ Math.min(price.cost) }} </div>
</span>

Maybe you can just just create computed property?
new Vue({
....
computed: {
cheapestTrip: function () {
return this.listing.trips.sort(trip=>trip.price)[0];
}
}
});
And then use it like this:
<span>
<div class="price">{{ cheapestTrip }} </div>
</span>
You can also omit [0] if you want to have array. In this case use your view snippet.

Related

How to increment a loop in Vue

I've got a multi-object array and I'd like to have it loop through a particular group of objects.
This is my code so far
<template>
<div>
<h1>My Test App</h1>
<button v-on:click="getHockeyData">Get Team Data</button>
<div v-for="hockeyData in hockeyDataList" :key="hockeyData.id" >
<p>{{ hockeyDataList.teams[0].roster.roster[1].person.fullName }}</p>
</div>
</div>
</template>
<script>
import axios from 'axios';
export default {
name: "Weather",
data() {
return {
hockeyDataList: []
};
},
methods: {
getHockeyData() {
axios.get("https://statsapi.web.nhl.com/api/v1/teams/21?expand=team.roster").then(response => (this.hockeyDataList = response.data));
}
}
};
</script>
I know the loop won't work in it's current state. The portion of my output within the loop that I need to increment is roster[1] - I'm new to Vue so not sure how to use the v- commands to increment that 1 until there are no more instances of it.
Any help or feedback would be greatly appreciated!
Just loop over the others, really you want to start from teams as that's the way the JSON is structured
Example using various parts of the JSON, view the file for others.
<div v-for="team in hockeyDataList.teams" :key="team.id">
<h1>{{ team.name }}</h1>
<div>{{ team.venue.name }}, {{ team.venue.city }}</div>
<div>Created in: {{ team.firstYearOfPlay }}</div>
<div>Division: {{ team.division.name }} - Conference: {{ team.conference.name }}</div>
<h2>Roster</h2>
<div v-for="player in team.roster.roster" :key="team.id + '-' + player.person.id">
<h3>{{ player.person.fullName }}</h3>
<div>
Number: {{ player.jerseyNumber }} - Position: {{ player.position.name }} {{ player.position.type }}
</div>
</div>
</div>
<div v-for="team in hockeyDataList.teams" :key="team.id" >
<p v-for="roster in team.roster.roster :key="roster.id">
{{ roster.person.fullName }}
</p>
</div>
It's not necessary, but if you want to, you can get index in a v-for as well:
<div v-for="(team, index) in hockeyDataList.teams" :key="team.id" >
<p>
{{ hockeyDataList.teams[index].roster.roster[1].person.fullName }}
</p>
</div>
v-for increments the index automatically for you. The problem is that the api returns a json and not an array we can iterate on. By looking at the api response, we can see that teams is the array we can iterate on.
<v-for="(team, i) in hockeyDataList.teams" :key="i">
where index automatically increments until the end of list. We can then iterate through the roster.
<v-for="(roster, j) in team.roster.roster" :key="j">
Putting it all together
<div v-for="(team, i) in hockeyDataList.teams" :key="i">
<div v-for="(roster, j) in team.roster.roster" :key="j">
<p>{{ hockeyDataList.teams[i].roster.roster[j].person.fullName }}</p>
</div>
</div>

total basket reactive vuejs

Is it possible to make the total reactive? for example, in my basket I have two articles for a price of 18 €, if I add an article I want the total to change without updating the page.
my property calculate total returns me for two options: 14,0012,00 I would have liked it to calculate the price of the two options dynamically: 14,00 + 12,00 = 26
thank you for help
<template>
<div>
<div >
<div >
<u>
<li v-for="item in shop" :key="item.id">
{{ item.label}} : {{ item.cost }}€
</li>
</u>
<button #click="addItem">Add</button>
</div>
<div >
<u>
<li v-for="item in shop" :key="item.id">
{{ item.label}} : {{ item.cost }}€
</li>
</u>
</div>
<p>{{ total}}</p>
</div>
</div>
</template>
<script>
computed:{
totalbasket(){
let total = 0
this.shop.forEach(item=>{
total += item.cost
})
return total
}
}
</script>
It looks like the problem is your item.cost is a string, not a float.
Parsing the cost to floats before adding them might do the trick:
totalbasket(){
let total = 0
this.shop.forEach(item=>{
total += parseFloat(item.cost)
})
return total
}
And at the moment you have {{total}} in your template. You probably want to change this to {{totalBasket}}.
As mentioned by tony19 javascripts parseFloat function does not recognize commas as decimal points. There's a question about how to solve it here.
I would add an Add Button to each item you want to add and then call addItem(item). It would be something like this:
...
<li v-for="item in shop" :key="item.id">
{{ item.label}} : {{ item.cost }}€
<button #click="addItem(item)">Add</button>
</li>
Then, the addItem method adds the item, which would trigger totalbasket:
methods: {
addItem(item) {
this.shop.push(item)
},
...
}
And in your template, you should print totalbasket instead of total:
<p>{{ totalbasket }}</p>
This should do it.

Conditionally hide the nth element of a v-for loop without modifying the array. vue 3 composition api search function

I have a ref variable (foxArticles ), which holds a list that contains 100 items. In a v-for loop i loop over each value. As a result, i have 100 values rendered on the page.
<template>
<div class="news_container">
<div
v-for="article in foxArticles"
v-bind:key="article"
class="article_single_cell"
>
<div
class="news_box shadow hover:bg-red-100 "
v-if="containsKeyword(article, keywordInput)"
>
<div class="news_box_right">
<div class="news_headline text-red-500">
<a :href="article.url" target="_blank">
{{ article.title }}
</a>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
const foxArticles = ref([]);
</script>
I also have a search function, which returns the value, if it includes the passed in keyword. The function is used in the child of the v-for loop.
<div class="search_input_container">
<input
type="text"
class="search_input"
v-model="keywordInput"
/>
</div>
<script>
const keywordInput = ref("");
function containsKeyword(article, keywordInput) {
if (article.title.toLowerCase().includes(keywordInput.toLowerCase())) {
return article;
}
}
</script>
The problem is, i can't use .slice() on the foxArticles array in the v-for loop, because that screws up the search functionality, as it returns only the values from the sliced range.
How can i have the access the all of the values of the array, while not rendering all 100 of returned articles on the initial load?
Any suggestions?
I think your approach will make it incredibly complex to achieve. It would be simpler to always iterate over some set, this set is either filtered based on a search-term, or it will be the first 100 items.
I'm not very familiar yet with the Vue 3 composition api so I'll demonstrate with a regular (vue 2) component.
<template>
<div class="news_container">
<div
v-for="article in matchingArticles"
v-bind:key="article"
class="article_single_cell"
>
... news_box ...
</div>
</div>
</template>
<script>
export default {
...
computed: {
matchingArticles() {
var articles = this.foxArticles;
if (this.keywordInput) {
articles = articles.filter(article => {
return this.containsKeyword(article, this.keywordInput)
})
} else {
// we will limit the result to 100
articles = articles.slice(0, 100);
}
// you may want to always limit results to 100
// but i'll leave that up to you.
return articles;
}
},
....
}
</script>
Another benefit is that the template does not need to worry about filtering results.
ok, so i kind of came up with another solution, for which you don't have to change the script part...
instead of having one v-for loop , you can make two of them, where each one is wrapped in a v-if statement div
The first v-if statement says, If the client has not used the search (keywordInput == ''), display articles in the range of (index, index)
The second one says = If the user has written something (keywordInput != ''), return those articles.
<template>
<div class="news_container">
<!-- if no search has been done -->
<div v-if="keywordInput == ''">
<div
v-for="article in foxArticles.slice(0, 4)"
v-bind:key="article"
class="article_single_cell"
>
<div class="news_box shadow hover:bg-red-100 ">
<div class="news_box_right">
<div class="news_headline text-red-500">
<a :href="article.url" target="_blank">
{{ article.title }}
</a>
</div>
</div>
</div>
</div>
</div>
<!-- if searched something -->
<div v-else-if="keywordInput != ''">
<div
v-for="article in foxArticles"
v-bind:key="article"
class="article_single_cell"
>
<div
class="news_box shadow hover:bg-red-100 "
v-if="containsKeyword(article, keywordInput) && keywordInput != ''"
>
<div class="news_box_right">
<div class="news_headline text-red-500">
<a :href="article.url" target="_blank">
{{ article.title }}
</a>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
im not sure how this impacts performance tho, but that's a problem for another day

how to add :key in v-for with multiple divs

I want to make v-for loop without any html element so I decided to use <template as parent. I don't know to assign :key for this loop. I can't assign it to template and to every div inside loop. Any ideas?
<template
v-for="{ id, text, option, percentage, value } in reports"
>
<div class="table-row__index">
{{ id }}
</div>
<div class="table-row__title">
<p>{{ text }} - <strong>{{ option }}</strong></p>
</div>
<div class="table-row__info">
{{ percentage }}%
</div>
<div class="table-row__info">
{{ value }}
</div>
</template>
As a good practice we should always have a parent element inside. But due to your constraints, it's okay to use for loop on template as given in official docs
https://v2.vuejs.org/v2/guide/list.html#v-for-on-a-lt-template-gt
In this case, any keys have to be added to child elements/components and this is what officially recommended.
See this example and please add keys to your div's inside template.
new Vue({
el: '#app'
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<template v-for="n in 5">
<span :key="'number' + n">{{ n }}</span>
<span :key="'dot' + n">. </span>
</template>
</div>
you just can't have the for loop in your template. The for loop directive is only allowed to be inside the first child component ( or root component) inside your template.
Here is an example of how you can render your loop:
<template>
<div class="cant-have-for-loop-this-is-the-root-component">
<div v-for="{ id, text, option, percentage, value } in reports" :key="{id}">
<div class="table-row__index">
{{ id }}
</div>
<div class="table-row__title">
<p> {{ text }} - <strong>{{ option }}</strong></p>
</div>
<div class="table-row__info">
{{ percentage }}%
</div>
<div class="table-row__info">
{{ value }}
</div>
</div>
</div>
</template>
This runs soomthly, and with no hesitations. And renders with no styling as this screenshots depicts:
Hope this answers what you want to achieve.

Setting a dynamic initial index for template variable using Vue JS

I'm using a CMS to create a front-end form where customers (theatre directors) can define several showtimes. I have a button they click to "add another" input field as needed (sidenote: I don't know how to enable a 'remove last' button using Vue, but that's another topic for another day).
I've gotten that working. But now, once a director has created their showtimes and they come back to edit them, I need the starting index of the Vue counter to be the number of fields that already exist. So if they've defined 3 showtimes already (0,1,2), the first field dynamically placed with Vue should start with 3.
Alternatively, I wonder if it'd be easier to generate an array of the existing data in Vue when the page loads, but I don't know where I'd start with that method.
Here's my HTML and Vue JS:
new Vue({
el: '#app',
data: {
timeslots: [
{
count: 0
}
],
count: 0
},
methods: {
addAnother: function(){
this.timeslots.push({
count: ++this.count
});
}
}
});
<div id="app">
{{ performances }}
<input name="performances[{{ zero_index }}][showtime]" value="{{ showtime }}">
{{ /performances }}
<template v-for="slot in timeslots">
<input name="performances[#{{ slot.count }}][showtime]" value="{{ now }}">
</template>
<div>
<button #click.prevent="addAnother">Add another</button>
</div>
</div>
Any help to move forward is appreciated!
I solved it myself in shorter time than expected using the 2nd method I speculated about in my OP, and using $index instead of an arbitrary integer like count. This method requires the JS to be put directly in my template though, not in a separate file. But that's fine by me.
<div id="oven">
<template v-for="biscuit in oven">
<div>
<input type="text" name="performances[#{{ $index }}][showtime]" value="#{{ biscuit.showtime }}">
</div>
</template>
<span class="add small black btn" #click="addAnother"><i class="fa fa-plus-circle"></i> Add another</span>
</div>
<script>
new Vue({
el: '#oven',
data: {
oven: [
{{ performances }}
{
showtime: "{{ showtime }}"
},
{{ /performances }}
]
},
methods: {
addAnother: function(){
this.oven.push({
showtime: '{{ now modify_date="+2 months" format="Y-m-d" }} 7:30 PM'
});
}
}
});
</script>

Categories

Resources