Dayjs is not updating in vue v-for - vue.js

In a component template I have the following code:
<v-col cols="6" v-for="(value, idx) in data[workingYear].monthValues" :key="idx">
{{ idx }}
<v-row class="ai-center">
<!-- Month -->
<v-col cols="2">{{
dayjs(idx + 1, "M").format("MMM").ucfirst()
}}</v-col>
<!-- Value -->
<v-col cols="10">
<v-text-field
class="mt-0 pt-0"
hide-details="auto"
min="0"
suffix=",00"
type="number"
v-model="data[workingYear].monthValues[idx]"
:label="$t('set')"
:prefix="getCurrency(true)"
:ref="'monthValue' + idx"
:rules="[rules.required, rules.invalid]"
/>
</v-col>
</v-row>
</v-col>
data[workingYear].monthValues is initialized as new Array(12).fill("") so it is an array of 12 empty string elements; ucfirst() is a custom extension to String.prototype that capitalize the first char of a string.
I'm aspecting to find in the first v-col block an incremented month, but the result is the following:
So, why {{ idx }} is incremented for every cycle as aspected but the month is always January? How can I fix this?

It seems that dayjs wants only strings as input date so write this is the solution:
dayjs("" + (idx + 1), "M").format("MMM").ucfirst()
By the way, at this point, the solution posted by Boussadjra Brahim is more elegant and readable, so +1 for the function.
Thanks

I suggest to use a computed property that returns a function which takes the index as parameter and return the month:
<v-col cols="2">{{
getMonth(idx)
}}</v-col>
script:
computed:{
getMonth(){
return (idx)=>dayjs(`${idx + 1}`, "M").format("MMM").ucfirst()
}
}

Related

computed property is not updating value in v-text-field after execution

I am doing this excercise in vue.js 2.6.
I have a toggled button that has two values: '1' or '2', and I made a computed that depending on these previous values return other values.
this returns either '1' or '2'
<v-col cols="12" sm="12" md="4" lg="4" xl="4">
<label>Toggle button</label><br />
<v-btn-toggle v-model="backendprop.prop1" color="blue" class="form-control p-0" dense borderless>
<v-btn v-for="option in BackendProp1" :key="option.value" :value="option.value">{{ option.label }}</v-btn>
</v-btn-toggle>
</v-col>
I want the value of this input to update according to computed setValueBecauseToggledButton
<v-col cols="12" sm="12" md="4" lg="4" xl="4">
<label>Value depending on Toggled Button</label><br />
<v-text-field
v-model="backendprop.prop2"
type="text"
disabled
outlined
dense
:value="setValueBecauseToggledButton"
/>
</v-col>
and this is the computed value:
computed:{
setValueBecauseToggledButton(){
return this.backendprop?.prop1?.toString() === '2' ? 'Valid prop' : ''
}
The behavior I expect is when I choose between the options of one input the other input should be updated.
Placing console.log in setValueBecauseToggledButton shows me that is working perfectly, but it does nothing on the v-text-field.
You could set a watcher on your property and then update the value for the v-text-field inside it.
<v-col cols="12" sm="12" md="4" lg="4" xl="4">
<label>Value depending on Toggled Button</label><br />
<v-text-field
v-model="backendprop.prop2"
type="text"
disabled
outlined
dense
/>
</v-col>
watch: {
'backendprop.prop1'(value){
this.backendprop.prop2 = value.toString() === '2' ? 'Valid prop' : ''
}
}

Vue show next 10 items when press button

I'm trying to do load more function. When load more button is clicked show next 10 hidden items in v-for allCategories loop.
<template v-for="(category, i) in allCategories">
<v-col :key="i" v-show="i <= 10" class="col-6 col-sm-4">
<v-checkbox
dense
color="black"
:label="category.title"
:value="isCategoryChecked(category.id)"
#click="() => selectCategory(category.id)"
/>
</v-col>
</template>
load more button
<v-btn color="black white--text"
#click="showMoreCategories"
:loading="loading">
{{ $t('general.loadMore') }}
</v-btn>
showMoreCategories function
showMoreCategories() {
}
How do i implement this?
Assuming your variable allCategories contains all the categories you could possibly want (i.e. you don't have to fetch additional categories from a server or local store) then you can replace v-show="i <= 10" with v-show="i <= numberOfCategoriesToShow". Where numberOfCategoriesToShow is a new variable you have defined and initially set to 10.
Then your showMoreCategories function would look like this
showMoreCategories() {
this.numberOfCategoriesToShow += 10;
}

How to prepopulate date range value?

I have an issue on my edit page while trying to prepopulate date range value.
Ex. 2022-06-03, 2022-06-04
Rather than see this
I got an error in the console.
I don't know if I understood the question correctly, but in the absence of a code snippet I refer to the official Vuetify documentation.
Basically you assign a date pattern to the Vuetify component and optionally, with a computed, calculate the divisor.
export default {
data: () => ({
dates: ['2019-09-10', '2019-09-20'],
}),
computed: {
dateRangeText () {
return this.dates.join(' ~ ')
},
},
}
<v-row>
<v-col
cols="12"
sm="6"
>
<v-date-picker
v-model="dates"
range
></v-date-picker>
</v-col>
<v-col
cols="12"
sm="6"
>
<v-text-field
v-model="dateRangeText"
label="Date range"
prepend-icon="mdi-calendar"
readonly
></v-text-field>
model: {{ dates }}
</v-col>
</v-row>

Vuetify adaptative grid layout from item list

I have what seems to be a very common issue, but I can get over it.
I have an API query that returns me a list of object.
I want to display it my page as a grid of card filled with the content.
I want my grid to be in 5 column, and the number of row will adapt to the number of element in my list.
I can figure out how to achieve it.
For example: i have a query returning
items[
{"id":1,"title":"title1,"description":"description1"}
{"id":2,"title":"title2,"description":"description2"}
{"id":3,"title":"title3,"description":"description3"}
{"id":4,"title":"title4,"description":"description4"}
{"id":5,"title":"title5,"description":"description5"}
{"id":6,"title":"title6,"description":"description6"}
{"id":7,"title":"title7,"description":"description7"}
]
My grid as to be made from 7 elements with systematically 5 columns and adaptive rows like:
And if my query returns for example 14 elements, the layout should adapt to look like this:
The closest I was able to get with the code was.
<v-container class="mt-2">
<h1>Top Rated views</h1>
<v-row v-for="n in gridDivider" :key="n" class="n === 1 ? 'mb-6' : ''">
<v-col v-for="(item,index) in Items" :key="index">
<v-card class="mx-auto" max-width="300">
<v-img
class="white--text align-end"
height="200px"
src="item.avatar"
>
<v-card-title>anything as text</v-card-title>
</v-img>
<v-card-subtitle class="pb-0"> Number 10 </v-card-subtitle>
<v-card-text class="text--primary">
<div>Whitehaven Beach</div>
<div>Whitsunday Island, Whitsunday Islands</div>
</v-card-text>
</v-card>
</v-col>
</v-row>
</v-container>
Thanks for helping
It could be done if you change v-row and v-col declarations this way:
<v-row class="five-cols">
<v-col v-for="(item,index) in Items" :key="index">
...
and create a CSS class five-cols using CSS Grid Layouts:
.five-cols {
display: grid;
grid-template-columns: repeat(5, 1fr);
}
Example at CodePen

How do I lazy load item lists on Vuejs and Vuetify's lazyload?

Im trying to make an infinite scroll list but it's really not lazy loading, been stuck with this for hours, the whole list will come in.
.....
<v-col
v-for="(post, i) in posts"
:key="i"
cols="12"
>
<v-lazy
v-model="isActive"
:options="{
threshold: .5
}"
transition="fade-transition"
>
{{Content here}}
</....
API used for test : https://jsonplaceholder.typicode.com/posts
There is a new virtual-scroller component, but it doesn't work with responsive content like grid rows/cols. Instead use v-lazy...
I discovered that the columns need to have defined min-height (approx. to the expected height of the cards) in order for the v-lazy intersection observer to work. Use something like a v-sheet or v-responsive to set the min-height and contain the cards.
Also bind the v-model of the v-lazy to each post (ie: post.isActive), instead of a global isActive var...
<v-col lg="3" md="4" sm="6" cols="12" v-for="(post, index) in posts">
<v-sheet min-height="250" class="fill-height" color="transparent">
<v-lazy
v-model="post.isActive" :options="{
threshold: .5
}"
class="fill-height">
<v-card class="fill-height" hover>
<v-card-text>
<v-row :key="index" #click="">
<v-col sm="10" cols="12" class="text-sm-left text-center">
#{{ (index+1) }}
<h2 v-html="post.title"></h2>
<div v-html="post.body"></div>
</v-col>
</v-row>
</v-card-text>
</v-card>
</v-lazy>
</v-sheet>
</v-col>
Demo: https://codeply.com/p/eOZKk873AJ
I can suggest another solution with v-intersect, which works perfect for me.
Sorry, the snippet may be not working as composed of my code, but the idea should be pretty clear
<template>
<v-list class="overflow-y-auto" max-height="500">
<v-list-item v-for="item in items">
{{ item.name }}
</v-list-item>
<v-skeleton-loader v-if="moreDataToAvailable" v-intersect="loadNextPage" type="list-item#5" />
</v-list>
</template>
<script lang="ts">
import Vue from 'vue'
const pageSize = 10
export default Vue.extend({
data(): any {
return {
pageLoaded: 0,
totalCount: 100,//fetch from API
items: []
}
},
computed: {
moreDataToAvailable (): boolean {
return Math.ceil(this.totalCount / pageSize) - 1 > this.pageLoaded
}
},
methods {
async loadNextPage (entries: IntersectionObserverEntry[]) {
if (entries[0].isIntersecting && this.moreDataToAvailable) {
const nextPage = this.pageLoaded + 1
const loaded = await this.loadPage(nextPage) //API call
loaded.data.forEach((item: any) => this.items.push(item))
this.totalCount = loaded.totalCount
this.pageLoaded = nextPage
}
},
}
})
</script>