How to binding parent's model to child in Vue.js?
These codes below is works fine. if i fill the input manually, then child's model return it's value to the parent's model.
But the issue is, if the data set from AJAX request in a parent, the input doesn't automatically filled.
Can anyone help me on this?
Form.vue
<template>
<form-input v-model="o.name" :fieldModel="o.name" #listenChanges="o.name = $event"/>
<form-input v-model="o.address" :fieldModel="o.address" #listenChanges="o.address = $event"/>
</template>
<script>
import FormInput from '../share/FormInput.vue'
export default {
data () {
return {
o: {
name: '',
address: ''
}
}
},
components: { 'form-input': FormInput },
created: function() {
axios.get('http://api.example.com')
.then(response => {
this.o.name = response.data.name
this.o.address = response.data.address
})
.catch(e => { console.log(e) })
}
}
</script>
FormInput.vue
<template>
<input type="text" v-model='fieldModelValue' #input="forceUpper($event, fieldModel)">
</template>
<script>
export default {
props: ['fieldModel'],
data() {
return {
fieldModelValue: ''
}
},
mounted: function() {
this.fieldModelValue = this.fieldModel;
},
methods: {
forceUpper(e, m) {
const start = e.target.selectionStart;
e.target.value = e.target.value.toUpperCase();
this.fieldModelValue = e.target.value.toUpperCase();
this.$emit('listenChanges', this.fieldModelValue)
}
}
}
</script>
Things are more straightforward if you take advantage of v-model in components.
If you put v-model on a component, the component should take a prop named value, and should emit input events to trigger it to update.
I like to make a computed to hide the event emitting, and allow me to just v-model the computed inside my component.
new Vue({
el: '#app',
data: {
o: {
name: '',
address: ''
}
},
components: {
'form-input': {
template: '#form-input',
props: ['value'],
computed: {
fieldModelValue: {
get() {
return this.value;
},
set(newValue) {
this.$emit('input', newValue.toUpperCase());
}
}
}
}
},
// Simulate axios call
created: function() {
setTimeout(() => {
this.o.name = 'the name';
this.o.address = 'and address';
}, 500);
}
});
<script src="//unpkg.com/vue#latest/dist/vue.js"></script>
<div id="app">
Name ({{o.name}})
<form-input v-model="o.name"></form-input>
Address ({{o.address}})
<form-input v-model="o.address"></form-input>
</div>
<template id="form-input">
<input type="text" v-model='fieldModelValue'>
</template>
The mounted() hook is blocking subsequent updates from the parent.
Remove mounted and change v-model to 'fieldModel'
<template>
<input type="text" :value='fieldModel' #input="forceUpper($event, fieldModel)">
</template>
<script>
export default {
props: ['fieldModel'],
data() {
return {
fieldModelValue: ''
}
},
// mounted: function() {
// this.fieldModelValue = this.fieldModel;
// },
methods: {
forceUpper(e, m) {
const start = e.target.selectionStart;
e.target.value = e.target.value.toUpperCase();
this.fieldModelValue = e.target.value.toUpperCase();
this.$emit('listenChanges', this.fieldModelValue)
}
}
}
</script>
Demo CodeSandbox
Related
I want to edit my templates via Quill. I'm using bubble theme. So here is my component;
<template>
<QuillEditor #input="onInputChange" theme="bubble" toolbar="essential" id="quill-Editor" #ready="ready"></QuillEditor>
</template>
<script>
import { QuillEditor } from '#vueup/vue-quill'
import '#vueup/vue-quill/dist/vue-quill.bubble.css';
export default {
props: ['value'],
components: {
QuillEditor
},
created(){
console.log();
setTimeout(() => {
document.querySelector('#quill-Editor .ql-editor').innerHTML = this.value
}, 100);
},
methods: {
onInputChange() {
this.$emit('input', document.querySelector('#quill-Editor .ql-editor').innerHTML);
},
ready(editor){
editor.getModule('toolbar').addHandler('image', () => {
const tooltip = editor.theme.tooltip;
const originalSave = tooltip.save;
const originalHide = tooltip.hide;
tooltip.save = function () {
const range = editor.getSelection(true);
const value = this.textbox.value;
if (value) {
editor.insertEmbed(range.index, 'image', value, 'user');
}
};
tooltip.hide = function () {
tooltip.save = originalSave;
tooltip.hide = originalHide;
tooltip.hide();
};
tooltip.edit('image');
tooltip.textbox.placeholder = 'Embed URL';
});
},
}
}
</script>
Value is this html value. But when I do that quill is deleting all tags giving just few blank <p> tag. What should I do? How can I do that?
I have an input in a child component and when the user starts typing in the input it updates the data in the parent component, or should. This is my code, I can post other parts if useful.
Child
<input
:keyup="updateFilters(filters[data.field.key])"
:placeholder="data.label"
/>
methods: {
updateFilters(value) {
this.$emit("input", value);
}
}
Parent
data() {
return {
filters: {
name: "",
age: "",
address: "",
},
};
},
You can change the parent from child component the same way you do emiting other events like onchange, onkeyup, onkeydown etc.
Vue.component('parent', {
data() {
return {
parentValue: ''
};
},
template: `
<div>
<label>Parent state value:</label>
{{parentValue}}
<br/><br/>
<label>input is the child component:</label>
<br/>
<child #fromChild="fromChild"></child>
</div>
`,
methods: {
fromChild(value) {
this.parentValue = value
console.log(value) // someValue
}
}
})
Vue.component('child', {
template: `
<input
v-on:keyup="updateParent($event.target.value)"
placeholder="type something"
/>
`,
methods: {
updateParent(value) {
console.log(value)
this.$emit("fromChild", value);
}
},
})
new Vue({
el: "#app",
data: {
label: 'in Vue'
},
methods: {
toggle: function(todo) {
todo.done = !todo.done
}
}
})
I've prepared a working example here.
I did a custom input component, it works correctly but there is a problem: when i try to update its value from a method the model is updated but the input value still there.
This is my component:
https://codepen.io/ken-ramirez/pen/JgyKad
const BasicInput = {
template: '<input v-model="content" #input="handleInput" />',
prop: ['value'],
data () {
return {
content: this.value
}
},
methods: {
handleInput (e) {
this.$emit('input', this.content)
}
}
}
new Vue({
el: '#app',
data: { name: '' },
components: { BasicInput },
methods: {
restart() {
this.name = ''
}
}
})
You can press on restart button to see what i mean.
You have a mistake in your code: props, not prop.
But this is not enough, also you need to update your content manually, it's not reactive with value prop. Everything declared inside data is not reactive with its initial values.
const BasicInput = {
template: '<input v-model="content" #input="handleInput" />',
props: ['value'],
data () {
return {
content: this.value
}
},
methods: {
handleInput (e) {
this.$emit('input', this.content)
}
},
watch: {
value(val) {
this.content = val;
}
}
}
Like #NikitaK mentioned in his answer you're making a typo , you should write props instead of prop, but i want to give a shorter solution without using watcher property only with this code #input="$emit('input',$event.target.value)"
Full example
const BasicInput = {
template: `<input :value="value" #input="$emit('input',$event.target.value)" />`,
props: ['value']
}
new Vue({
el: '#app',
data() {
return{
name: ''
}},
components: { BasicInput },
methods: {
restart() {
this.name = ''
}
}
})
body {
display: flex;
align-items: center;
justify-content: center;
height: 100vh;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.js"></script>
<div id="app">
<basic-input v-model="name"></basic-input>
<p>
<strong>Name:</strong> {{ name }}
</p>
<button #click="restart">restart</button>
</div>
I'm trying to change the state of my Vuex Store with the mutations, and I also have routes in my application. In my default component (the first route) has a button that changes my user.name state, and I have a second component (the second route) that show to me the user.name. In my first component, when I call the mutation to change the user.name state it shows to me the new user.name, but this doesn't become different of the first state.
For example: In my first user.name state I have "John Dave" and when I click on the button that calls the mutation CHANGE_USER(), this has to change to "David Lionel", but don't work.
Store
export default new Vuex.Store({
state: {
user: {
name: 'John Dave',
},
},
mutations: {
changeUser (state, payload) {
state.user = payload;
}
},
getters: {
getName(state) {
return state.user;
}
}
});
First component
<template>
<div>
{{user}}
</div>
</template>
<script>
export default {
computed: {
user() {
const { name } = this.$store.getters.getName;
return name;
},
},
};
</script>
Second component
<template>
<div>
{{user}}
<button #click="changeUser() ">
Change user
</button>
</div>
</template>
<script>
export default {
computed: {
user() {
const { name } = this.$store.state.user;
return `My user is ${name}`
},
},
methods: {
changeUser() {
const payload = {
name: 'Name changed',
email: 'vitor#v.cm',
level: 'ADM',
};
this.$store.commit('changeUser', payload);
},
}
};
</script>
I'm currently doing the below:
<script>
export default {
computed: {
editingItem: {
get() {
return this.$store.getters['editing/editingItem'];
},
set(newValue) {
this.$store.commit('editing/UPDATE_EDITING', newValue);
}
},
editingItemName: {
get() {
return this.editingItem.name;
},
set(newValue) {
this.editingItem.name = newValue;
this.editingItem = this.editingItem;
}
}
},
}
</script>
Am I over complicating it? The second line on the editingItemName set(), is a workaround to make the editingItem set() function trigger.
Check this article. it's about forms, but it shows the way to achieve to 2-way binding with vuex.
regarding your special case, see the code. telephone is a nested property inside an object.
myModule.js
const myModule = {
state: {
customerInfo: {
name: '',
telephone: ''
}
},
getters: {
getTelephone(state) {
return state.customerInfo.telephone
}
},
mutations: {
setTelephone(state, payload) {
state.customerInfo.telephone += payload
},
}
}
export default myModule;
form.vue
<template>
<div>
<input v-model="telephone"></input>
</div>
</template>
<script>
export default {
computed: {
telephone: {
get() {
return this.$store.getters['getTelephone']
},
set(value) {
this.$store.commit('setTelephone', value)
}
},
}
}
</script>