How to binding only single class when using several v-on:click? - vue.js

I'm newbie and studying Vue.js
I create v-on:click function and element toggle class when I click the button.
I am not good at English, I think it will be fast to show the code.
<button #click="bindA = !bindA">A</button>
<button #click="bindB = !bindB">B</button>
<span :class="[{ classA:bindA }, { classB:bindB }]"></span>
data: function() {
return {
bindA: true, // default
bindB: false
}
it's now. clicked bindA and B.
// browser
<span class="classA classB"></span>
but I want
// bindA click , remove classB
<span class="classA"></span>
// bindB click , remove classA
<span class="classB"></span>
It's simple in jquery, but difficult in vue.

It's very simple in vue as well.
Bind the class according to the conditions you want to see the data
:class="{'classA': (bindA== true), 'classB':(bindA== false)}"

Try adding a method to the #click - then you can build up a more complex logic, than simple "toggle".
new Vue({
el: "#app",
data: {
bindA: true,
bindB: false
},
methods: {
bind(btn) {
if ((btn === 'A' && !this.bindA) || (btn === 'B' && !this.bindB)) {
this.bindA = !this.bindA
this.bindB = !this.bindB
}
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<button #click="bind('A')">A</button>
<button #click="bind('B')">B</button>
<span :class="{ classA:bindA, classB:bindB }"></span>
</div>

Related

How to execute a function when Click Button (X) on Input Field search vuejs

I need to do an action when Click Button (X) on search is clicked using Vue.
when he clicks on the input seach? on "x
new Vue({
el: "#app",
data: {
msg: 'the input is cleaned',
info:''
},
methods: {
handleClick: function(){
// to do sonthing when is cleaned not before
if(this.info=='')
alert(this.msg)
},
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<input type="search" v-model="info" #click="handleClick" >
<br>
<span>{{ info }}</span>
</div>
You seem to want to differentiate between a normal click inside a input[type="search"] and a click on its clear control. I've spent some time trying to compare the two clicks and I wasn't able to find how they differ.
But I came up with a trick to figure it out:
if the input has a value (you need one for the clear button to be displayed)
we wait for DOM to update and, after it did, if the input no longer has a value (it was cleared) => bingo! it was a clear search click.
Therefore:
Vue.config.devtools = false;
Vue.config.productionTip = false;
new Vue({
el: '#app',
methods: {
handleClick(e) {
if (e.target.value) {
this.$nextTick() // wait for DOM update
.then(() => {
if (e.target.value === '') {
console.log('clear button clicked you must have, young Jedi!');
}
})
}
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<input type="search" #click="handleClick">
</div>
However, I believe you're better off with a watch on your v-model:
watch: {
info(newVal, oldVal) {
if (oldVal && !newVal) {
console.log('info was cleared').
}
}
}
Vue.config.devtools = false;
Vue.config.productionTip = false;
new Vue({
el: '#app',
data: () => ({ info: '' }),
watch: {
info(newVal, oldVal) {
if (oldVal && !newVal) {
console.log('morning has broken...');
}
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<input type="search" v-model="info">
</div>
It has the advantage it will not only trigger on click. It will trigger every time info changes from a truthy value to a falsy one, regardless of what caused the change (i.e: user selecting the text and pressing Delete or Backspace; any other component method clearing it, resetting the input or the parent form, etc...).
From the comment on the question, you're asking to call a function when the user clicks on the input field ? It's the way that I understood your question.
For that case, here is a quick demo on how to achieve that.
https://codesandbox.io/s/dry-lake-34bvi?file=/src/App.vue

Update properties of component in Vue.js

I've been searching for the answer 2nd day. But still couldn't find solution.
I have the modal window template. And the main page template from where I need to update modal window size by clicking on the button (span). Shortly it's like this for HTML:
<template id="modal">
<div>
<div :class="'modal-' + size">
...
</div>
</div>
</template>
<template id="list">
<div>
<span #click="onDetails">
Show Details
</span>
</div>
<modal size="md" #showdetails="showdetails();" ref="modal">
...
</modal>
</template>
And for JS:
Vue.component("modal", {
template: "#modal",
props: {
size: {
type: String,
default: ""
}
},
methods: {
onDetails() {
this.$emit("showdetails")
}
}
})
var List = Vue.extend({
template: "#list",
methods: {
showDetails() {
if(this.$refs.modal.size == "md") {
this.$refs.modal.size = "lg"
}
<additional code here>
}
}
})
When I'm accessing this.$refs.modal.size for read - it's OK. When I'm just changing it from showDetails - OK if only this action in the function. When I'm put something else instead of - size is not updating.
For example:
this.$refs.modal.size = "lg" - will work
this.$refs.modal.theme = "danger"; this.$refs.modal.size = "lg" - neither of them are updating
What am I doing wrong?
You need to assign the attribute value of Size by the javascript method setAttribute . Example : this.$refs.modal.setAttribute('size', 'lg')
There is a working demo below:
new Vue({
el: '#app',
methods: {
showdetails() {
console.log(this.$refs.modal.getAttribute('size'));
this.$refs.modal.setAttribute('size', 'lg')
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.15/vue.js"></script>
<div id='app'>
<button size="md" #click="showdetails" ref="modal">Click</button>
</div>

Vue updating components at the same time after push

I am building a form in Vue
I have a component that looks as follow:
<template>
<transition name="preview-pane">
<label>{{ option.group }}</label>
<input type="text" class="form-control"
:name="`group_name[${index}]`"
v-on:input="option.group = $event.target.value"
:value="option.group">
<a ref="#" class="btn btn-primary float-right" #click="$emit('copy')" role="button">{{ __('Copy') }} </a>
</transition>
</template>
<script>
export default {
props: {
option: {
group: ''
},
index: {}
}
}
</script>
My Vue instance is as follow:
var products = new Vue({
el: '#products',
data: {
options: []
},
methods: {
add() {
this.options.push({
group: ''
})
},
copy(index) {
this.options.push(this.options[index])
}
}
})
And last my html looks as follow
<product-option
v-for="(option, index) in options"
:key="index"
:option="option"
:index="index"
#copy="copy(index)">
</product-option>
I have one button that basically takes one of the options and push it once again (copy method on the vue instance). When I run everything seems fine but then when I change the input it update the props of all the components that have been copied.
What can I do to make vue understand that each component should work separately?
Well in case someone have the same issue, I sort it out like this:
copy(index) {
var object = this.options[index]
var newObject = {}
for (const property in object) {
newObject[property] = object[property]
}
this.options.push(newObject)
}

Conditionally rendering icons only one time depending on property

I am trying to write a template that displays either Icon AAA or Icon BBB, depending on whether or not the item in the current iteration has a specific flag. Here is my code:
<div v-for="(item, itemIndex) in items">
<div v-if="item.hasUnreadComments">
<span>Display Icon AAA</span>
</div>
<div v-else>
<span>Display Icon BBB</span>
</div>
</div>
The issue here is that I need either icon displayed ONCE. If more than one item has it set to item.hasUnreadComments === true, Icon AAA will be displayed equally as many times, which is not what I want. Couldnt find anything in the docs and I dont want to bind it to a v-model.
Can this be done in Vue without a third data variable used as a flag?
You will have to do some sort of intermediate transformation. v-if is just a flexible, low-level directive that will hide or show an element based on a condition. It won't be able to deal directly with how you expect the data to come out.
If I'm understanding what you're asking for, you want an icon to only be visible once ever in a list. You can prefilter and use an extra "else" condition. This sounds like a use case for computed properties. You define a function that can provide a transformed version of data when you need it.
This example could be improved upon by finding a way to boil down the nested if/elses but I think this covers your use case right now:
const app = new Vue({
el: "#app",
data() {
return {
items: [{
hasUnreadComments: true
},
{
hasUnreadComments: true
},
{
hasUnreadComments: false
},
{
hasUnreadComments: true
},
{
hasUnreadComments: false
},
]
}
},
computed: {
filteredItems() {
let firstIconSeen = false;
let secondIconSeen = false;
return this.items.map(item => {
if (item.hasUnreadComments) {
if (!firstIconSeen) {
item.firstA = true;
firstIconSeen = true;
}
} else {
if (!secondIconSeen) {
item.firstB = true;
secondIconSeen = true;
}
secondIconSeen = true;
}
return item;
});
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div v-for="(item, itemIndex) in filteredItems">
<div v-if="item.firstA">
<span>Display Icon AAA</span>
</div>
<div v-else-if="item.firstB">
<span>Display Icon BBB</span>
</div>
<div v-else>
<span>Display no icon</span>
</div>
</div>
</div>

v-on:click problem if the elements has another element inside

I am dealing with the problem of my click event in vue js. I made a click even on element that has another element inside it.
Here's my code:
<span class="pull-down-controller" #click="pullDown($event)">
<span class="indicator">-</span> Controller
</span>
in the frontend it will show - Controller
if I click the word Controller it will call the specified function which is pullDown() but why is it whenever I click the indicator or the minus symbol, it will not do anything even if it is inside the <span> where I put the #click event?
The reason why I put a <span> inside it so I can change the symbol to + using jquery.
thanks!
No need for jQuery, Vue's reactivity provides all you need:
new Vue({
el: '#app',
template: `
<span #click="pullDown" style="font-size: 48px;">
<span>{{ indicator }}</span> Controller
</span>
`,
data () {
return {
expanded: false
}
},
computed: {
indicator () {
return this.expanded ? '+' : '-'
}
},
methods: {
pullDown (event) {
this.expanded = !this.expanded
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app"></div>