Using Bootstrap-Vue (vue2).
When clicking on a div, I need to show a tooltip only if isDisabled=true
<div id="disabled-wrapper" class="includeExclude" :class="{ disabled: isDisabled }"
#click="excludeCountry" tabindex="0">
<b-tooltip v-if="isDisabled" variant="secondary" target="disabled-wrapper" triggers="click">
</b-tooltip>
this is the method that fires on clicking the div
excludeCountry(){
if (this.temporaryFilters['countries'] != undefined){
this.isDisabled = true;
}
else {
this.operator = 'notin';
this.exclude = !this.exclude;
}
}
I cannot get the v-if condition to work, If I remove it, the tooltip works fine clicking on the element
the part
if (this.temporaryFilters['countries'] != undefined){
this.isDisabled = true;
}
works because I have some logs on this.isDisabled and it changes to true
this.temporaryFilters['countries'] !== undefined
You can update the condition like this and try.
If condition works and this.isDisabled is true, you can see tooltip after second click. v-if works correct.
If you want to see tooltip after first click you must add Event Modifier .capture. Example: #click.capture="excludeCountry"
Docs,
Demo
Related
Am I accessing the DOM correctly in Vue.js? My task is such that when I click on the title, a form appears and I can edit the record in it. To cancel, press ESC, and to save, press ENTER.
my template:
<div v-for="(note, i) in notes" :key="i">
<div class="card-body">
<h5 class="card-title" #click="editNote($event, i)">{{note.title}}</h5>
<input type="hidden">
</div>
</div>
my script:
editNote (event, i){
let parent = event.target,
block = parent.closest('.card-body'),
input = block.querySelector('input'),
text = parent.textContent;
input.type = "text"
input.value = text
parent.style.display = "none"
input.onkeydown = (e) =>{
if (e.keyCode == 27){
input.type = "hidden"
parent.style.display = "block"
}
else if (e.keyCode == 13){
input.type = "hidden"
parent.style.display = "block"
parent.textContent = input.value
this.notes[i].title = input.value
}
}
}
Your solution is a solution, but you can do much better by using v-if, v-model and #keypress.
With v-if you can check whether a specific variable is true or not. With that you can manipulate your h5 and input by setting the variable in editNote to true. This is how you can get rid of the display CSS manipulation.
With #keypress in the input tag, you can get rid of the keydown event and enter your solution there.
With the v-model you can easily get the value whenever someone enters something in the input. Also, you can get directly the value of the input.
Checkout these links for more:
v-model
v-if
event handling
I've got a bit of an add situation with a custom "Edit In Place" Vuetify text input. I noticed that on keyup of the enter key, there were two requests being made to my backend to update the field. I know what's causing the issue, just not sure how to fix it!
I have this text field:
<v-text-field
:id="uniqueIdentifier"
:ref="uniqueIdentifier"
v-focus
v-mask="mask"
:class="[ 'click-to-edit', active ? 'active' : 'b-none', customClass ]"
color="primary"
dense
:full-width="false"
hide-details
outlined
:style="{...customStyle}"
:type="type"
:value="localValue"
#blur="handleUpdateItem($event)"
#click="activateInput"
#keyup.enter.native="handleUpdateItem($event)" />
As you can see, I have both #blur and #keyup.enter.native events being fired to update the text field. The method to update the field looks like this:
handleUpdateItem (e) {
this.localValue = e.target.value;
this.active = false;
this.$emit('handle-update-item', this.localValue);
// if (e.type === 'keyup') { this.$refs[this.uniqueIdentifier].blur(); }
// if (e.type === 'keyup') { this.active = false; }
}
The two last lines of that method that are commented out are my attempts, and are intended to "deactivate" the input on click of enter, however because the first line is then running .blur(), the text field runs this function a second time. The second line still has the blinking cursor in the input, so it's not really "deactivated" either.
Ultimately what I'm looking for is a way to allow the user to either press enter or click outside of the input to fire off the request (obviously without it firing twice) and deactivate the input. Does anyone have a suggestion?
If I understand correctly, you want to emit the local value when either the input is tabbed/clicked out of OR if the enter key is pressed but not double up on emits.
If the enter key is pressed, we need to programmically unfocus which will trigger the blur listener.
If the input is unfocused (with a outside click or tab key or with the aforementioned unfocus) then we need to emit the value.
<v-text-field
...
#keyup.native.enter="$refs[uniqueIdentifier].blur()"
#blur="handleUpdateItem($event)"
/>
handleUpdateItem (e) {
this.localValue = e.target.value;
this.active = false;
this.$emit('handle-update-item', this.localValue);
}
In below code, the component could be either button, input or RouterLink, and we don't know at advance which one will be. I added a lot of attributes to prevent v-if solution, because in this case we need to duplicate most of attributes:
<component
:is="rootElementTagOrComponentName"
#click.prevent="$emit('click', $event.target)"
:to="route"
:type="inputOrButtonElementTypeAttributeValue"
:class="CSS_Classes"
:value="rootElementTagNameIsInput && lettering"
:role="rootElementIsLink && 'link'"
:disabled="(rootElementTagNameIsButton || rootElementTagNameIsInput) && disabled"
:ref="ROOT_ELEMENT_REFERENCE"
>
<template v-if="!rootElementTagNameIsInput && lettering">{{ lettering }}</template>
<slot v-if="!rootElementTagNameIsInput"></slot>
</component>
The #click.prevent="$emit('click', $event.target)" not work for RoterLink. However, with #click.native.prevent="$emit('click', $event.target)" we get
[Vue warn]: The .native modifier for v-on is only valid on components but it was used on <button>.
How to resolve this conflict?
It gets tricky when you have a dynamic component which may or may not be a native element. Unfortunately there is no easy way to do this in the template alone, you'll have to do it in code (v-on object syntax does not support native events).
Untested, but something like this may work:
<Root
:to="route"
:type="inputOrButtonElementTypeAttributeValue"
:class="CSS_Classes"
:value="rootElementTagNameIsInput && lettering"
:role="rootElementIsLink && 'link'"
:disabled="(rootElementTagNameIsButton || rootElementTagNameIsInput) && disabled"
:ref="ROOT_ELEMENT_REFERENCE"
>
<template v-if="!rootElementTagNameIsInput && lettering">{{ lettering }}</template>
<slot v-if="!rootElementTagNameIsInput"></slot>
</Root>
components: {
Root: {
functional: true,
render(h, ctx) {
const { data, parent, children } = ctx
const tag = parent.rootElementTagOrComponentName
const click = e => {
e.preventDefault()
parent.$emit('click', e.target)
}
if (tag === 'RouterLink') {
data.nativeOn = data.nativeOn || {}
data.nativeOn.click = click
} else {
data.on = data.on || {}
data.on.click = click
}
return h(tag, data, children)
}
}
}
All I've done in code is deal with registering the click event, but you could move other things into code as well if you want.
I want to keep the value of a variable identical with the content of a textarea.
I don't want to use v-bind or v-model, because I have already bound the textarea with another value.
This is a notebook app, and the textarea is used to display the content of a note, so it has been bound using v-bind with a note object, like
<textarea cols="30" rows="3" v-bind:value="note"></textarea>
Now, I want to add the "edit note" functionality. So when the content of the textarea changes, I want to store its value into a variable, and when the "submit" button is clicked, I pass the value of the variable, which contains the new content of the note, to backend to update the note.
My question is, how to store the textarea's content into the variable after each time the content changes?
I think I cannot use v-model because this way the note will be changed right after the content of the textarea is modified (though not sent to backend), but this is not what I want. What I want is the note to be changed only after the "submit" button is clicked. Thus, I cannot use v-model
Should I use v-on:change? If so, how to get the content of the textarea?
Like,
<textarea v-on:change="updateTheVariable(I need to get the content of the textarea here)"> ... </textarea>
methods: {
updateTheVariable(content of the textarea) {
this.variable = content of the textarea
}
}
Thanks
I'm assuming this thing only shows up when you click some kind of edit button which is why you don't want to alter note so try something like this instead
<button type="button" v-if="!editMode" #click="editNote">Edit</button>
<form v-if="editMode" #submit="handleSubmit">
<fieldset :disabled="saving">
<textarea v-model="editingNote"></textarea>
<button type="submit">Edit</button>
</fieldset>
</form>
export default {
data: () => ({
note: 'whatever', // maybe it's a prop, maybe assigned later, doesn't matter
editMode: false,
editingNote: null, // this will be used to bind the edited value
saving: false
}),
methods: {
editNote () {
this.editingNote = this.note
this.editMode = true
this.saving = false
},
async handleSubmit () {
this.saving = true // disables form inputs and buttons
await axios.post('/notes/update', { note: this.editingNote}) // just an example
this.note = this.editingNote // or maybe use data from the response ¯\_(ツ)_/¯
// or if it's a prop, this.$emit('updated', this.editingNote)
this.editMode = false
}
}
}
As #Phil indicated in a deleted post, the right way to do it is
<textarea #input="updateTheVariable($event.target.value)"></textarea>
.
.
.
methods:{
updateTheVariable(value){
this.variable = value
}
}
I am rendering two texts based on a condition and be able to pass methods to the click event based on the condition. The default text is ADD TO COLLECTION because initially hasPaid property is false. Once payment has been made, I want to set that property to true
The function addToCollection first opens a modal, on the modal, the handlePayment function is implemented. I have been able to conditionally render the div to show either ADD TO COLLECTION or DOWNLOAD using v-on="". I also return hasPaid property from the handlePayment function.
<div class="float-right peexo-faded-text card-inner-text" :face="face" v-on="!hasPaid ? {click: addToCollection} : {click: handleDownload(face)}">
{{!hasPaid ? 'ADD TO COLLECTION': 'DOWNLOAD' }}
</div>
data: function () {
return {
hasPaid: false,
}
},
addToCollection(){
this.showcollectionModal = true;
},
handlePayment(){
this.showcollectionModal = false;
let accept = true;
this.showpaymentsuccessmodal = true;
//this.hasPaid = true;
return {
hasPaid: accept
}
},
I want to be able to set hasPaid property on the handlePayment function for the render function to pick it, so that the handleDownload function can then work.
The last section of this bit is going to be problematic:
v-on="!hasPaid ? {click: addToCollection} : {click: handleDownload(face)}"
When hasPaid is true it will invoke the method handleDownload immediately. That is, it will be called during render, not when the <div> is clicked.
You could fix it by 'wrapping' it in a function:
{click: () => handleDownload(face)}
I've used an arrow function in my example but you could use a normal function if you prefer.
Personally I wouldn't try to do this using the object form of v-on.
My first instinct is that you should consider just having two <div> elements and use v-if to decide which one is showing.
If you did want to use a single <div> I would put the click logic in a method. So:
<div class="..." :face="face" #click="onDivClick(face)">
Note that despite the apparent syntactic similarity to the way you defined your click listener this won't invoke the method immediately.
Then in the methods for the component:
methods: {
onDivClick (face) {
if (this.hasPaid) {
this.handleDownload(face)
} else {
this.addToCollection()
}
}
}