Getting the value of button (vuejs) - vue.js

i want to get the value of the button when click i already add a method. but whenever i click theres nothing. the value not passing. can anyone help me? Thank you.
Here is the code:
<button
class="button buttonvalue"
v-for="(p,index) in buttonvalue"
:key="index"
#click="webcamSendRequestButton($event)"
>{{p}}</button>
Method:
methods: {
webcamSendRequestButton: function(e) {
// const buttonValue = e.target.value;
// console.log(e.target.value)
alert(e.target.value);
}
}
DATA (Json) of button value
array data: small,medium,large
this.buttonvalue= this.item[0].buttonvalue.split(',');

To get value button value you can either bind a :value attribute first like:
<button
class="button buttonvalue"
v-for="(p,index) in buttonvalue"
:key="index"
#click="webcamSendRequestButton($event)"
:value="p"
>{{p}}</button>
Then e.target.value will work fine.
Or, if you don't want to add another attribute, then you can simply use:
methods: {
webcamSendRequestButton(e) {
console.log(e.target.textContent)
}
}
instead of using e.target.value.
Or, if the value p is required, then you can simply pass that to click handler like:
#click="webcamSendRequestButton(p)"
and then access it like:
methods: {
webcamSendRequestButton(buttonvalue) {
console.log(buttonvalue)
}
}

If you simply need to pass a value of p, for example then you would write:
#click="webcamSendRequestButton(p)"
then in your method:
methods: {
webcamSendRequestButton(val) {
alert(val);
}
}
I am assuming that buttonvalue is some kind of array that you are looping through.
Hope it helps.

Related

How to Call Function on Each dom-repeat Element

I am trying to call a function on each element inside of a dom-repeat template.
<dom-repeat items="[[cart]]" as="entry">
<template>
<shop-cart-item id="item"></shop-cart-item>
</template>
</dom-repeat>
...
checkStatus() {
this.$.item.doSomething();
}
How can I call doSomething on each element?
You can iterate through nodes like:
checkStatus() {
const forEach = f => x => Array.prototype.forEach.call(x, f);
forEach((item) => {
if(item.id == 'cartItem') {
console.log(item);
item.doSomething(); // call function on item
}
})(this.$.cartItems.childNodes)
}
You can add an on-tap event in the loop. In order to observe which item you clicked look into model property :
<dom-repeat items="[[cart]]" as="entry">
<template>
<!-- need to give dynamic id for each item in dome-repeat -->
<shop-cart-item id="[[index]]" on-tap = 'checkStatus'></shop-cart-item>
</template>
</dom-repeat>
...
checkStatus(status) {
console.log(status.model) // you can get index number or entry's properties.
this.$.item.doSomething();
}
EDIT:
So as per comment of #Matthew, if need to call a function in one of element's function in dom-repeat first give a dynamic id name as above then:
checkStatus(status) {
this.shadowRoot.querySelector('#'+ status.model.index).doSomething();
}

Vue js dynamic update set/unset class

I have few inputs with submit button. Have some validation logic that adds 'has-error' class to input. How can i unset this class on focus?
Template:
<div class="input-styled badge-icon" :class="{ 'has-error': errors.email}">
<input type="text" #focus="delete errors.email" v-model="email" placeholder="example#gmail.com">
</div>
<button #click="submit">Submit</button>
JS
data() {
return {
errors: {},
email: ''
}
},
methods: {
submit(){
this.errors = {};
if(!this.email){
this.errors.email = 'Something';
}
}
}
I'm trying delete error property, trying #focus='errors.email="" ', but class 'has-error' disappears only when i'm typing something on inputs. #focus event works and i think that i should call some function that will update my DOM?
It is a good practice to move operations on component's data to functions. You can achieve desired validation reset, by creating a resetValidation function and binding it to focus event on input field.
Method itself should reset errors field to falsy values. Example below assumes, there are multiple input fields in the form. Each field should call resetVlidation method with corresponding error field name. If no field is provided, we can reset validation as whole:
resetValidation (field) {
if (field) {
this.errors = {
...this.errors,
[field]: ''
}
} else {
this.errors = {}
}
Please, check the working example below:
codesandbox

Toggle of TWO classes in Vuejs

I have tried for a few hours to be able to add / remove TWO classes in Vue JS.
The doc examples only show how to toggle one.
I have a button, which I click that I want to change class to either: active toggle-on or toggle-off
I can do the "active" on and off, but I can not seem to be able to add/remove the second class.
The button looks like this:
<button v-on:click="toggleOnTop" id="toggleTopButton" v-bind:class="toggleActive"></button>
And my data:
data: () => ({
toggleActive: {
active: true,
'toggle-on': true
}
})
But it still only applies these two. How can I apply "toggle-off" in reverse?
You may want to use a computed property or object syntax for this, lets say your method toggles a boolean in your data:
data () {
return {
isActive: false
}
}
methods: {
toggleOnTop () {
this.isActive = !this.isActive
}
}
The short form would be to add the following class binding:
< ... v-bind:class="{'active toggle-on': isActive, 'toggle-off': !isActive}">
Another approach would be to use a computed property that will set the classes as you want:
computed: {
toggleActive () {
return {
'active': this.isActive,
'toggle-on': this.isActive,
'toggle-off': !this.isActive
}
}
}
Know too that you are able to pass a ternary operator to the class attribute when bound. For example:
<i :class="['fa', isHappy ? 'fa-smile' : 'fa-frown']"></i>
This just saves from having to repeatedly use the same boolean value in an object as suggested above, especially if you need to have multiple classes bound to each state - like so:
<i :class="['fa', isHappy ? 'fa-smile active' : 'fa-frown']"></i>

Using $refs in a computed property

How do I access $refs inside computed? It's always undefined the first time the computed property is run.
Going to answer my own question here, I couldn't find a satisfactory answer anywhere else. Sometimes you just need access to a dom element to make some calculations. Hopefully this is helpful to others.
I had to trick Vue to update the computed property once the component was mounted.
Vue.component('my-component', {
data(){
return {
isMounted: false
}
},
computed:{
property(){
if(!this.isMounted)
return;
// this.$refs is available
}
},
mounted(){
this.isMounted = true;
}
})
I think it is important to quote the Vue js guide:
$refs are only populated after the component has been rendered, and they are not reactive. It is only meant as an escape hatch for direct child manipulation - you should avoid accessing $refs from within templates or computed properties.
It is therefore not something you're supposed to do, although you can always hack your way around it.
If you need the $refs after an v-if you could use the updated() hook.
<div v-if="myProp"></div>
updated() {
if (!this.myProp) return;
/// this.$refs is available
},
I just came with this same problem and realized that this is the type of situation that computed properties will not work.
According to the current documentation (https://v2.vuejs.org/v2/guide/computed.html):
"[...]Instead of a computed property, we can define the same function as a method. For the end result, the two approaches are indeed exactly the same. However, the difference is that computed properties are cached based on their reactive dependencies. A computed property will only re-evaluate when some of its reactive dependencies have changed"
So, what (probably) happen in these situations is that finishing the mounted lifecycle of the component and setting the refs doesn't count as a reactive change on the dependencies of the computed property.
For example, in my case I have a button that need to be disabled when there is no selected row in my ref table.
So, this code will not work:
<button :disabled="!anySelected">Test</button>
computed: {
anySelected () {
if (!this.$refs.table) return false
return this.$refs.table.selected.length > 0
}
}
What you can do is replace the computed property to a method, and that should work properly:
<button :disabled="!anySelected()">Test</button>
methods: {
anySelected () {
if (!this.$refs.table) return false
return this.$refs.table.selected.length > 0
}
}
For others users like me that need just pass some data to prop, I used data instead of computed
Vue.component('my-component', {
data(){
return {
myProp: null
}
},
mounted(){
this.myProp= 'hello'
//$refs is available
// this.myProp is reactive, bind will work to property
}
})
Use property binding if you want. :disabled prop is reactive in this case
<button :disabled="$refs.email ? $refs.email.$v.$invalid : true">Login</button>
But to check two fields i found no other way as dummy method:
<button :disabled="$refs.password ? checkIsValid($refs.email.$v.$invalid, $refs.password.$v.$invalid) : true">
{{data.submitButton.value}}
</button>
methods: {
checkIsValid(email, password) {
return email || password;
}
}
I was in a similar situation and I fixed it with:
data: () => {
return {
foo: null,
}, // data
And then you watch the variable:
watch: {
foo: function() {
if(this.$refs)
this.myVideo = this.$refs.webcam.$el;
return null;
},
} // watch
Notice the if that evaluates the existence of this.$refs and when it changes you get your data.
What I did is to store the references into a data property. Then, I populate this data attribute in mounted event.
data() {
return {
childComps: [] // reference to child comps
}
},
methods: {
// method to populate the data array
getChildComponent() {
var listComps = [];
if (this.$refs && this.$refs.childComps) {
this.$refs.childComps.forEach(comp => {
listComps.push(comp);
});
}
return this.childComps = listComps;
}
},
mounted() {
// Populates only when it is mounted
this.getChildComponent();
},
computed: {
propBasedOnComps() {
var total = 0;
// reference not to $refs but to data childComps array
this.childComps.forEach(comp => {
total += comp.compPropOrMethod;
});
return total;
}
}
Another approach is to avoid $refs completely and just subscribe to events from the child component.
It requires an explicit setter in the child component, but it is reactive and not dependent on mount timing.
Parent component:
<script>
{
data() {
return {
childFoo: null,
}
}
}
</script>
<template>
<div>
<Child #foo="childFoo = $event" />
<!-- reacts to the child foo property -->
{{ childFoo }}
</div>
</template>
Child component:
{
data() {
const data = {
foo: null,
}
this.$emit('foo', data)
return data
},
emits: ['foo'],
methods: {
setFoo(foo) {
this.foo = foo
this.$emit('foo', foo)
}
}
}
<!-- template that calls setFoo e.g. on click -->

Vue.js 2 - $forceUpdate() on components doesn't refresh computed properties?

I'm not sure if I'm doing this right or wrong, but all the answers I seem to find how to update the dom for computed values...
I have this component:
Vue.component('bpmn-groups', {
props: ['groups', 'searchQuery'],
template: '#bpmn-groups',
computed: {
filteredGroups: function () {
var self = this;
return this.groups.filter(function(group) {
self.searchQuery = self.searchQuery || '';
return _.includes( group.name.toLowerCase(), self.searchQuery.toLowerCase() );
});
}
},
methods: {
clearFilter: function () {
this.searchQuery = '';
},
deleteGroup: function(group) {
Vue.http.delete('api/groups/'+group.id ).then(response => { // success callback
var index = this.groups.indexOf(group); // remove the deleted group
this.groups.splice(index, 1);
this.$forceUpdate(); // force update of the filtered list?
toastr.success('Schemų grupė <em>'+group.name+'</em> sėkmingai pašalinta.');
}, response => { // error callback
processErrors(response);
});
this.$forceUpdate();
},
},
});
And in the template I just have a simple v-for to go through filteredGroups:
<input v-model="searchQuery" type="text" placeholder="Search..." value="">
<div v-for="group in filteredGroups" class="item">...</div>
The deletion works fine, it removes it from groups property, however the filteredGroups value still has the full group, until I actually perform a search or somehow trigger something else...
How can I fix it so that the filteredGroup is updated once the group is updated?
Don't mutate a prop - they are not like data defined attributes. See this for more information:
https://v2.vuejs.org/v2/guide/components.html#One-Way-Data-Flow
Instead, as recommended in the link, declare a local data attribute that is initialized from the prop and mutate that.