According to vue we can pass events and handlers as objects (very useful for slots).
Script
computed: {
on() {
return {
input: this.onInput,
keyup: this.onEnter
}
}
}
Template
<template>
<div>
<slot name="mySlot" :on="on"></slot>
</div>
<template>
So in this example, lets say the slot takes an input field. In this case, that input field will emit every input event and key up event. Both these events will be caught by the 'child component' which is the one defined here, that declares the slot.
However if we just wanted to get the 'enter' keyup event, this syntax doesn't seem to work.
Script
computed: {
on() {
return {
input: this.onInput,
'keyup.enter': this.onEnter
}
}
}
Using the above, the keyup enter event is not caught. Anyone know how to reconcile the syntax here, so the events can be caught?
The object syntax of v-on doesn't support event modifiers out of the box. The object keys can only be event names, so using keyup.enter would register an event listener for the nonexistent keyup.enter event (instead of the keyup event).
One solution is to listen to keyup, and check the event's key for 'Enter':
export default {
computed: {
on() {
return {
keyup: e => {
if (e.key === 'Enter') {
this.onEnter(e)
}
}
}
}
}
}
demo
Related
Working with VUE.JS...
Having a 'theParent' component with two childs: 'theForm' and 'theButtons'.
(This is a simplification of more complex scenario).
In 'theButtons' exists a 'Clear' button to clean fields of 'theForm' form.
I am able to pass the event from 'theButtons' to 'theParent', but not from 'theParent' to 'theForm' ¿?
Within 'theButtons':
<b-button v-on:click="clean()">Clear</b-button>
methods: {
clean() {
this.$emit('clean');
}
},
Within 'theParent':
<theForm />
<theButtons v-on:clean="clean"/>
methods: {
clean() {
this.$emit('theForm.clean'); //Here is the point I dont know what to put
}
},
Within 'theForm':
methods: {
clean() {
alert("This is what I want to get executed!!");
}
},
Add a ref to the TheForm component then run its method from parent :
<theForm ref="form" />
<theButtons v-on:clean="clean"/>
methods: {
clean() {
this.$refs.form.clean(); //clean should be a method in theForm component.
}
},
Or you could add a prop to theForm component which could be updated in parent and watch in child component to clean the form :
<theForm :clean="clean" />
<theButtons v-on:clean="clean"/>
data(){
return {
clean:false
}
},
methods: {
clean() {
this.clean=clean;
}
},
inside theForm :
props:['clean'],
watch:{
clean(val){
if(val){
this.clean()
}
}
}
One thing to keep in mind is the flow of passing data from child to parent and vice versa.
In a nutshell:
Parent => Child: if you want to pass data from parent to child, use props.
Child => Parent: if you want to pass data from child to parent, use events.
One solution to your problem is to have <b-button> as a child to the <theForm> as follows:
<theForm v-on:clean="cleanForm">
<!-- form children -->
....
<theButton />
....
</theForm>
Another solution is to have an event bus that passes events between components which is totally unnecessary in your case.
Reference: https://v3.vuejs.org/guide/component-basics.html#passing-data-to-child-components-with-props
https://v3.vuejs.org/guide/component-basics.html#listening-to-child-components-events
I have a Vue component that receives a bunch of props through v-bind and has multiple event handlers attached using v-on.
<SomeComponent
v-for="object in objects"
v-bind:prop1="prop1"
v-bind:prop2="prop2"
v-bind:prop3="prop3"
v-bind:key="object.created_at"
v-on:event-one="eventOne"
v-on:event-two="eventTwo"
v-on:event-three="eventThree"
/>
All works fine.
The problem is that this component can appear on different parts of the interface depending on some conditionals. It's the exact same component with the exact same props and event handlers.
Our current approach is a simple copy and paste of all the above lines, but it seems error prone and verbose, since if tomorrow we need to add another event handler (say v-on:event-four="eventFour"), it requires it to be added manually to every instance of SomeComponent in the template. The same goes for any prop change and so on.
In React we would probably wrap that component in a function and just invoke it like {renderSomeComponent()} as needed.
What would the approach be using Vue?
One approach would be to use a method to create JavaScript objects for props and events. (You could get away with computed properties except that one of your bindings depends on the object of the v-for loop.)
<SomeComponent
v-for="object in objects"
v-bind="getProps(object)"
v-on="getHandlers()"
/>
computed: {
getHandlers() {
return {
"event-one": this.eventOne,
"event-two": this.eventTwo,
"event-three": this.eventThree
};
}
},
methods: {
getProps(object) {
return {
"prop1": this.prop1,
"prop2": this.prop2,
"prop3": this.prop3,
"key": object.created_at
}
},
eventOne(). { /* ... */ },
eventTwo() { /* ... */ },
eventThree() { /* ... */ }
},
data() {
return {
prop1: /* ... */,
prop2: /* ... */,
prop3: /* ... */
}
}
<el-form-item label="range"
v-if="isShowDatepicker">
<el-date-picker type="datetimerange"
style="width: 100%"
v-model="timeRange"
range-separator="to"
start-placeholder="start"
end-placeholder="end"></el-date-picker>
</el-form-item>
I have a form that has an item to input date and time (just like the code above), and I want to obtain the value after it has changed. I want to use Vue watch to accomplish this task. However, I don't know how to name the variable in watch. I've tried this:
"SearchForm.timeRange": function (val) {
console.log(val)
}
To watch data changes using the watch object, add a function to that object with a name matching the data property to watch. For instance, to watch a data property named "question", add this function:
watch: {
question(newValue, oldValue) {
//...
}
}
Alternatively, you could add an object named after the target data property, with a handler function and additional watcher options: deep and immediate.
watch: {
question: {
deep: true, // detecting nested changes in objects
immediate: true, // triggering the handler immediately with the current value
handler(newValue, oldValue) {
//...
}
}
}
In your case with "timeRange", the syntax would be:
watch: {
timeRange(newValue) {
//...
}
}
demo
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
I have the following component:
Vue.component('visible-filter', {
template: `
<span class="text-muted"
#mouseenter="changeClassMouseenter($event)"
#mouseout="changeClassMouseout($event)"
#click="countryTest(filter)"
#clicked="clicked = true"
v-model="clicked"
><slot></slot></span>
`,
props: ['filter', 'clicked'],
methods: {
changeClassMouseenter(event) {
console.log(this.clicked);
event.target.classList.remove('text-muted')
},
changeClassMouseout(event) {
event.target.classList.add('text-muted')
},
countryTest(filter) {
Event.$emit('clicked');
Event.$emit('country-filter', filter);
}
}
});
The Event in the main instance:
Event.$on('clicked', () => {
this.clicked = true;
})
The data in the main instance:
data: {
clicked: false
},
The thing I want to do:
When I click on the element, I want to set the clicked property to true (for that element), and for the rest of the elements I want to set it to false; Also I want to check if the clicked is true/false when the mouseenter/mouseout event is fired.
How can I achive this?
You should not change the parent from child.
To make communication between parent and child (and child->parent as well) you can/should set up the events.
Emit the event in child component. Nice examples here: https://v2.vuejs.org/v2/guide/components.html#Using-v-on-with-Custom-Events
Listen to event in your parent using <child-component #yourEventName='eventHandler(data)'>
Handle data in your parent, so add eventHandler(data) into your methods and do whatever you want with your data.