VueJS Loop remaining - javascript

I have a little problem. I would like to create a loop to display my articles the problem is that. I want it to never be empty basically if I have 0 articles but. I want 10 this one will display 10 skeleton if I have 1 item this will remove 1 skeleton to replace it with my item.
I don't know if I was clear enough so here is a little picture.
Basically I have 6 dark gray rectangles but if I have 1 item I would only have 5 if I have 6 I would have no dark gray rectangle at all.

I use
<transition-group name="fade" tag="div" class="badge__grid">
<template v-for="(n, i) in badges.data">
<div :key="n + i" class="badge__item">
<img
class="badge__item--picture"
:src="
'https://images.habbo.com/c_images/album1584/' +
n.badge_code +
'.gif'
"
:alt="n.badge_code"
/>
<span class="badge__item--code">
{{ n.badge_code }}
</span>
</div>
</template>
<div
v-for="i in badges.remaining"
:key="i"
class="badge__item badge__item--empty"
></div>
</transition-group>
It works but I find it too many and I can't simplify my code

You have to use two loops. Using a computed property you can simplify it a bit further:
<div v-for="a in articles" >
<h4>{{a.title}}</h4>
</div>
<div v-for="r in remaining" >
</div>
and the computed property:
remaining(){
let rem = 10 - this.articles.length;
return(rem > 0?rem:0)
}
Code Sandbox: https://codesandbox.io/s/empty-cherry-vy9fw?file=/src/components/Looper.vue:516-599

Related

How to start loop from index 1 instead of index 0 in *ngFor loop in angular?

I am currently getting some data from a dummy api, data from response starts from 1 and the index is currently starting from 0.
How I can start the index loop from 1 instead of 0?
Following the html for *ngFor below:
component.html
<div class="all-users" >
<div class="nested-items" *ngFor="let data of flattenedResponse[0]; let i=index; " (click)="switchUsers(i)">
<h4>{{i}}</h4>
<img src="{{data.image}}" alt="profile">
</div>
</div>
Why not just do the following:
<div class="all-users" >
<div class="nested-items" *ngFor="let data of flattenedResponse[0]; let i=index; " (click)="switchUsers(i)">
<h4>{{i + 1}}</h4>
<img src="{{data.image}}" alt="profile">
</div>
</div>
I don't know of a way to change where the index starts, but this way works fine for most cases
How I can start the index loop from 1 instead of 0
The correct answer here is... you can't.
So there are some suggestions already, but personally I would avoid hacking your markup to get round the data structure the api returns. It's generally a bad idea to make your markup need to know too much about what your server does.
If the array your api returns is not in the best format for your markup - just create a new data array which is, and point your markup at that. It's best to do this in a service at the point you handle the api result, but if nothing else, do it in your .ts file.
have you try :
<div class="all-users">
<div class="nested-items" *ngFor="let data of flattenedResponse[0]; let i=index; " (click)="switchUsers(i+1)">
<h4>{{i+1}}</h4> <img src="{{data.image}}" alt="profile">
</div>
</div>
or you can just skip first element like this :
<div class="nested-items" *ngFor="let data of flattenedResponse[0] | slice:1; let i=index; " (click)="switchUsers(i)"></div>
You can skip first index explicitly with *ngIf:
<div class="all-users" >
<div class="nested-items" *ngFor="let data of flattenedResponse[0]; let i=index;" (click)="switchUsers(i)">
<ng-container *ngIf="index !== 0">
<h4>{{i}}</h4>
<img src="{{data.image}}" alt="profile">
</div>
</ng-container>
</div>
</div>

Is there a way to add the attribute loading="lazy" only in certain conditions?

I am using the relativly new HTML attribute loading="lazy". I only want it to be enabled when the number of images rendered on the page excedes 10. I am building my website using Vue.js v3. I am loading the images with a v-for card like this:
<div
class="gallery-panel"
v-for="photo in filteredPhotos"
v-bind:key="photo.id"
:class="`${photo.display}`"
>
<a #click="this.$router.push(`/Album/${album}/Photo/${photo.id}`)">
<img
loading="auto"
v-bind:src="thumbUrl(photo.filename, album)"
v-bind:alt="`${photo.filename}`"
/>
</a>
</div>
Where filteredPhotos is an array with image information from a JSON file.
Is there a way to change the loading="auto" to loading="lazy" when the number of photos is going to be more than 10?
Well, you can simply add an index attribute to your v-for to check whether the length of your array exceeding 10 or not (Since the index will begin from 0 you should set your condition to > 9). Then you can make your loading attribute conditional like this:
<img :loading="index > 9 ? 'lazy' : 'auto'" />
So your final code should be something like this:
<div class="gallery-panel" v-for="(photo, index) in filteredPhotos" v-bind:key="photo.id" :class="`${photo.display}`">
<a #click="this.$router.push(`/Album/${album}/Photo/${photo.id}`)">
<img :loading="index > 9 ? 'lazy' : 'auto'" v-bind:src="thumbUrl(photo.filename, album)" v-bind:alt="`${photo.filename}`" />
</a>
</div>
You can use index of v-for e.g
<div
class="gallery-panel"
v-for="(photo, index) in filteredPhotos"
v-bind:key="photo.id"
:class="`${photo.display}`"
>
<a #click="this.$router.push(`/Album/${album}/Photo/${photo.id}`)">
<img
:loading="index > 9 ? 'lazy' : 'auto'"
v-bind:src="thumbUrl(photo.filename, album)"
v-bind:alt="`${photo.filename}`"
/>
</a>
</div>

Vue How to add phone number call link

In my vuejs-application, I have a table with some users which also includes a phone number. Now I want to make the phone number clickable, but for some reason it does not work.
This is what I got:
<router-link class="table-row" tag="div" v-for="entry in paginate(filteredData)" :key="entry.id" :to="routePath + entry.id">
<div class="table-data" v-for="column in columns" v-bind:key="column.id" :class="column.key">
<div v-if="column.key == 'phone'" :class="column.key">
<span>
{{entry[column.key]}}
</span>
</div>
<div v-else>
<span>{{entry[column.key]}}</span>
</div>
</div>
</router-link>
the result is 12448877 - which is the same route as the <router-link> - how can I achieve that the phone number gets displayed without using any plugin/package?
I think this line should be
{{entry[column.key]}}
like
<a :href="`tel:${entry[column.key]}`">{{entry[column.key]}}</a>
you can simply use this way
<a href="tel:{entry[column.key]}" > {{entry[column.key]}} </a>
avoiding too much useless code

Hide String if Child is empty after filtering v-for list in VueJS

I'm stuck trying to hide a title in vuejs.
I have a v-for which goes trough Months and it give me 12 div
in this v-for, i have another v-for with my data which have date for each item
and with a v-if, i match the month of the item with the month of my first v-for and display it.
But i add filter on it and it's possible for example that April has no data after filtering.
So i tried to hide the name of the month (date.nom in my code) which is in the first, if the second loop is empty but with the v-if, the div outside the 2nd loop is not really empty.
I tried to fin a qway to add it in my second v-for but it displays, logically, the name on top of each item.
maybe there is a way to display it for the first item only ?
<v-row v-for="(date,index) in dates" :key="date.id">
<v-col cols="12" v-if="monthDate >= date.nombre">
<span>{{date.nom}}</span>
</v-col>
<div v-for="(histo, index) in orderBy(itemsFiltered, 'date_publication', -1)" :key="index"">
<div v-if="getYearDate(histo.date_publication) == year">
<div v-if="getMonthDate(histo.date_publication) == date.nombre && monthDate >= date.nombre">
<v-col cols="12">
<div>
<div class="parent-img-histofull">
<img :src="histo.cover_medium" lazy class="img-album-histofull" />
<span >{{getDayDate(histo.date_publication)}}</span>
</div>
</div>
</v-col>
</div>
</div>
</div>
</v-row>

Loop variables in Vue.js similar to AngularJS

I my vue.js application I want to know the last repeated element in a v-for directive. I know there is something similar in angularjs with the $last loop variable in its ngRepeat directive:
<div ng-repeat="(key, value) in myObj">
<span ng-if="$last">Shows if it is the last loop element</span>
</div>
Is there any equivalent in vue.js which I am not aware of or do I have to implement some own logic?
It works for me :)
<div v-for="(item, index) in items">
<span v-if="index === (items.length-1)">This is the last loop element</span>
</div>
you can also put the logic in a computed property :
<div v-for="(value, key) in myObj">
<span v-if="key === last">This is the last loop element : {{ value }}</span>
</div>
//...
computed: {
last() {
let keys = Object.keys(this.myObj)
return keys.slice(-1)[0]
}
}
fiddle here
There's no equivalent for this in Vue, no.
You can do this instead, check for index if it is last
<div v-for="(value, key, index) in myObj">
<div v-if="index === Object.keys(myObj).length-1"> my content</div>
</div>
You can try this
<div v-repeat="myObj">
<span v-if="$index === (myObj.length-1)">Shows if it is the last loop element</span>
</div>
Based on #Nisha answer I have been doing this:
<template v-for="(obj, index) in originalData">
<template v-if="index == 0 || (index >= 1 && obj.valueA !== originalData[index - 1].valueA)">
{{ obj.valueA }}
</template>
</template>
I want the loop to always happen the first time, but to conditionally check a value for every loop afterwards. I am using <template> tags to avoid outputting unnecessary markup. In my example originalData is an array of objects.

Categories