Using v-model and still loading initial value if available - vue.js

I have a child component where I am passing in customerData, if available as a prop. In that component, I have initial data as customer objects and add fields on them. However, if customerData is available and has that key in the object, I'd want to show that as the default value. How could I do that while still using v-model?
<child-component :customer-data="customerData" > </child-component>
Child
data: {
customer: {}
},
props: {
customerData: {type: Object}
}
<div>
<input v-model="customer.name" />
<input v-model="customer.age" />
<input v-model="customer.address" />
</div>

You can update customer data of the childcomponent by the values sent in the prop:
const childcomponent = Vue.component('childcomponent', {
template: '#childcomponent',
data: () => ({ customer: {} }),
props: { customer_data: {type: Object} },
created() {
this.customer = { ...this.customer, ...this.customer_data };
}
});
new Vue({
el:"#app",
data: () => ({ customer_data: { name:'name', address:'address' } }),
components: { childcomponent }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<template id="childcomponent">
<div>
<input v-model="customer.name" />
<input v-model="customer.age" />
<input v-model="customer.address" />
</div>
</template>
<div id="app">
<childcomponent :customer_data="customer_data" />
</div>

Related

Vue component prop not updating on $emit

I need to update the component's prop on every route change but it stays with the last info given.
For example, I fill the form in RouteTwo, with id, name, lastname and phone, and if I change to RouteOne, ComponentOne stays with those four values (including phone) until I start filling the form in RouteOne.
I'm working with vue 2.6.12, vue-router 3.4.9
Here's an example code:
General.vue
<template>
<div>
<div>
<router-view #data-updated="updateFunction"></router-view>
</div>
<div>
<component-one v-bind:component-prop="reactiveProp" />
</div>
</div>
</template>
<script>
import ComponentOne from './ComponentOne.vue';
export default {
components: {
ComponentOne,
},
data: () => ({
reactiveProp
}),
methods: {
updateFunction(value) {
this.reactiveProp = value;
},
}
}
</script>
ComponentOne.vue
<template>
<div>
<p>{{ componentProp.id }}</p>
<p>{{ componentProp.name }}</p>
<p>{{ componentProp.lastname }}</p>
<p v-if="routePath == 'routetwo'">{{ componentProp.phone }}</p>
</div>
</template>
<script>
export default {
props: {
componentProp: Object,
},
data: () => ({
routePath: ''
}),
mounted() {
this.routePath = this.$route.path.split('/').at(-1);
},
}
</script>
RouteOne.vue
<template>
<div>
<input type="text" v-model="dataObject.id" />
<input type="text" v-model="dataObject.name" />
<input type="text" v-model="dataObject.lastname" />
</div>
</template>
<script>
export default {
data: () => ({
dataObject: {
id: '',
name: '',
lastname: '',
},
}),
methods: {
// some logic methods
},
watch: {
dataObject: {
handler: function() {
this.$emit('data-updated',this.dataObject);
},
deep: true,
}
},
}
</scipt>
RouteTwo.vue
<template>
<div>
<input type="text" v-model="dataObject.id" />
<input type="text" v-model="dataObject.name" />
<input type="text" v-model="dataObject.lastname" />
<input type="text" v-model="dataObject.phone" />
</div>
</template>
<script>
export default {
data: () => ({
dataObject: {
id: '',
name: '',
lastname: '',
phone: '',
},
}),
methods: {
// some logic methods
},
watch: {
dataObject: {
handler: function() {
this.$emit('data-updated',this.dataObject);
},
deep: true,
}
},
}
</scipt>
Router
{
name: 'general',
path: '/general',
component: () => import('General.vue'),
children: [
{
name: 'route-one',
path: 'routeone',
component: () => import('RouteOne.vue')
},
{
name: 'route-two',
path: 'routetwo',
component: () => import('RouteTwo.vue')
}
]
}
Switching routes doesn't cause a change in dataObject, so no emit will fire. Technically, switching routes causes one route component to be unmounted (destroyed), and the next route component to be created, meaning dataObject is created/recreated every time you switch back and forth, which is fundamentally different from dataObject being "changed" (which activates the watcher).
Instead, put a watcher on the route itself in the General component and reset reactiveProp when that watcher activates. This will clear the fields when going between RouteOne and RouteTwo:
watch: {
$route(to, from) {
this.reactiveProp = {};
},
},

how to send click button's value to a props

In my parent component I have a button and child component:
<div> #click="editMe(edit)" />
<edit-team :openModal="openModal"/>
</div>
export default {
data() {
return {
openModal: false,
};
},
method: {
editMe(edit) {
this.openModal = true;
},
}
}
So after I click the editMe button, I am expecting openModal becomes true and goes true to child component.
<template>
<el-dialog
:visible.sync="modal"
/>
</template>
<script>
export default {
props: {
openModal: {
type: Boolean,
required: true,
},
},
data() {
return {
modal: this.openModal,
};
},
</script>
But unfortunately, the modal is not opening because props comes as false always. I assigned openModal to new variable because it was giving me a warning about mutating props. So how do you think I can send the props in right value?
In child just try this
<template>
<el-dialog
:visible.sync="openModal"
/>
</template>
<script>
export default {
props: {
openModal: {
type: Boolean,
required: true,
},
},
},
</script>
If you have sibilngs components and you need to retrieve data, you can use the emit keyword and emit events
Then it will work like this :
The sibling emit the event to show the modal
The parent update the showModalData to true
This child is re-rendered
Vue.component('modal', {
template: '#modal',
props: ['show'],
});
Vue.component('buttonModal', {
template: '#buttonModal',
methods: {
showModal(){
this.$emit('show-modal-button', true)
}
}
});
new Vue({
el: '#app',
data: () => ({
showModalData: false
}),
methods: {
editMe() {
this.showModalData = true
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.13/vue.js"></script>
<div id="app">
<p>this is the parent</p>
<hr>
<button-modal #show-modal-button="editMe">Show modal</button-modal>
<hr>
<modal :show="showModalData" />
</div>
<template id="modal">
<div>
props show : {{ show }}
<h2 v-if="show">This is the modal</h2>
</div>
</template>
<template id="buttonModal">
<div>
<p>This is the sibilng</p>
<button #click="showModal"> Show the modal through sibiling components </button>
</div>
</template>

Vuex-ORM two-way-data binding cannot watch a nested object

this question is related to Two way data binding with Vuex-ORM
i tried using a watch with deep to handle a user form like this.
<template>
<div id="app">
<div style="display: inline-grid">
<label for="text-1">Text-1: </label>
<input name="text-1" type="text" v-model="user.name" />
<label for="text-2">Text-2: </label>
<input name="text-2" type="text" v-model="user.lastName" />
<label for="text-3">Text-3: </label>
<input name="text-3" type="text" v-model="user.birth" />
<label for="text-4">Text-4: </label>
<input name="text-4" type="text" v-model="user.hobby" />
</div>
<div>
<h5>Result</h5>
{{ userFromStore }}
</div>
</div>
</template>
<script>
import { mapGetters, mapMutations, mapActions } from "vuex";
export default {
name: "App",
computed: {
...mapGetters({
userFromStore: "getUserFromStore",
messageFromStore: "getMessage",
}),
user: function () {
return this.userFromStore ?? {}; // basically "User.find(this.userId)" inside store getters
},
},
watch: {
user: {
handler(value) {
console.log('called')
// this.updateUser(value);
},
deep: true,
},
},
methods: {
...mapActions({
fetchUser: "fetchUser",
}),
...mapMutations({
updateUser: "updateUser",
}),
},
created() {
this.fetchUser();
},
};
</script>
problem is my watcher is not watching, no matter what i try. as soon as the data came from Vuex-ORM my component is not able to watch on the getters user
Anyone idea why?
User.find(...) returns a model. The properties of that model are not reactive i.e. you cannot perform two-way data binding on items that are not being tracked. Hence your watcher will not trigger.
My advice would be to push your user data as props to a component that can handle the data programmatically.
Or, by way of example, you can simply handle two-way binding manually:
Vue.use(Vuex)
class User extends VuexORM.Model {
static entity = 'users'
static fields() {
return {
id: this.number(null),
name: this.string(''),
lastName: this.string(''),
birth: this.string(''),
hobby: this.string('')
}
}
}
const db = new VuexORM.Database()
db.register(User)
const store = new Vuex.Store({
plugins: [VuexORM.install(db)]
})
User.insert({
data: {
id: 1,
name: 'John',
lastName: 'Doe',
birth: '12/12/2012',
hobby: 'This, that, the other'
}
})
Vue.component('user-input', {
props: {
value: { type: String, required: true }
},
template: `<input type="text" :value="value" #input="$emit('input', $event.target.value)" placeholder="Enter text here...">`
})
new Vue({
el: '#app',
computed: {
user() {
return User.find(1)
}
},
methods: {
update(prop, value) {
this.user.$update({
[prop]: value
})
}
}
})
<script src="https://unpkg.com/vue#2.6.12/dist/vue.min.js"></script>
<script src="https://unpkg.com/vuex#3.6.2/dist/vuex.min.js"></script>
<script src="https://unpkg.com/#vuex-orm/core#0.36.4/dist/vuex-orm.global.prod.js"></script>
<div id="app">
<div v-if="user" style="display: inline-grid">
<label for="text-1">Name: </label>
<user-input
id="text-1"
:value="user.name"
#input="update('name', $event)"
></user-input>
<label for="text-2">Last name: </label>
<user-input
id="text-2"
:value="user.lastName"
#input="update('lastName', $event)"
></user-input>
<label for="text-3">D.O.B: </label>
<user-input
id="text-3"
:value="user.birth"
#input="update('birth', $event)"
></user-input>
<label for="text-4">Hobby: </label>
<user-input
id="text-4"
:value="user.hobby"
#input="update('hobby', $event)"
></user-input>
</div>
<pre>User in store: {{ user }}</pre>
</div>

v-model input working after pressing enter

I am working in Vue
My input search bar is filtering after every letter that I type. I want it to filter after I pressed the enter key.
Can somebody help me please?
<template>
<div id="show-blogs">
<h1>All Blog Articles</h1>
<input type="text" v-model="search" placeholder="Find Car" />
<div v-for="blog in filteredBlogs" :key="blog.id" class="single-blog">
<h2>{{blog.title | to-uppercase}}</h2>
<article>{{blog.body}}</article>
</div>
</div>
</template>
<script>
export default {
data() {
return {
blogs: "",
search: ""
};
},
methods: {},
created() {
this.$http
.get("https://jsonplaceholder.typicode.com/posts")
.then(function(data) {
// eslint-disable-next-line
console.log(data);
this.blogs = data.body.slice(0, 10);
});
},
computed: {
filteredBlogs: function() {
return this.blogs.filter(blog => {
return blog.title.match(this.search);
});
}
}
};
</script>
There are a few ways you could accomplish this. Probably the most accessible would be to wrap the input in a form and then user the submit event to track the value you want to search for. Here's an example:
<template>
<div id="show-blogs">
<h1>All Blog Articles</h1>
<form #submit.prevent="onSubmit">
<input v-model="search" type="text" placeholder="Find Car" />
</form>
</div>
</template>
export default {
data() {
return {
search: '',
blogSearch: '',
};
},
computed: {
filteredBlogs() {
return this.blogs.filter(blog => {
return blog.title.match(this.blogSearch);
});
},
},
methods: {
onSubmit() {
this.blogSearch = this.search;
},
},
};
Notice that blogSearch will only be set once the form has been submitted (e.g. enter pressed inside the input).
Other notes:
You'll probably want to trim your search value
You should add a label to your input.
You could skip using v-model and instead add a keyup event handler with the .enter modifier that sets the search data property
<input type="text" :value="search" placeholder="Find Car"
#keyup.enter="search = $event.target.value" />
Demo...
new Vue({
el: '#app',
data: () => ({ search: '' })
})
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.min.js"></script>
<div id="app">
<input type="text" :value="search" placeholder="Find Car"
#keyup.enter="search = $event.target.value" />
<pre>search = {{ search }}</pre>
</div>

how to enable v-model binding when building a custom components from other custom components

I am able to build a simple textbox component from <input /> and setup v-model binding correctly.
I'm trying to do same with a custom component: vs-input from vuesax.
Following the pattern below does not work as expected:
<template>
<div>
<vs-input type="text" v-model="value" #input="text_changed($event)" />
<!-- <input type="text" :value="value" #input="$emit('input', $event.target.value)" /> -->
</div>
</template>
<script>
export default {
name: 'TestField',
props: {
value: {
type: String,
default: ''
}
},
data() {
return {}
},
methods: {
text_changed(val) {
console.log(val)
// this.$emit('input', val)
}
}
}
</script>
In building custom components from other custom components is there anything particular we should look out for to get v-model binding working properly?
Following code might help you.(Sample code try it in codepen)
updating props inside a child component
//html
<script src="https://unpkg.com/vue"></script>
<div id="app">
<p>{{ message }}</p>
<input type="text" :value="test" #change="abc">
{{ test }}
</div>
//VUE CODE
new Vue({
el: '#app',
data: {
message: 'Hello Vue.js!',
},
props:{
test:{
type:String,
default:''
}
},
methods:{
abc:function(event){
//console.log("abc");
console.log(event.target.value);
this.test=event.target.value;
}
}
})
I prefer to interface props with computed:
<template>
<div>
<vs-input type="text" v-model="cValue" />
</div>
</template>
<script>
export default {
name: 'TestField',
props: {
value: {
type: String,
default: ''
}
},
data() {
return {}
},
computed: {
cValue: {
get: function(){
return this.value;
},
set: function(val){
// do w/e
this.$emit('input', val)
}
}
}
}
</script>
Computed Setter