How to use v-item-group when using vue.components - Vuetify - vue.js

I'm trying to create a group of selectable items using Vuetify.
Nevertheless, it is not working because inside the template I'm using a Vue.component called fm-card.
When you use a Vue.component you have to use #click.native instead of #click.
<v-item-group active-class="primary">
<v-container>
<v-row justify="center">
<v-col
class="fm-card-container"
cols="2"
v-for="item in items"
v-bind:key="item.id"
>
<v-item v-slot="{ active, toggle }">
<fm-card
#click.native="toggle;"
class="fm-card d-flex align-center"
:title="item.name"
:image="productImage(item.image)"
:imageHeight="95"
dark
height="200"
:color="active ? 'primary' : ''"
>
{{`hola soy el ${toggle}`}}
<v-scroll-y-transition>
<div v-if="active" class="text-h2 flex-grow-1 text-center">
Active
</div>
</v-scroll-y-transition>
</fm-card>
</v-item>
</v-col>
</v-row>
</v-container>
</v-item-group>
I have tried to use #click and #click.native but it seems that nothing is happening

First of all why don't you put your <fm-card> as an "real" component and reference it in your parent and pass all of your values you are using in there with it?
Like this
<fm-card :value1="value1" :value2="value2"/>
than you can put your complete code in this component - it's making your code much more clearer.
And than I think you don't need to use #click.native any more... #click should be enough, but than check that you have a semicolon after your toggle; - you have to delete this!
Than you can go to your methods and work with your click event like this:
methods: {
toggle() {
//your code in here
}
}
Hopefully this helps you out! Pls let me know!

Related

How to make this.$refs.form method work by creating the component before?

I've been running into this problem for a while and it's time to try to solve it. At the moment, let's say I have 2 different components, where each have a form. Then, in my Home.vue, I'm creating a tab system with 2 tabs, allowing me to have each form on a tab, and creating the components, giving a ref to each. Inside each form component, there's different methods to handle data, things like reset() and validate() and to call them on my Home.vue, I'd simply do this.$refs.form1.validate().
Everything is working fine for the first form but whenever it comes down to call methods inside the second form without having pressed the second tab, a lot of undefined start to come up. From what I've seen, the second component isn't created yet, so I'm basically calling a method of something that doesn't exist. I've seen a lot of talking about the mounted() method and I believe this is the solution to the problem, but I have no idea how to implent it.
What I basically need is to have each form created once the page loads so I can call whatever I need, even if I'm still at the first form. Some parts of my code below:
Home.vue
<v-card class="mt-2" elevation="10" >
<v-tabs v-model="tab" show-arrows>
<v-tab v-for="item in items" :key="item.tab">
{{ item.tab }}
</v-tab>
</v-tabs>
<v-tabs-items v-model="tab">
<v-tab-item>
<Form1 ref="form1"/>
</v-tab-item>
<v-tab-item>
<Form2 ref="form2"/>
</v-tab-item>
</v-tabs-items>
</v-card>
<script>
import Form1 from '#/components/Form1';
import Form2 from '#/components/Form2';
export default {
components: {
Form1,
Form2
}
submit(){
if(this.$refs.form1.validate() && this.$refs.form2.validate()){
console.log('Hello')
}
}
</script>
Form1.vue
<v-form v-model="valid" ref="form" >
<v-text-field
v-model="resposta.answer"
:rules="rules.required"
label="Text"
></v-text-field>
</v-form>
methods: {
validate() {
return this.$refs.form.validate()
}
}
Form2.vue can simply be a copy of the first one.
I've tried to keep the code as simple as possible since there's a lot of stuff already going on, so if something is missing that doesn't really relate to the problem in question, I apologize.
you need to add the eager prop to the v-tab-item like so:
<v-tab-item eager>
<Form1 ref="form1"/>
</v-tab-item>
<v-tab-item eager>
<Form2 ref="form2"/>
</v-tab-item>
this means it will render the tab even if it is not visible.
according to vuetify documentation:
When using v-tab-item's that contain required input fields you must use the eager prop in order to validate the required fields that are not yet visible.
Just use the eager prop on your v-tab-item. It will force the component to render even though the tab isn't selected yet.
<v-tabs-items v-model="tab">
<v-tab-item eager>
<Form1 ref="form1"/>
</v-tab-item>
<v-tab-item eager>
<Form2 ref="form2"/>
</v-tab-item>
</v-tabs-items>

Vue/Vuetify #change doesn't provide full $event object

I'm using Vuetify in a project and creating an edit-in-place experience. Switching between the span and text field works, and it correctly sends the update to my server. One last thing though before I can call this feature complete, is that I need to determine whether the original value of the input has changed from the new value so that I'm not posting to my server when I don't need to be.
<span
v-if="editableCategory !== `category${category.id}Ref`"
class="category-header"
#click="setCategoryEditing(`category${category.id}Ref`)">
<h4>{{ category.name }}</h4>
<v-icon small>
edit
</v-icon>
</span>
<v-text-field
v-else
:ref="`category${category.id}Ref`"
:value="category.name"
color="primary"
dense
hide-details
type="text"
outlined
#blur="updateCategory(category, $event)"
#change="updateCategory(category, $event)" />
The problem is that when when I console log the $event, I get two different responses when testing the #change and the #blur. The #blur seems to give me the normal, full event object and I am then able to compare old and new values correctly. However, the #change event simply gives me a string of the new value.
Is this an issue with the Vuetify v-text-field #change event not firing correctly (and I should therefore create a Github issue with them?), or am I totally misunderstanding blur/change events (also another very real possibility)?
Blur event usually returns the event from the element attached. But the change event returns the updated value from the input element
but still you can able to read the updated value from the blur event using the below approach
Working codepen here, check the console for expected output: https://codepen.io/chansv/pen/JjjaZOJ?editors=1010
<div id="app">
<v-app id="inspire">
<v-form>
<v-container>
<v-row>
<v-col cols="12" sm="6" md="3">
<v-text-field
label="Outlined"
placeholder="Placeholder"
outlined
#change="updateCategory($event, category)"
#blur="updateCategory($event.target.value, category)"
></v-text-field>
</v-col>
</v-row>
</v-container>
</v-form>
</v-app>
</div>
new Vue({
el: '#app',
vuetify: new Vuetify(),
data: {
category: 'category text',
},
methods: {
updateCategory(event, category) {
console.log(event, category);
},
}
})

vue-draggable not able to disable

I wrote a component that is shared throughout my application, in some places I need the dragging/sorting and some do not want it there. I pass a prop to my component called disableDraggable and based on that it should disable, unfortunately it does not do anything, how can I disable the draggable?
I should note I tried both the options object syntax and also a simple :disable , here is the relevant code:
<draggable v-model="copyOfQuestions" #end="$emit('updateQuestionsOrder', copyOfQuestions)" :options="{disable : disableDraggable}">
// or :disable="disableDraggable"
<v-card flat class="list_outer_block" v-for="q in questions" :key="q.question_id">
<v-card-text class="pa-0">
<v-layout justify-start align-center>
<v-flex initial-xs px-2 py-3 class="handle minwdth-0" :title="$t('general.drag_for_reorder')">
<v-icon class="text--secondary text--lighten-3">$vuetify.icons.drag_indicator</v-icon>
</v-flex>
....
props: ['questions', 'disableDraggable'],
How can I disable the draggable functionality?
I should note that vue-draggable (what I am using) supposedly has the same api as SortableJs
It should be :disabled and NOT :disable.
<draggable v-model="copyOfQuestions" #end="$emit('updateQuestionsOrder', copyOfQuestions)" :options="{disabled : disableDraggable}">
Reference:
https://github.com/SortableJS/Vue.Draggable/blob/17bdd4b8b2ab4f4df45dd76edf1afec864ec0936/example/debug-components/slot-example.vue

Vuetify Unable to locate target when using attach on v-autocomplete

I want to use autocomplete from Vuetify and I am facing issues there because on my website I have one of the outer divs position: relative the dropdown part of the autocompelete, which is position: absolute, is attaching itself not to the bottom of the input but in random place.
Autocomplete has a prop attach which Specifies which DOM element that this component should detach to. Use either a CSS selector string or an object reference to the element. so I thought I use that and set it to class of my input.
And this works but it causes warning in the console
[Vuetify] Unable to locate target v-autocomplete
found in
---> <VMenu>
<VAutocomplete>
<VCard>
<VApp>
<Root>
Here the link where I reproduced the console warning.
If you are not using v-app component in App.vue, make sure to add data-app attribute to the div with the id app in App.vue.
The result will be like the following:
<template>
<div id="app" data-app>
.... All components, routers, views here ...
</div>
</template>
This worked for me:
<div id="app">
<v-app id="inspire">
<v-card>
<v-card-title class="headline font-weight-regular blue-grey white--text">Profile</v-card-title>
<v-card-text>
<v-subheader class="pa-0">Where do you live?</v-subheader>
<v-autocomplete
v-model="model"
:hint="!isEditing ? 'Click the icon to edit' : 'Click the icon to save'"
:items="states"
:readonly="!isEditing"
:label="`State — ${isEditing ? 'Editable' : 'Readonly'}`"
persistent-hint
prepend-icon="mdi-city"
:attach="'#attach'"
>
<template v-slot:append-outer>
<div id="attach"></div>
<v-slide-x-reverse-transition
mode="out-in"
>
<v-icon
:key="`icon-${isEditing}`"
:color="isEditing ? 'success' : 'info'"
#click="isEditing = !isEditing"
v-text="isEditing ? 'mdi-check-outline' : 'mdi-circle-edit-outline'"
></v-icon>
</v-slide-x-reverse-transition>
</template>
</v-autocomplete>
</v-card-text>
</v-card>
</v-app>
</div>

Meaning of v-slot:activator="{ on }"

Looking at the Vuetify example code for v-toolbar, what is the purpose of v-slot:activator="{ on }"? For example:
<template v-slot:activator="{ on }">
<v-toolbar-title v-on="on">
<span>All</span>
<v-icon dark>arrow_drop_down</v-icon>
</v-toolbar-title>
</template>
<script>
export default {
data: () => ({
items: [
'All', 'Family', 'Friends', 'Coworkers'
]
})
}
</script>
As far as I can see, on is not a defined variable anywhere, so I don't see how this is working. When I try it in my project, Internet Explorer throws an error on the <template v-slot:activator="{ on }">, but if I remove it, the page renders.
You're likely referring to this example:
<v-toolbar color="grey darken-1" dark>
<v-menu :nudge-width="100">
<template v-slot:activator="{ on }">
<v-toolbar-title v-on="on">
<span>All</span>
<v-icon dark>arrow_drop_down</v-icon>
</v-toolbar-title>
</template>
...
</v-menu>
</v-toolbar>
The following line declares a scoped slot named activator, and it is provided a scope object (from VMenu), which contains a property named on:
<template v-slot:activator="{ on }">
This uses destructuring syntax on the scope object, which IE does not support.
For IE, you'd have to dereference on from the scope object itself:
<template v-slot:activator="scope">
<v-toolbar-title v-on="scope.on">
But the ideal solution IMO is to use a Vue CLI generated project, which includes a Babel preset (#vue/babel-preset-app) to automatically include the transforms/polyfills needed for the target browsers. In this case, babel-plugin-transform-es2015-destructuring would be automatically applied during the build.
Details on the activator slot
VMenu allows users to specify a slotted template named activator, containing component(s) that activate/open the menu upon certain events (e.g., click). VMenu provides listeners for those events via an object, passed to the activator slot:
<v-menu>
<template v-slot:activator="scopeDataFromVMenu">
<!-- slot content goes here -->
</template>
</v-menu>
The slot content can access VMenu's event listeners like this:
<v-menu>
<template v-slot:activator="scopeDataFromVMenu">
<button v-on="scopeDataFromVMenu.on">Click</button>
</template>
</v-menu>
For improved readability, the scoped data can also be destructured in the template:
<!-- equivalent to above -->
<v-menu>
<template v-slot:activator="{ on }">
<button v-on="on">Click</button>
</template>
</v-menu>
The listeners from the scope object are passed to the <button> with v-on's object syntax, which binds one or more event/listener pairs to the element. For this value of on:
{
click: activatorClickHandler // activatorClickHandler is an internal VMenu mixin
}
...the button's click handler is bound to a VMenu method.
I think the original question is about understanding the "on" object. It is best explained here:
https://github.com/vuetifyjs/vuetify/issues/6866
Essentially "on" is a prop passed in from the activator. What v-on="on" does is bind that on prop to the component. "on" itself is all of the event listeners passed from the activator.
To call out a readability tip, it's possible to use this syntax:
<v-menu>
<template v-slot:activator="{ on: activationEvents }">
<v-btn v-on="activationEvents">
I like turtles 🐢
</v-btn>
</template>
</v-menu>
In my brain this has a more fluent readability than v-on="on", which to me is like observing a conversation consisting solely of:
Person 1: "Hey"
Person 2: "Yep"
Understand? ;)
By the way, activationEvents could be any alias, like "slotEvents", "listeners", "anyOldEvent", or whatever makes more sense to the reader as a renaming of the mysterious on.
Run the below code,you will know what is 'attrs' an 'on' in v-menu.
<v-menu>
<template v-slot:activator="{ on, attrs }">
<div v-bind="attrs" v-on="on">
v-menu slot activator:
<br />
attrs == {{ JSON.stringify(attrs) }}
<br />
on == {{ '{' + Object.keys(on).map(k => k + " : " + on[k]).join(',') + '}' }}
</div>
</template>
</v-menu>
Result:
v-menu slot activator:
attrs == {"role":"button","aria-haspopup":true,"aria-expanded":"false"}
on == {
click:function (e) {if (_this.openOnClick) {onClick && onClick(e);}_this.absoluteX = e.clientX;_this.absoluteY = e.clientY;},
keydown:function () { [native code] }
}
Explanation:
<div v-bind="attrs" v-on="on"> equals
<div
v-bind="{role:'button',aria-haspopup:true,aria-expanded:'false'}"
v-on="{click:function (e) {/*implement by v-menu*/},keydown:function () {/*implement by v-menu*/}}"
>
Starting in vue 2.4.0+, v-on also supports binding to an object of event/listener pairs without an argument. Note when using the object syntax, it does not support any modifiers.
Example:
<!-- v-on's object syntax (vue 2.4.0+) -->
<button v-on="{ mousedown: doThis, mouseup: doThat }"></button>
About <template> tags in Internet Explorer throws an error :
as vuetify docs say:
Template caveat
Due to Internet Explorer’s limited support for <template> tags, you must send fully compiled dom elements to the browser. This can be done by either building your Vue code in advance or by creating helper components to replace the dom elements. For instance, if sent directly to IE, this will fail:
<!-- Vue Component -->
<template v-slot:items="props">
<td>{‌{ props.item.name }‌}</td>
</template>