How am I suppose to get the value of a checkbox? - vuejs2

So I have the following element:
<input v-on:click="$emit('addPartId', $event)" v-bind:value="13209" name="add_13209" type="checkbox">
Which then calls the following method:
methods: {
....
addPartId(evnt) {
console.log(evnt);
}
},
In the parent container and is passed to the child:
<table-body
v-bind:items="items"
v-bind:columns="columns"
v-bind:sort-column="sortColumn"
v-bind:direction="direction"
#sort="sort"
#addPartId="addPartId"
>
</table-body>
The question I have, that I can't find on stack, is how do I register a click event so that when the checkbox is clicked I get the event object (I want the value, from v-bind:value, of the checkbox.

You should use event name which is the kebab-cased version, check Vue Guide: Custom Event,
As the guide says:
Unlike components and props, event names will never be used as
variable or property names in JavaScript, so there’s no reason to use
camelCase or PascalCase. Additionally, v-on event listeners inside DOM
templates will be automatically transformed to lowercase (due to
HTML’s case-insensitivity), so v-on:myEvent would become v-on:myevent
– making myEvent impossible to listen to.
For these reasons, we recommend you always use kebab-case for event
names.
Vue.component('my-checkbox', {
template: `
<input v-on:click="$emit('add-part-id', {'whole': $event, 'value':13209})" v-bind:value="13209" name="add_13209" type="checkbox">
`
})
Vue.component('my-another-checkbox', {
template: `
<input v-on:click="$emit('add-part-id', $event)" v-bind:value="13209" name="add_13209" type="checkbox">
`
})
new Vue({
el: '#emit-example-simple',
methods: {
getChecked1: function (ev) {
console.log('checkbox1', ev.value)
console.log('checkbox1', ev.whole.target.value)
},
getChecked2: function (ev) {
console.log('checkbox2', ev.target.value)
}
}
})
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<div id="emit-example-simple">
First Example: <my-checkbox #add-part-id="getChecked1"></my-checkbox>
Second Example: <my-another-checkbox #add-part-id="getChecked2"></my-another-checkbox>
</div>

Related

Vue.js 2: How to bind to a component method?

I have a VueJS (v2) component with a private array of objects this.private.messagesReceived which I want displayed in a textarea. The array should be converted to a string by a method/function and Vue is blocking all my attempts to bind. Every attempt results in my serialization function (converting the array to a string) only being called once and never again when the data changes.
I feel there must be a way to do this without Vue.set() or some forceUpdate shenanigans.
https://jsfiddle.net/hdme34ca/
Attempt 1: Computed Methods
Here we have the problem that Vue only calls my computed method messagesReceived1 once and never again.
<script>
{
computed: {
messagesReceived1() {
console.log("This is called once and never again even when new messages arrive");
return this.private.messagesReceived.join("\n");
},
...
methods: {
addMessage(m) {
console.log("This is called multiple times, adding messages successfully");
this.private.messagesReceived.push(m);
}
}
<script>
<template>
<textarea rows="10" cols="40" v-model="messagesReceived1"></textarea>
</template
Attempt 2: Binding Methods
Here Vue decides it doesn't like moustaches inside a textarea {{ messagesReceived2() }} and balks. It also doesn't allow messagesReceived2() or messagesReceived2 in v-model.
<script>
{
methods: {
messagesReceived2() {
return this.private.messagesReceived.join("\n");
},
addMessage(m) {
console.log("This is called multiple times, adding messages successfully");
this.private.messagesReceived.push(m);
}
}
</script>
<template>
<textarea rows="10" cols="40">{{ messagesReceived2() }}</textarea><!--Nope-->
<textarea rows="10" cols="40" v-model="messagesReceived2()"></textarea><!--Nope-->
<textarea rows="10" cols="40" v-model="messagesReceived2"></textarea><!--Nope-->
</template
You can define a data variable and set its value in the function. Then bind variable with textarea, not directly with the function.

Vue v-on:click change it to load or mouse over event

I am trying to get v-bind:value on to function reservationBy() as load event instead of v_on:clickevent. Right now it passes the value when I click on it only.
Is there a way to make it load automatically or use mouse over event? I even try to use v-on:load and v-on:focus event but it did not work.
View
<div id="app">
<input v-bind:value="2" v-on:click="reservationBy"/>
</div>
Script
new Vue({
el: "#app",
data: {
},
methods: {
reservationBy: function(e) {
var peopleBookedId = e.target.value;
console.log(peopleBookedId);
}
}
})
Here is example on JSFIDDLE
https://jsfiddle.net/ujjumaki/yz0p1vqL/4/
#mouseover and #mouseleave will do the job.
<input v-bind:value="2" #mouseover="reservationBy"/>
or
<input v-bind:value="2" #mouseleave="reservationBy"/>
If using v-model is not an option (that would be the easiest way), and you want to execute the function when component is rendered, you can use ref and access value in mounted hook:
<input ref="myInputRef" :value="2" />
Script:
mounted: function() {
console.log(this.$refs.myInputRef.value);
}

Using $emit to call a function

I'm new to vue.js and am trying to understand how $emit can be used. In the following code, every input element except for the last one works as expected.
My assumption in the last input element is that calling $emit with an event name would be the same as calling the function which has that event name, but it doesn't call that function. What is occurring with this $emit?
I've read through Binding Native Events to Components and that shows that it is to be used differently as I'm doing it. All this approach started from me watching a YouTube video (7 Secret Patterns...), specifically at this time https://youtu.be/7lpemgMhi0k?t=21m57s where you can see that usage on the slide.
Here is the code in JSFiddle https://jsfiddle.net/sbtmfweq/
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<div id="test-app">
<input v-model="text1" placeholder="edit me" #keyup.enter="submit"> {{ text1 }}
<br><br>
<input :value="text2" #input="inputEvent" #keyup.enter="submit"> {{ text2 }}
<br><br>
<input :value="text2" #input="inputEvent($event)" #keyup.enter="submit"> {{ text2 }}
<br><br>
<input :value="text2" #input="$emit('inputEvent', $event.target.value)"> {{ text2 }} | {{reversedText2}}
</div>
<script>
new Vue({
el: '#test-app',
data: {
text1: 'text1',
text2: 'text2',
},
methods: {
log: console.log,
submit: function(event) {
console.log("submit -->", event, event.target.value, '<--')
},
inputEvent: function(event) {
console.log("input 2 -->", event.target.value, '<--')
this.text2 = event.target.value
},
},
watch: {
text1: {
handler: function(newValue, oldValue) {
console.log("input 1 -->", newValue, '<-->', oldValue, '<--')
}
}
},
computed: {
reversedText2: function () {
return this.text2.toUpperCase()
}
}
})
</script>
It's just a simple issue. Whenever you are dispatching an event, you have to add listener to it. In $emit, the first param is actually the name of the event.
As written in docs for $emit.
vm.$emit( eventName, […args] )
In the below code, I have added an event listener, using $on. Also I have changed the arg for $emit.
<input :value="text2" #input="$emit('inputEvent', $event)"> {{ text2 }} | {{reversedText2}}
<script>
new Vue({
...
created(){
this.$on('inputEvent', this.inputEvent);
}
...
})
</script>
I hope it helps.
$emit works like this.
Say you have 2 components, <parent> and <child>, where <child> is inside <parent>'s template.
When <child> emits an event, it can be handled by the <parent> component only (unless you register a listener via $on, but that's unrelated to your scenario). In your code, the $emit call is executed within the scope of the parent component (which is the root component instantiated with new Vue()); that is, the parent component is the one that's emitting the event. The root component has no parent so the emit call is pointless.
My assumption in the last input element is that calling $emit with an event name would be the same as calling the function which has that event name, but it doesn't call that function.
This isn't true; the name of the event has no relation to the method registered as a listener for that event in the parent.
it's normal that $emit doesn't work in your case.
In fact, $emit is used for communication between parent and child components. Here, you are using $emit but you haven't defined any parent component.
Here is the docs for the custom events.
Hope it helps!

Get reference to element in method in Vue.js

How can I get reference to the element that fired the method in Vue.js?
I have HTML like this:
<input type="text" v-model="dataField" v-bind:class="dataFieldClass" />
And in my Vue.js viewmodel I have a method:
dataFieldClass: function () {
// Here I need the element and get its ID
// Pseudo code
var elementId = $element.id;
}
I know that it's possible to get the element from event (v-on:click), but this is not an event, it's a simple method returning CSS class for the element according to few conditions of the viewmodel. It should be computable as well, but the problem is the same.
You can get the reference to your element in three ways
1. with Method Event Handlers (doc)
template:
<input type="text" v-model="dataField" v-bind:class="dataFieldClass" />
script:
dataFieldClass: function (e) {
const element = e.target;
}
2. with Inline Handlers (doc)
template:
<input type="text" v-model="dataField" v-bind:class="dataFieldClass($event, otherArgument)" />
script:
dataFieldClass: function (e, otherArgument) {
const element = e.target;
}
3. with Refs (doc)
template:
<input type="text" v-model="dataField" v-bind:class="dataFieldClass" ref="el"/>
script:
dataFieldClass: function () {
const element = this.$refs.el;
}
Maybe you could use ref?
<input type="text" v-model="dataField" v-bind:class="dataFieldClass" ref="el" />
And use it like this:
dataFieldClass: function () {
var elementId = this.$refs.el;
}
See documentation here: https://v2.vuejs.org/v2/api/#ref
What about using the ref pattern. Put ref="someName" in your DOM element, and access it in your method with this.$refs["someName"] (you can pass 'someName' as parameter to your method).
Note that's not a very good pattern except if for some reason you really need the DOM element. Otherwise just pass a relevant parameter to your method.
It's not a good method mainly because it has a major drawback: there is no $refs the first time the vue is rendered (because the element is not present yet). So you should force the vue to render twice.
If you have multiple elements inside a v-for loop, then this.$refs["someName"] becomes an array. You can get it to work with some adaptation, here is an example:
new Vue({
el: '#app',
data() {
return {
fields: [{
name: 'field1',
value: 'value1'
},
{
name: 'field2',
value: 'value2'
}
]
};
},
methods: {
dataFieldClass(index) {
if (!this.$refs.fields) {
// First render, the element is not there yet
return '';
} else {
// Here is the element
console.log(this.$refs.fields[index]);
}
}
},
mounted() {
// Force the instance to render a second time
this.$forceUpdate();
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.3.4/vue.js"></script>
<div id="app">
<label v-for="(field, index) in fields">
{{ field.name }}:
<input ref="fields" :value="field.value" v-bind:class="dataFieldClass(index)">
</label>
</div>
You can get the reference from DOM event object. "event.currentTarget" is the property that references the element where the event listener(vuejs method) assigned.
This is standard DOM specification, but you can also use this property in Vuejs.
dataFieldClass: function (event) {
var elementId = event.currentTarget.id;
}
A straightforward solution is to pass a reference to the element in the method to be called.
Here's what worked for me (a pretty basic example to help understand):
new Vue({
el: '#app',
data: {
msg: '',
},
methods: {
// in order to access the HTML element,
// add an argument (namely 'event') in the method definition,
// and access the element's current value by `event.target.value`
updateValue: function(event) {
this.msg = event.target.value;
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<input :value="msg" #input="updateValue" autofocus>
<br/>
<h2>
>>> {{ msg }}
</h2>
</div>
This seem to work for me, using ref (if element is nested another element)
<div ref="element">
vm.$refs.element
or $el if targeted element is the outermost
<template><div class="targeted-element">
this.$el
You can use refs as mentioned in other answers here.
Remember, refs cannot apply to computed objects. So be careful when using refs

Is it possible to access event listeners from component in Vue.js 2

I have a custom component in Vue.js(2) as:
Vue.component("modal-panel", {
props: {
id: {
required: true
},
title: {} ,
size: {
validator: function(value) {
return !value || value=="lg" || value=="sm";
}
},
confirmLabel: {
"default": "Yes",
},
closeLabel: {
"default": "No"
},
confirm: {
type: Function
}
},
//...
template: `
//...
<button type="button" class="btn btn-primary confirm" data-dismiss="modal" v-on:click="$emit('confirm')" v-if="confirm">{{confirmLabel}}</button>
//...
`
}
And this is the code using component
<modal-panel title="New User" id="userModal" #confirm="doSomething">...</modal-panel>
As seen from the component code, confirm has been inserted into the props and on the button code in the template there is a conditional rendering according to whether confirm listener attached or not. However, button is not rendered. I checked component dom and properties, but there is not such an info.
Is it possible to make conditional rendering according to whether a specific listener attached to component in vue.js?
Thanks.
Since Vue 2.4, Vue components have a $listeners property.
Contains parent-scope v-on event listeners (without .native
modifiers).
This is documented here. You can determine whether or not the parent is listening to a particular event by examining the $listeners property.
**Original Answer**
It's not generally good practice for a component to reach out of itself to determine things.
I would recommend instead that you have a confirm callback property. You can pass a function in as a property. Then you can decide to show/hide the button on whether you received the callback or not.
Vue.component("modal",{
props:["confirm"],
template: `
<div>
<h1>Modal</h1>
<button v-if="confirm" #click="confirm">Confirm</button>
</div>
`
})
Example.
Edit
You can determine if there is a handler defined on a component for a given event, but it requires examining an internal Vue property, and you should only use this at your own risk.
Vue.component("modal",{
template: `
<div>
<h1>Modal</h1>
<button v-if="hasConfirmHandler" #click="$emit('confirm')">Confirm</button>
</div>
`,
computed:{
hasConfirmHandler(){
return !!this._events["confirm"]
}
}
})
The _events property of the component will contain the handler, if a handler is defined from the parent.
Example.
You need to bind your function with v-bind or : instead of just passing it as a string. So use :confirm syntax:
<modal-panel title="New User" id="userModal" :confirm="doSomething"></modal-panel>
Then in component template simply use v-on:click="confirm()":
<button type="button" class="btn btn-primary confirm" data-dismiss="modal"
v-on:click="confirm()"
v-if="confirm">
{{confirmLabel}}
</button>