I want to add #change event on custom input component.
This is my component:
<template>
<div class="w-100">
<div class="form-text">
<input
:value="value"
#input="updateValue($event.target.value)"
autocomplete="off"
class="form-text__input"
/>
</div>
</div>
</template>
<script>
export default {
name: 'FormText',
props: {
value: {
required: true,
type: String
}
},
methods: {
updateValue(value) {
this.$emit('input', value)
}
}
}
</script>
and this is how I use it:
<form-text
v-model="form.placeOfBirth"
/>
I want to add #change event. This event should fire when user start typing in input, not when data came from API and input were filled.
in the template:
<input
...
#keyup="onKeyUp"
/>
in the code:
methods: {
onKeyUp(event) {
this.$emit('change', event)
}
}
Please pay attention that this event will be fired after every pressed key.
Related
I´m using a Vue base component which wraps a simple check box. In my template Im doing a 2-way bind using a v-model to a Boolean variable in my data. Nothing too fancy but there´s a problem with my implementation where instead of the target variable receiving a true/false value when the control state is turned on/off (check/unchecked), it receives an event object. I don't know what I'm doing wrong, any ideas?
I see this exception in the console when I click the control:
[Vue warn]: Invalid prop: type check failed for prop "value". Expected
Boolean, got Event
found in
<BaseSwitch> at src/components/BaseSwitch.vue
<Card> at src/components/Card.vue
<SlideYUpTransition>
<Modal> at src/components/Modal.vue
<RFQSales> at src/views/rfq/RFQSales.vue
<FadeTransition>
<App> at src/App.vue
<Root>
base-switch component:
<template>
<label class="custom-toggle">
<input type="checkbox"
v-model="model"
v-bind="$attrs"
v-on="$listeners">
<span class="custom-toggle-slider rounded-circle"></span>
</label>
</template>
<script>
export default {
name: "base-switch",
inheritAttrs: false,
props: {
value: {
type: Boolean,
default: false,
description: "Switch value"
}
},
computed: {
model: {
get() {
return this.value;
},
set(value) {
this.$emit("base-switch", value);
}
}
}
};
</script>
<style>
</style>
Template:
<base-switch class="pull-right" v-model="modals.modalNewRFQ.data.borrowButton"
You haven't stated the version of Vue you use. Is it Vue2 or Vue3?
Your tag is also not fully copy/pasted. I have changed it to
<base-switch class="pull-right" v-model="borrowButton" #base-switch="process($event)"></base-switch>
Now your code works perfectly with Vue3.
Here is the link to the working playground:
https://stackblitz.com/edit/vue-f3nqz1?file=src/App.vue
For Vue3 you must define the emitted events, to make it work well.
emits: ['base-switch'],
Also, pay attention to passing value:
this.$emit("base-switch", value);
and
#base-switch="process($event)">
If your function gets an Even Object, then you can try JSON.stringify() it to check the properties. Possibly, it is related to your $listeners solution. I couldn't guess it to reproduce the problem.
Here is the code:
App.vue
<template>
<div id="app">
<base-switch class="pull-right" v-model="borrowButton" #base-switch="process($event)"></base-switch> <br />
Value: {{value}}
</div>
</template>
<script>
import BaseSwitch from './components/BaseSwitch.vue'
export default {
name: 'App',
components: {
BaseSwitch
},
data() {
return {
borrowButton: false,
value: null
}
},
methods: {
process(value) {
this.value = value;
}
}
}
</script>
BaseSwitch.vue
<template>
<label class="custom-toggle">
<input type="checkbox"
v-model="model"
v-bind="$attrs"
>
<span class="custom-toggle-slider rounded-circle"></span>
</label>
</template>
<script>
export default {
name: "base-switch",
inheritAttrs: false,
emits: ['base-switch'],
props: {
value: {
type: Boolean,
default: false,
description: "Switch value"
}
},
computed: {
model: {
get() {
return this.value;
},
set(value) {
this.$emit("base-switch", value);
}
}
}
};
</script>
<style>
</style>
I want to create a formRow component that have a slot for the input field and vee-validate for validation
this is my markup
//form
<vFormRow validate="required" v-slot="{ onInput, errors }">
<vTextfield #input="onInput" :errors="errors"/>
</vFormRow>
I tried to emit the value to the parent and get the value with #input="onInput" on the slot, but this doesn't work
//formrow
<template>
<div class="mb-4">
<v-validation-provider
v-slot="{ errors }"
:rules="validate"
>
<label>{{ label }}</label>
<slot
:onInput="onInput"
:errors="errors"
/>
<vFormError
:message="errors[0]"
/>
</v-validation-provider>
</div>
</template>
<script>
import { ValidationProvider as vValidationProvider } from 'vee-validate'
import vRadioButton from '#primitives/textfield'
import vFormError from '#primitives/formError'
export default {
components: {
vTextfield,
vFormError,
vValidationProvider
},
props: {
value: {
type: String,
default: '',
},
validate: {
type: String,
required: true,
}
},
methods: {
onInput(value) {
console.log(value)
}
}
}
</script>
//textfield
<template>
<div>
<input :value="value" #input="onInput"/>
</div>
</template>
<script>
export default {
props: {
value: {
type: String,
default: '',
},
errors: {
type: Array,
default: () => {}
}
},
data: {
return {
// local_value: ''
}
}
methods: {
onInput(e) {
// this.local_value = e.target.value
this.$emit('input', e.target.value)
}
}
}
</script>
What I'm doing wrong?
You're not actually passing the slot props to <vTextfield>. I think you might be assuming that data bindings and event handlers on the <slot> are automatically applied to the slot's children, but that's not true.
To use the value slot prop, destructure it from v-slot, and bind it to the <vTextfield> child:
<vFormRow validate="required" v-slot="{ value }">
<vTextfield :value="value" />
</vFormRow>
You could also pass the onInput method as a slot prop:
// vFormRow.vue
<slot :onInput="onInput" />
Then bind the event handler to <vTextfield>:
<vFormRow validate="required" v-slot="{ onInput }">
<vTextfield #input="onInput" />
</vFormRow>
What's the problem
I wanted to assign a local component variable to prop. I constantly get Vue alert Invalid watch handler specified by key "undefined". Maybe the case is that the prop is passed from another component, where I use v-model, but I don't really know. I would really appreciate your help, because my small exercise project really depends on this mechanic.
Parent Component
Here I have some HTML select, this is where I actually model my state.selectedPhysicsModule
<template>
<div>
<div>
<h1>Some header</h1>
</div>
<form class="choosePhysicsModule">
<label for="selectedPhysicsModule"></label>
<select class="select_Module" id="selectedPhysicsModule" v-model="state.selectedPhysicsModule">
<option :value="option.value" v-for="(option, index) in importedListToSelect" :key="index">
{{option.name}}
</option>
</select>
</form>
<list_of_exercises v-if="state.selectedPhysicsModule" :what_exercises="state.selectedPhysicsModule"/>
</div>
</template>
<script>
export default {
name: 'ChoosePhysicsModule',
components: {list_of_exercises},
setup() {
const state = reactive({
selectedPhysicsModule: null,
})
return {
state,
importedListToSelect
}
}
}
Child Component
</script>
export default {
name: "list_of_exercises",
props: {
whatExercises: {
type: String,
required: true
}
},
data() {
return {
exercises: this.what_exercises,
}
},
watch: {
whatExercises: function () {
this.exercises = this.whatExercises
}
}
In the parent component where you are passing the prop you need to add a setter for the prop passed. Here is an example:
<template>
<div id="app">
<label>
<input name="whatExercises" v-model="whatExercises">
</label>
<ListOfExercises v-if="whatExercises" :what_exercises="whatExercises" />
</div>
</template>
<script>
export default {
data() {
return {
whatExercises: null,
}
}
}
</script>
P.S: as a side note, I recommend using camelCase for prop names. It's more in-line with the rest of the community. If you have time feel free to check out the style guide on the official website.
I'm working on a VueJS project and have parent/child components like this.
Parent.vue
<template>
<ChildA v-bind="this.postData.someDataA" />
...
<ChildZ v-bind="this.postData.someDataZ"/>
<button #click="save">Save</button>
</template>
<script>
import ChildA from './ChildA';
...
import ChildZ from '.ChildZ';
data() {
return {
postData: {
someDataA: {field1: 'initialValue'}
someDataB: { // no initial value'}
...
},
},
methods: {
save() {this.$root.db.save(this.postData)}
}
</script>
Child.vue
<template>
<input type="text" v-model="field1" />
...
<input type="text" v-model="field10" />
</template>
<script>
props: {
field1:{type: String, default: 'default if not set by parent'},
...
}
</script>
As you can see, I want to pass this.postData from Parent.vue to a function which saves it to a DB. However, the values for someDataA, etc.. come from the Child.vue.
When I run my code like this, I get the Vue warning:
Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders.
Now, my question is, what's the best practice to deal with this kind of situation? Do I have to implement a <ChildA #change="setSomeDataA()" /> to each of the child elements, and $emit an event each time a value of the childs props change?
Actually it's better to make computed properties instead of using copy of value prop.
props: {
value: {
type: Object,
default: () => ({})
}
},
computed: {
field1: {
get() { return this.value.field1 },
set(field1) { this.$emit('input', {...this.value, field1 })}
}
}
and use it like that
<input type="text" v-model="field1" />
Set up your child to use v-model and then emit the change. For example:
The parent calls the child using v-model
Parent.vue
<template>
<ChildA v-model="this.postData.someDataA" />
...
<ChildZ v-model="this.postData.someDataZ"/>
<button #click="save">Save</button>
</template>
<script>
import ChildA from './ChildA';
...
import ChildZ from '.ChildZ';
data() {
return {
postData: {
someDataA: {field1: 'initialValue'}
someDataB: { // no initial value'}
...
},
},
methods: {
save() {this.$root.db.save(this.postData)}
}
</script>
The child takes the prop value and sets an internal variable to value. Your input uses the internal variable as it's v-model. Then using #change or a watcher to emit('input', <your internal variable>)
Child.vue
<template>
<input type="text" #change="objChanged" v-model="myObj.field1" />
...
<input type="text" #change="objChanged" v-model="myObj.field10" />
</template>
<script>
props: {
value:{type: String, default: 'wont need a default'},
...
},
data: {
myObj: {}
},
methods: {
objChanged(){
this.$emit('input', this.myObj);
}
},
created(){
this.myObj = this.value;
}
</script>
Using Vue Multiselect, I am trying to send a console.log once I have made a selection. I thought it would work by putting it in the watch but it does not work. Where should it be placed. Please see my component below.
Component
<template>
<div>
<label v-for="topic in topics" class="radio-inline radio-thumbnail" style="background-image: url('http://s3.hubsrv.com/trendsideas.com/profiles/74046767539/photo/3941785781469144249_690x460.jpg')">
<input type="radio" v-model="internalValue" name="topics_radio" :id="topic.id" :value="topic.name">
<span class="white-color lg-text font-regular text-center text-capitalize">{{ topic.name }}</span>
</label>
</div>
</template>
<script>
export default {
props: ['value'],
data () {
return {
internalValue: this.value,
topics: []
}
},
mounted(){
axios.get('/vuetopics').then(response => this.topics = response.data);
},
watch: {
internalValue(v){
this.$emit('input', v);
console.log('topic has been chosen!!!');
}
}
}
</script>
It fires events, so you may catch them.
<multiselect ... #select="doSomething" ...>
Then add your method
...
methods: {
doSomething(selectedOption, id) {
console.log(selectedOption);
}
}
Make sure you implemented vue-multiselect correctly, I don't see the component in your code.