How to get category value in slot (Vuetify calendar) - javascript

I am having trouble figuring out how to have hover functionality in categories view of Vuetify calendar in the body part (slot day-body). Now when I implement the hover functionality, the whole row has the hover, but I am hovering over one category only. Everything else such as the week, day and 4-day is working properly because I can individually get date, week and timeToY from the day-body slot.
<template v-slot:day-body="{ date, timeToY }">
<div :class="{ hover: isHover && date === hoverDate }"
:style="{ top: `${timeToY(hoverY)}px` }">
<p class="mt-2 ml-2">{{ hoverY }}</p>
</div>
</template>
This is the HTML that I am using and these two events #mousemove:time="hoverToSeeTime" and #mouseleave:time="removeHover". In the mousemove handler I assign isHover, hoverDate and hoverY. But I can't grasp how to make individual category tiles hoverable because day-body slot does not have the category key. Am I missing anything?
Thanks in advance.

Use #mousemove:time-category and #mouseleave:time-category instead and category will be available in the event handler. Then you can set a hoverCategory along with the other hover properties:
hoverToSeeTime({ date, time, category }) {
...
this.hoverCategory = category;
...
}
category is available in the slot props:
<template v-slot:day-body="{ date, week, timeToY, category }">
So you can compare the two in the slot template. Adjust that template to use a v-if on the div instead of a conditional class:
<template v-slot:day-body="{ date, week, timeToY, category }">
<div v-if="category == hoverCategory && date === hoverDate"
class="hover" :style="{ top: `${timeToY(hoverY)}px` }"
>
<p class="mt-2 ml-2">{{ hoverY }}</p>
</div>
</template>
Here is an updated codepen

Related

How to change the border variant of a selected Bootstrap Vue card when clicked in v-for

I have my b-card and v-for showing and functioning mostly as desired, the one issue I'm running into is changing the border-variant of a card that is selected. So far I have tried nesting the v-for that was inside the card in a div container and then using a v-if to check and see if a card has been selected. This code changes the border variant in the way I would like, but I only want the selected card to change its border variant. The code working as described above is shown below.
<div v-for="(team, index) in teams" :key="index">
<b-card
v-if="selected"
img-src="https://picsum.photos/600/300/?image=25"
img-alt=""
v-model="selected"
border-variant="success"
img-bottom
no-body
class="mb-4 mr-4"
body-class="text-center"
>
<b-row class=" my-2" align-h="center">
<b-link #click="functionThatChangesColorAndDoesOtherStuff(stuff)"><b-icon icon="plus-circle" font-scale="2.5"></b-icon></b-link>
</b-row>
</b-card>
<b-card
v-else
img-src="https://picsum.photos/600/300/?image=25"
img-alt=""
v-model="selected"
img-bottom
no-body
class="mb-4 mr-4"
body-class="text-center"
>
<b-row class=" my-2" align-h="center">
<b-link #click="functionThatChangesColorAndDoesOtherStuff(stuff)"><b-icon icon="plus-circle" font-scale="2.5"></b-icon></b-link>
</b-row>
</b-card>
</div>
The selected property is on my data() and I have a function that we'll call functionThatChangesColorAndDoesOtherStuff() where that selected property gets toggled. The method's code is as shown below
data() {
return {
teams: [],
selected: false
}
}
functionThatChangesColorAndDoesOtherStuff(stuff) {
this.selected = !this.selected
\\\Business logic below that does business stuff with the stuff
}
I've also tried utilizing the index in the v-for, but that didn't even get me to the point where the color was changing at all. This makes me think I'm not using it properly and I would like some help with how I can accomplish this effect. I've checked around SO and haven't seen an example similar to my situation which I will reiterate again. I would like to click the plus button and have the selected card's border variant change.
Please take a look at following snippet:
you can bind border like: :border-variant="selected === index ? 'success' : 'danger'" , and pass index to function.
new Vue({
el: '#demo',
data() {
return {
teams: [1,2,3],
selected: []
}
},
methods: {
functionThatChangesColorAndDoesOtherStuff(idx) {
if (this.selected.includes(idx)) {
this.selected = this.selected.filter(s => s !== idx)
} else this.selected.push(idx)
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<link type="text/css" rel="stylesheet" href="//unpkg.com/bootstrap/dist/css/bootstrap.min.css" />
<link type="text/css" rel="stylesheet" href="//unpkg.com/bootstrap-vue#latest/dist/bootstrap-vue.min.css" />
<script src="//unpkg.com/bootstrap-vue#latest/dist/bootstrap-vue.min.js"></script>
<script src="//unpkg.com/bootstrap-vue#latest/dist/bootstrap-vue-icons.min.js"></script>
<div id="demo">
<div v-for="(team, index) in teams" :key="index">
<b-card
img-src="https://picsum.photos/600/300/?image=25"
img-alt=""
v-model="selected"
:border-variant="selected.includes(index) ? 'success' : 'danger'"
img-bottom
no-body
class="mb-4 mr-4"
body-class="text-center"
>
<b-row class=" my-2" align-h="center">
<b-link #click="functionThatChangesColorAndDoesOtherStuff(index)"><b-icon icon="plus-circle" font-scale="2.5"></b-icon></b-link>
</b-row>
</b-card>
</div>
</div>
Firstly instead of using v-if and v-else just for a class name use the following
:class=" selected ? 'className1' : 'className2' "
The first value has to be a truthy value. The following are your class names that will be applied based on the value.

Target element when inside v-for loop Vuejs

I am trying to build a To-Do list in Vue.js which has 3 columns: To-Do, Doing, Done.
I would like to be able to move an item between columns by clicking on arrows that are inside the list item.
Right now I have a list of objects that I separate in 3 arrays depending on a "status" attribute. I would like to change that attribute when clicking on left/right arrow then refresh the UI with new arrays.
I haven't found the way to target the element that received the click.
<ul>
<li v-for="todo in todoTodos" v-bind:key="todo._id">
<span v-if="todo.importance == 1" class="bg-success"></span>
<span v-else-if="todo.importance == 2" class="bg-warning"></span>
<span v-else-if="todo.importance == 3" class="bg-alert"></span>
<div>
<h3>{{ todo.title }}</h3>
<p>{{ todo.description }}</p>
</div>
<p class="todo__date">Début: {{ todo.datebegin }} - Fin espérée: {{ todo.dateend }}</p>
<div class="todo__actions">
<i #click="editTodo" class="icofont-edit"></i>
<i #click="moveRight" class="icofont-arrow-right"></i>
<i #click="moveLeft" class="icofont-arrow-left"></i>
</div>
</li>
</ul>
My linter prevents me from using v-for + v-if, but I guess that means I will have to re-calculate each list (todoTodos, doingTodos, doneTodos) after each modification. Is there a better way ?
I tried console.logging this e.target e.currentTarget but
this logs the entire data model
e.target and e.currentTarget logs the element which I can't use to find my way back to the todo item I want to modify
You /usually/ pass the ($event) argument to a method if you want to access an event.
<i #click="moveLeft($event)" class="icofont-arrow-left"></i>
......
methods: {
moveLeft(e){
console.log(e.target)
}
}

Active class not reactive on the first load using vue.js

So I am building a tab component and using :class to bind the active class like this.
<nav class="tabs__header" >
<ul>
<li>
<a
ref="tabItems"
v-for="(tabItem, idx) in tabs"
:key="tabItem.idx"
:class="{ 'b-active': idx === selectedIndex }"
#click="changeTab(idx)">
<span v-if="icon"> {{ tabIcon }} </span>
{{ tabItem.name }}
</a>
</li>
</ul>
</nav>
The selectedIndex is set with tabIndex value when mounted() for the first time and updated based on the 'idx' selected afterward. I have no problem with the b-active class if we click the tab manually, but it not bind the tabIndex value assigned in mounted().
This is the script I use to directly add value '0' for testing and still not get anything.
props: {
tabIndex: {
type: String,
default: '0'
},
mounted() { this.selectedIndex = this.tabIndex }
can anyone help me? because it seems very simple and I couldn't figure it out for 2 days
The problem solved and it turns out to be the data type problem, apparently tabIndex is a String and selectedIndex is a Number.
I changed:
:class="{ 'b-active': idx === selectedIndex }"
to:
:class="{ 'b-active': idx == selectedIndex }"
and it worked!
At initial render idx seems to be undefined.
Replace this:
:key="tabItem.idx"
With this:
:key="idx"
This now should work fine.
Alternatively, you may use v-for like:
v-for="tabItem in tabs"
Now, using tabItem.idx works just fine as you do in others like tabItem.name.
So, here's your code updated:
<nav class="tabs__header" >
<ul>
<li>
<a
ref="tabItems"
v-for="tabItem in tabs"
:key="tabItem.idx"
:class="{ 'b-active': tabItem.idx == selectedIndex }"
#click="changeTab(idx)">
<span v-if="icon"> {{ tabItem.icon }} </span>
{{ tabItem.name }}
</a>
</li>
</ul>
</nav>

Dynamic component insertion using VueJs, Cordava and Javascript

Using vuejs and Cordova, I could not dynamicaly insert/create a new v-select component.
My goal is to duplicate and insert a copy an existing v-select when my "Add" button gets clicked.
The code of my page is:
<v-select
:items="exerciceList"
v-model="selectedExercice"
label="pick an exercise"
v-validate="'required'"
data-vv-name="select"
required>
</v-select>
</div>
<div id="selectsContainer"></div>
<v-btn flat icon id="btn" v-on:click="newExercice()">
My (not working) click event listener:
methods: {
newExercice: function () {
var container = document.getElementById('selectsContainer');
var select = document.getElementById('exo');
// select.items = this.exerciceList;
select.removeAttribute('id');
select.style.display = 'block';
container.appendChild(select);
}
},
The item with the id expo's code:
<v-select
id="exo"
style="display: none;"
:items="exerciceList"
v-model="selectedExercice"
label="Choisissez un exercice"
v-validate="'required'"
data-vv-name="select"
required>
Thank you for helping me improve my implementation.
What Michael S has said is correct, you do not want to manipulate the DOM directly, causes many issues. Probably the best solution (quick and easy anyway) would be to import that additional v-select component and then add it dynamically with a simple list or in this case an integer increase.
If you're wanting to get a little more customized with it, you can create an object and add and remove styles, attr, etc. from it dynamically the same way this option would work but turning item into an array of objects. then proceed to add an object to it whenever the button is clicked.
<v-select
:items="exerciceList"
v-model="selectedExercice"
label="pick an exercise"
v-validate="'required'"
data-vv-name="select"
required>
</v-select>
</div>
<div id="selectsContainer"
v-for="(item, idx) in items">
<v-select
id="item.id" <!-- or use idx -->
style="item.style" <!-- or use any string statement -->
...etc
>
</div>
<v-btn flat icon id="btn" v-on:click="newExercice()">
...{
components: [componentName],
data: return {
items: 0,
...
}
methods: {
newExercice: ()=>{
this.items++
}
}
...}

How to disable NgbTooltip if tooltip template is empty, in Angular2?

I'm showing an array of data inside a tooltip, so I used a template. My code looks like:
<ng-template #ToolTipTemplate>
<small *ngFor="let month of data.months; let first = first; let last = last"> {{ month.appliedMonthYear | utc | date:'MM/y' }}{{ last ? '' : ', ' }} </small>
</ng-template>
<span [ngbTooltip]="ToolTipTemplate">Months: {{data.months.length}}</span>
If data.months is empty I do not want the tooltip to appear. Currently if it's empty it shows the tooltip arrow only.
I've tried adding an *ngIf on the <small> tag inside the template, but that didn't work. I've also tried adding *ngIf into <ng-template> to no avail.
Ok, I was finally able to figure it out. Here's what I had to do
<span [ngbTooltip]="(data.months?.length) ? ToolTipTemplate : ''">Months: {{data.months.length}}</span>
You can take the element reference like #t="ngbTooltip" and then manually fire the tooltip. In your case fire it if required or don't show the tooltip at all.
<div
[ngbTooltip]="(data.months?.length) ? ToolTipTemplate : ''"
triggers="manual"
#t="ngbTooltip"
(mouseenter)="(data.months?.length) && t.open()"
(mouseleave)="t.close()">
show Tooltip
</div>
You can disable it with
<span [ngbTooltip]="ToolTipTemplate" [disableTooltip]="true">Months: {{data.months.length}}</span>
See docs for more info: https://ng-bootstrap.github.io/#/components/tooltip/api
Simple way of doing is
<div *ngIf='data.months.length > 0'>
<ng-template #ToolTipTemplate>
<small *ngFor="let month of data.months; let first = first; let last = last"> {{ month.appliedMonthYear | utc | date:'MM/y' }}{{ last ? '' : ', ' }} </small>
</ng-template>
<span [ngbTooltip]="ToolTipTemplate">Show Info</span>
</div>
<div *ngIf='data.months.length === 0'>
<span>Show Info</span>
</div>
You can use disableTooltip input attribute.
https://ng-bootstrap.github.io/#/components/tooltip/api

Categories

Resources