How to make vuetify v-stepper header as completed in dynamically? - javascript

Currently, I am working on <v-stepper> on vuetify js, and there are 12 steps that I've created. Every step there are set of tasks of contents available when you will click on each step, and accordingly step the contents will be changed by on click. I wanted the <v-stepper> step will show complete(or tick icon) if the set of tasks has completed on that particular step. Let's say if Step 1 is completed then step 1 icon should show a green tick icon. Now contents are populated on each step and I have the completed steps data but couldn't able to show complete-icon if the step is completed.
Below is my code:
<v-stepper-header>
<template v-for="n in steps">
<v-stepper-step :key="`${n}-step`" complete="completed" :step="n" :editable="editable" >
Week {{ n }}
</v-stepper-step>
<v-divider v-if="n !== steps" :key="n"></v-divider>
</template>
</v-stepper-header>
completed method:
e1(e1){
this.SET_STATUS_TO_DEFAULT(false)
// alert here
console.log(e1)
if(this.completedWeeksList.includes(e1)){
this.completed = true
this.editable = false
}else{
this.completed = false
this.editable = true
}
}
Please let me know if you need more elaboration.

The question isn't clear on wether you're trying to change the complete icon, or just trying to change the color to show the step as completed.
Either way complete is a prop, so you must pass it in as :complete...
<v-stepper-header>
<template v-for="n in steps">
<v-stepper-step :key="`${n}-step`" :complete="completed" :step="n" :editable="editable" >
Week {{ n }}
</v-stepper-step>
<v-divider v-if="n !== steps" :key="n"></v-divider>
</template>
</v-stepper-header>
Demo: https://codeply.com/p/kNjSEzu15W

the problem with the <v-stepper> in vutify is that, the <v-divider> must be a sibling not a child of each individual step, so in this case you need to use the <template> element to loop through the list of items by printing 2 siblings or more for each iteration and the most important thing to note here is all children of <template> which are siblings must have the same :key binding attribute but not the same value when you use it for any v-for directive
<v-stepper v-if="studyReportsList.length > 1">
<v-stepper-header>
<template v-for="(item, key) in studyReportsList">
<!-- all siblings must have the same :key or v-bind:key-->
<v-stepper-step :key="key+1" :step="key+1" editable>
{{ item.name }}
</v-stepper-step>
<v-divider v-if="key !== studyReportsList.length - 1" :key="key"></v-divider>
</template>
</v-stepper-header>
</v-stepper>

Related

Independent boolean variable for each item in v-for

I'm trying to create a custom play/pause button for my audio items which I did like so...
<div v-for="(post, p) in post_list">
<!-- ... -->
<!-- ... -->
<!-- ... -->
<v-avatar v-if="!is_played" color="#663399" size="42" class="mx-2"
#click="playMe('custom-wave-aud-'+p)"> <!-- is_played = true -->
<v-icon dark> mdi-play </v-icon>
</v-avatar>
<v-avatar v-if="is_played" color="#663399" size="42" class="mx-2"
#click="pauseMe('custom-wave-aud-'+p)"> <!-- is_played = false-->
<v-icon dark> mdi-pause </v-icon>
</v-avatar>
</div>
The above code toggles from pause icon to play icon on click. The problem is that, it affects all play/pause button for all items since the is_played is assigned for all the items.
What I want is that is_played should only affect one item and should only be exclusive to the item that I toggle play/pause button.
EDITED: Here's the play/pause function
playMe(c, index) {
document.getElementsByClassName(c)[0].play();
this.is_played = true;
},
pauseMe(c, index) {
document.getElementsByClassName(c)[0].pause();
this.is_played = false;
},
As explained in comments, you have to reference the index of the current post in is_played. Otherwise all the buttons will change when is_played changes. Here's some sample code which should do that:
<div v-for="(post, p) in post_list" :key="p">
<v-avatar v-if="is_played !== p"
color="#663399"
size="42"
class="mx-2"
#click="playMe('custom-wave-aud-' + p, p)">
<v-icon dark>mdi-play</v-icon>
</v-avatar>
<v-avatar v-if="is_played === p"
color="#663399"
size="42"
class="mx-2"
#click="pauseMe('custom-wave-aud-' + p, p)">
<v-icon dark>mdi-pause</v-icon>
</v-avatar>
</div>
data: () => ({
is_played: null
}),
methods: {
playMe(className, index) {
if (this.is_played !== null) {
// pause the currently playing one first
this.pauseMe(className, this.is_played);
}
this.is_played = index;
document.getElementsByClassName(className)[0].play();
},
pauseMe(className, index) {
this.is_played = null;
document.getElementsByClassName(className)[0].pause();
}
}
Note: the above code assumes you don't want more than one of the wavs to play at one time. If you do want to allow playing multiple at the same time, you'll have to change is_playing to an array and change the condition to check if current index is contained in the array. Obviously, you'll want to add/remove the index to the array when playing/pausing.
If you need more help, please provide a runnable minimal reproducible example.

Vuetify Data Table (v-data-table) - how to conditionally style values in certain columns

What is the easiest way to highlight certain values in a column in a Vuetify Data Table (v-data-table component).
E.g. let's say in this first example here: https://vuetifyjs.com/en/components/data-tables
How to automatically style values in the Calories column that are greater than 300 in bold and red?
You can do something like that :
<v-data-table
:headers="headers"
:items="desserts"
class="elevation-1"
>
<template v-slot:item.calories="{ item }">
<div :class="getStyle(item.calories)">{{ item.calories }}</div>
</template>
</v-data-table>
Then, in your script you can add a method "getStyle" that automatically style the values.
methods: {
getStyle (calories) {
if (calories > 300) return 'red--text font-weight-bold'
else return ''
},
},
Here a codepen example : https://codepen.io/guizboule/pen/LYPyWMV?&editable=true&editors=101
In you want another solution, Vuetify made an example with chips : https://codepen.io/guizboule/pen/vYBmxwg?&editable=true&editors=101

Dynamically integrate Vuetify v-stepper with Vue router

I would like to integrate vuetify's v-stepper with vue router. My requirements are as follows:
Each step has its own route (e.g. /myform/step1, /myform/step2, /myform/step3, etc)
Each step is component on its own which is dynamically loaded (lazy-load).
Each step is dynamically created (e.g. via a loop).
This is more of a 'what is the best approach' kind-of-question. I've tried multiple solutions but none fit my requirements.
I've tried creating nested routes and placing a router-view in v-stepper-content. Example below. The issue I faced here was that it's impossible to synchroniously update position (see v-stepper element) and the route. So you'll always see the route updating before the step is updated.
<v-stepper v-model="position" vertical>
<template v-for="(item, index) in steps">
<v-stepper-step :complete="position > index + 1" :step="index + 1">
<h2>
{{item.title}}
</h2>
</v-stepper-step>
<v-stepper-content :step="index+1">
<router-view></router-view>
</v-stepper-content>
</template>
</v-stepper>
Another solution I tried is loading the components async/dynamically directly (so without router). However, then I lose the beautiful ability to navigate through my v-stepper using the browser's back and next buttons.
In my experience, the biggest pitfall is that (contrary to e.g. v-tab), is that every step has to have its own v-stepper-content. If I were to do this with tabs, I would just create one tab-item and update the view. I can't do that with v-stepper, because it wouldn't continue to the next 'step'.
Would anyone have a creative approach?
I was able to achieve this by doing the following:
<v-stepper-step #click="goToRoute('step1')">
with
goToRoute(name) {
this.$router.push({'name': name})
}
You should be able to do this:
<v-stepper-step #click="$router.push({'name': name})">
As an additional answer to #tmfmaynard, in order to align the correct highlighted stepper with your current route after a page refresh, here is the code.
<v-stepper v-model="e1" alt-labels non-linear>
<v-stepper-header class="elevation-0">
<v-stepper-step
step="1"
class="caption"
editable
#click="$router.push({name: 'name'}).catch(err => {})"
>
</v-stepper-step>
</v-stepper-header>
<v-stepper-items>
<v-stepper-content step="1">
<router-view />
</v-stepper-content>
</v-stepper-items>
</v-stepper>
<script>
export default {
data () {
return {
e1: 1
}
},
created() {
this.getStepper()
},
methods: {
getStepper() {
const path = this.$route.path.split('/')
if(path[path.length-1].toLowerCase() === 'your-router-path') {
this.e1 = 1
// this.e1 = 1 = <v-stepper-step step="1" />
// this.e1 = 2 = <v-stepper-step step="2" />
// and so on.
}
}
}
}

vuetify v-select with multiple options - getting selected/deselected option

I'm using Vuetify and its v-select component with multiple option enabled to allow selecting multiple options.
These options represent talent(candidate) pools for my CRM software.
What it needs to do is that when some option in v-select is checked, candidates from checked talent pool are fetched from API and saved to some array (let's call it markedCandidates), and when option is deselected, candidates from that pool need to be removed from markedCandidates array.
The problem is that #change or #input events return complete list of selected options. I need it to return just selected/deselected pool and information if it's selected or deselected, to be able to update the markedCandidates array.
My existing code:
<v-select return-object multiple #change="loadCandidatesFromTalentPool" v-model="markedCandidates" :item-text="'name'" :item-value="'name'" :items="talentPoolsSortedByName" dense placeholder="No pool selected" label="Talent Pools" color='#009FFF'>
<template slot="selection" slot-scope="{ item, index }">
<span v-if="index === 0">{{ item.name }}</span>
<span v-if="index === 1" class="grey--text caption othersSpan">(+{{ talentPools.length - 1 }} others)</span>
</template>
</v-select>
Any idea how to solve this?
As I said, loadCandidatesFromTalentPool(change) returns complete array of v-model (markedCandidates)..
EDIT:
I found this solution, it's more of a workaround actually, would be nice if there was dedicated event for this situation:
https://codepen.io/johnjleider/pen/OByoOq?editors=1011
Actually there is only one event related to changing values of v-autocomplete : #change (See https://vuetifyjs.com/en/components/autocompletes#events). The watch approach is useful if you want to monitor only individual changes. However, if you plan to do this with more selectors, it could be better if you create a custom reusable component with a new attached event (in this example, for the last change).
Vue.component('customselector',{
props:[
"value",
"items"
],
data: function() {
return {
content: this.value,
oldVal : this.value
}
},
methods: {
handleInput (e) {
this.$emit('input', this.content)
},
changed (val) {
oldVal=this.oldVal
//detect differences
const diff = [
...val.filter(x => !oldVal.includes(x)),
...oldVal.filter(x => !val.includes(x))
]
this.oldVal = val
var deleted=[]
var inserted=[]
// detect inserted/deleted
for(var i=0;i<diff.length;i++){
if (val.indexOf(diff[i])){
deleted.push(diff[i])
}else{
inserted.push(diff[i])
}
}
this.$emit("change",val)
this.$emit("lastchange",diff,inserted,deleted);
}
},
extends: 'v-autocomplete',
template: '<v-autocomplete #input="handleInput" #change="changed" :items="items" box chips color="blue lighten-2" label="Select" item-text="name" item-value="name" multiple return-object><slot></slot></v-autocomplete>',
})
Then you can use your component as simple as:
<customselector #lastchange="lastChange" >...</customselector>
methods:{
lastChange: function(changed, inserted, deleted){
this.lastChanged = changed
}
}
The changed only shows items which are actually changed. I've added the inserted and deleted arrays to return new items added or removed from the selection.
Source example: https://codepen.io/fraigo/pen/qQRvve/?editors=1011
Replace
:item-text="'name'" :item-value="'name'"
by
item-text="name" item-value="name"

How can I add a element everytime after 4 elements in Vue.js?

I'm immigrating my code to Vue.js so pretty new to Vue. As you see the screenshot(link below), There are 4 columns inside div with columns class name. I was trying to use v-if to add the div(class columns) conditionally but I have no idea how to get a length of columns elements in Vue.
Example Code
<template lang="pug">
.container
.columns(v-if="") // add this conditionally
.column.is-3.vid(v-for='item in items')
.panel
p.is-marginless
a(href=item.videoId)
img(src=item.thumbnail)
.panel.vidInfo
.columns.hax-text-centered
.column
.panel-item.reddit-ups
| item.score
i.fa.fa-reddit-alien.fa-2x
.panel-item.reddit-date
i.fa.fa-calendar.fa-2x
</template>
You can access v-for iteration number like this:
change
.column.is-3.vid(v-for='item in items')
to
.column.is-3.vid(v-for='(item, index) in items')
then somewhere where you need it check the index:
v-if="(index % 4 === 0) ? 'show it after every 4 elements' : ''"
for example, inject a span:
<span v-if="index % 4 === 0">after 4</span>

Categories