I have a custom input element my-input in MyInput.vue:
<input :value="value" #input="inputOccurred($event.target.value)">
In my inputOccurred method, I am emitting oninputoccurred custom event and also passing the value:
inputOccurred: function(value){
this.$emit('oninputoccurred', value);
}
But how can I receive the passed value from the parent component? And where does the second argument (value) go from this.$emit()?
<my-input #oninputoccurred="printValue(<!-- How do I get the value here -->)"></my-input>
Here is a basic example of sending a value to a component :in then internally setting the components model this.value, then upon #input send the current value back to the parent via emitting it.
Vue.component('myInput', {
template: '#input-template',
props: ['in'],
data () {
return {
value: this.in
}
},
methods: {
inputOccurred (e) {
// set the model
this.value = e.target.value
this.$emit('on-out', this.value.split("").reverse().join(""))
}
}
});
//
var vm = new Vue({
el: '#app',
data () {
return {
value: 'Sent from parent, reverse by typing a value'
}
},
methods: {
setValue (value) {
this.value = value
}
}
});
<script src="https://cdn.jsdelivr.net/vue/1.0.16/vue.js"></script>
<div id="app">
<my-input :in="value" #on-out="setValue"></my-input>
{{ value }}
</div>
<template id="input-template">
<input :value="value" #input="inputOccurred">
</template>
Related
I can normally select all the text with $event.target.select() but in this case I think it is selecting all and then replacing the selection with the computed property. How do you select all after the computed property is finished?
Vue.component('my-component', {
template: `
<div>
My Component
<input type="text" v-model="displayValue" #blur='isInputActive = false' #focus='isInputActive = true;$event.target.select()'></input>
</div>
`,
props:['value'],
data() {
return {
isInputActive: false
};
},
computed: {
displayValue: {
get: function() {
return (this.isInputActive) ? this.value : this.value.toUpperCase();
},
set: function(val) {
this.$emit('input', val);
},
}
},
})
new Vue({
el: '#app',
data() {
return {
test: "Test"
};
},
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<my-component v-model="test"></my-component>
</div>
You can use the $nextTick which run the callback after the computed property is finished.
#focus='isInputActive = true; $nextTick(() => $event.target.select())'
I'm a newbie of Vue, and I'm trying to simply clear the data of input component once I've submitted, but it seems I'm missing something, because since it's parent data is cleared, I still see the filled value of the input component.
Here is a living example.
I've set to the input child component v-model="title" from it's parent wrapper. Once I submit the data to the parent, I call addItem and in the end, I supposed to clear the data model just by clear it this.title = '', but I probably do something wrong on how to bind data from parent to child.
And above the code, starting from the parent component:
<template>
<form #submit="addItem" class="todo-insert">
<input-item
icon="create"
name="title"
placeholder="Add a ToVue item..."
v-model="title"
/>
<button-item tone="confirm" class="todo-insert__action">
Aggiungi
</button-item>
</form>
</template>
<script>
import ButtonItem from '#vue/Form/ButtonItem/ButtonItem.vue'
import InputItem from '#vue/Form/InputItem/InputItem.vue'
import uuid from 'uuid'
export default {
name: 'TodoInsert',
components: {
ButtonItem,
InputItem
},
data () {
return {
title: ''
}
},
methods: {
addItem (e) {
e.preventDefault()
const todo = {
id: uuid.v4(),
isComplete: false,
title: this.title
}
this.$emit('add-todo', todo)
this.title = ''
}
}
}
</script>
<style lang="scss" scoped src="./TodoList.scss"></style>
This is the child input component:
<template lang="html">
<label class="input">
<div v-if="label" class="input__label text-sans text-sans--label">
{{ label }}
</div>
<div class="input__row">
<input
:autocomplete="autocomplete"
:class="[hasPlaceholderLabel, isDirty]"
:name="name"
:placeholder="placeholder"
class="input__field"
type="text"
v-on:input="updateValue($event.target.value)"
v-on:blur="updateValue($event.target.value)"
>
<div v-if="placeholderLabel" class="input__placeholder text-sans text-sans--placeholder">
{{ placeholderLabel }}
</div>
<div v-if="icon" class="input__icon-area">
<icon-item
:name="icon"
/>
</div>
</div>
</label>
</template>
<script>
import IconItem from '../../IconItem/IconItem.vue'
export default {
name: 'InputItem',
props: {
autocomplete: {
type: String,
default: 'off'
},
icon: String,
label: String,
name: {
type: String,
default: 'input-text'
},
placeholder: String,
placeholderLabel: String
},
computed: {
hasPlaceholderLabel () {
return this.placeholderLabel ? 'input__field--placeholder-label' : ''
},
isDirty () {
// return this.$attrs.value ? 'input__field--dirty' : ''
return 'input__field--dirty'
}
},
methods: {
updateValue: function (value) {
this.$emit('input', value)
}
},
components: {
IconItem
}
}
</script>
<style lang="scss" src="./InputItem.scss"></style>
What am I missing?
Your child component is bound unidirectionally. It means that it can change the value, but does not receive any update from the parent component. To receive updates, you need to receive the property value in your child:
props: {
value: String
}
Then, you need to pass the value received to the input :
<input
:value="value"
:autocomplete="autocomplete"
:class="[hasPlaceholderLabel, isDirty]"
:name="name"
:placeholder="placeholder"
class="input__field"
type="text"
v-on:input="updateValue($event.target.value)"
v-on:blur="updateValue($event.target.value)"
>
Now the input should update when the parent component changes the value
I have a Vue.js text-input component like the following:
<template>
<input
type="text"
:id="name"
:name="name"
v-model="inputValue"
>
</template>
<script>
export default {
props: ['name', 'value'],
data: function () {
return {
inputValue: this.value
};
},
watch: {
inputValue: function () {
eventBus.$emit('inputChanged', {
type: 'text',
name: this.name,
value: this.inputValue
});
}
}
};
</script>
And I am using that text-input in another component as follows:
<ul>
<li v-for="row in rows" :key="row.id">
<text-input :name="row.name" :value="row.value">
</text-input>
</li>
</ul>
Then, within the JS of the component using text-input, I have code like the following for removing li rows:
this.rows = this.rows.filter((row, i) => i !== idx);
The filter method is properly removing the row that has an index of idx from the rows array, and in the parent component, I can confirm that the row is indeed gone, however, if I have, for example, two rows, the first with a value of 1 and the second with a value of 2, and then I delete the first row, even though the remaining row has a value of 2, I am still seeing 1 in the text input.
Why? I don't understand why Vue.js is not updating the value of the text input, even though the value of value is clearly changing from 1 to 2, and I can confirm that in the parent component.
Maybe I'm just not understanding how Vue.js and v-model work, but it seems like the value of the text input should update. Any advice/explanation would be greatly appreciated. Thank you.
You cannot mutate values between components like that.
Here is a sample snippet on how to properly pass values back and forth. You will need to use computed setter/getter. Added a button to change the value and reflect it back to the instance. It works for both directions.
<template>
<div>
<input type="text" :id="name" v-model="inputValue" />
<button #click="inputValue='value2'">click</button>
</div>
</template>
<script>
export default {
props: ['name', 'value'],
computed: {
inputValue: {
get() {
return this.value;
},
set(val) {
this.$emit('updated', val);
}
}
}
}
</script>
Notice that the "#updated" event updates back the local variable with the updated value:
<text-input :name="row.name" :value="row.value" #updated="item=>row.value=item"></text-input>
From your code you are trying to listen to changes.. in v-model data..
// Your Vue components
<template>
<input
type="text"
:id="name"
:name="name"
v-model="inputValue"
>
</template>
<script>
export default {
props: ['name', 'value'],
data: function () {
return {
inputValue: ""
};
},
};
</script>
If You really want to listen for changes..
<ul>
<li v-for="row in rows" :key="row.id">
<text-input #keyup="_keyUp" :name="row.name" :value="row.value">
</text-input>
</li>
</ul>
in your component file
<template>...</template>
<script>
export default {
props: ['name', 'value'],
data: function () {
return {
inputValue: ""
};
},
methods : {
_keyUp : () => {// handle events here}
};
</script>
check here for events on input here
To bind value from props..
get the props value, then assign it to 'inputValue' variable
it will reflect in tthe input element
I'm making a component which is a wrapper around a checkbox (I've done similar with inputs of type 'text' and 'number') but I cannot get my passed in value to bind correctly.
My component is:
<template>
<div class="field">
<label :for="name" class="label">
{{ label }}
</label>
<div class="control">
<input :id="name" :name="name" type="checkbox" class="control" :checked="value" v-on="listeners" />
</div>
<p v-show="this.hasErrors" class="help has-text-danger">
<ul>
<li v-for="error in errors" :key="error">{{ error }}</li>
</ul>
</p>
</div>
</template>
<script>
export default {
name: 'check-edit',
props: {
value: {
type: Boolean,
default: false
},
label: {
type: String,
default: ''
},
name: {
type: String,
default: ''
},
errors: {
type: Array,
default: () => []
}
},
mounted () {
},
computed: {
listeners () {
return {
// Pass all component listeners directly to input
...this.$listeners,
// Override input listener to work with v-model
input: event => this.$emit('input', event.target.value)
}
},
hasErrors () {
return this.errors.length > 0
}
},
}
</script>
I've imported it globally; and am invoking it in another view by doing:
<check-edit name="ShowInCalendar" v-model="model.ShowInCalendar" label="Show in calendar?" :errors="this.errors.ShowInCalendar"></check-edit>
My model is in data and the property ShowInCalendar is a boolean and in my test case is true. So when I view the page the box is checked. Using the Vue tools in firefox I can see the model.ShowInCalendar is true, and the box is checked. However, when I click it the box remains checked and the value of ShowInCalendar changes to 'on', then changes thereafter do not change the value of ShowInCalendar.
I found this example here: https://jsfiddle.net/robertkern/oovb8ym7/ and have tried to implement a local data property for it but the result is not working.
The crux of what I'm trying to do is have the initial checkstate of the checkbox be that of ShowInCalendar (or whatever property is bound via v-model on the component) and then have that property be update (to be true or false) when the checkbox is checked.
Can anyone offer me any advice please?
Thank you.
You should not $emit event.target.value, it's the value of the checkbox, it's not a Boolean value. If you want to detect the checkbox is update(to be true or false) or not, You should $emit event.target.checked just like fstep said.
If v-on is the only listener that will be used it might be easier to use v-model as in the checkbox example from the Vue input docs.
However you can use listeners based on Binding-Native-Events-to-Components docs
<template>
<div class="field">
<label :for="name" class="label">
{{ label }}
</label>
<div class="control">
<input :id="name" :name="name" type="checkbox" class="control" checked="value" v-on="listeners" />
</div>
<p v-show="this.hasErrors" class="help has-text-danger">
<ul>
<li v-for="error in errors" :key="error">{{ error }}</li>
</ul>
</p>
</div>
</template>
<script>
export default {
name: 'check-edit',
props: {
value: {
type: Boolean,
default: false
},
label: {
type: String,
default: ''
},
name: {
type: String,
default: ''
},
errors: {
type: Array,
default: () => []
}
},
mounted() {},
computed: {
listeners() {
var vm = this;
// `Object.assign` merges objects together to form a new object
return Object.assign(
{},
// We add all the listeners from the parent
this.$listeners,
// Then we can add custom listeners or override the
// behavior of some listeners.
{
// This ensures that the component works with v-model
input: function(event) {
vm.$emit('input', event.target.checked);
}
}
);
},
hasErrors() {
return this.errors.length > 0;
}
}
};
</script>
Don't change props. Your component, having a v-model, should be emitting input events on change. The parent will handle the actual changing of the value.
Is there a way to simplify this code?
The button should also change the localValue of the child.
Vue.component('my-input', {
template: `
<div>
<b>My Input:</b> <br>
localValue: {{ localValue }} <br>
<input v-model="localValue">
</div>
`,
props: ['value'],
data() {
return { localValue: this.value }
},
watch: {
value () {
this.localValue = this.value
},
localValue () {
this.$emit('input', this.localValue)
}
}
})
new Vue({
el: '#app',
data: () => ({
parentValue: 'Inital value'
}),
methods: {
change () {
this.parentValue = 'Changed value'
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.3/vue.min.js"></script>
<div id="app">
<my-input v-model="parentValue"></my-input>
<button #click="change">Change</button><br>
parentValue: {{ parentValue }}
</div>
I have always faced difficulties when I need to do so.
I will be very grateful for the help!
If you avoid using v-model inside your custom form component, you really only need
<b>My Input:</b> <br>
localValue: {{ value }} <br>
<input :value="value" #input="$emit('input', $event.target.value)">
No data, no watch, that's it.
See https://v2.vuejs.org/v2/guide/components.html#Using-v-model-on-Components
If you really want something representing a value local to your component, the Vue docs favour using computed values over watchers (ref: https://v2.vuejs.org/v2/guide/computed.html#Watchers).
The idea here is to create a computed value with getter and setter to facilitate a simplified one-way data flow.
Vue.component('my-input', {
template: `<div><b>My Input:</b> <br>localValue: {{ localValue }} <br><input v-model="localValue"></div>`,
props: ['value'],
computed: {
localValue: {
get () {
return this.value
},
set (value) {
this.$emit('input', value)
}
}
}
})
new Vue({
el: '#app',
data: () => ({
parentValue: 'Inital value'
}),
methods: {
change () {
this.parentValue = 'Changed value'
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.3/vue.min.js"></script>
<div id="app">
<my-input v-model="parentValue"></my-input>
<button #click="change">Change</button><br>
parentValue: {{ parentValue }}
</div>
How to pass complex objects to child component (potentially down a few layers):
Parent component:
<child v-model='parentEntity' />
Child component:
model: {
prop: 'modelValue',
event: 'update:modelValue',
},
props: {
modelValue: {
type: Object,
required: true,
},
},
...
entity: {
// getter
get() {
return Object.assign({}, this.modelValue);
},
// setter
set(newValue) {
this.$emit('update:modelValue', newValue);
},
},
...