Unable to put value using :value wiht v-model together in VueJS 2 - vue.js

In a page i am fetcing data from API and showing the data in the text field. After that user can change the data and submit the form. I am uanble to show data using :value in the inout field. Its showing conflicts with v-model on the same element because the latter already expands to a value binding internally
I tried to mount the data after it loads. But it is still not showing.
<input v-model="password" :value="credentials.password">
created() {
let txid = this.$route.params.id
this.$axios.get(this.$axios.defaults.baseURL+'/v1/purno/internal/users/'+ txid, {
}).then((response) => {
if (response.data.status == 'error') {
toast.$toast.error('Something Went Wrong at!', {
// override the global option
position: 'top'
})
} else if (response.data.status == 'success') {
if(response.data.data.found ==true) {
this.credentials = response.data.data;
})
}
}
})
});
},
data(){
return {
credentials:[],
password:null
}
},
mounted(){
this.password = this.credentials.password
}
How can i solve this problem? Thanks in advance

Please complete it within the request callback
this.credentials = response.data.data;
this.password = this.credentials.password;
like this in the then callback fn. Try it!

conflicts with v-model on the same element because the latter already expands to a value binding internally error message tells it all
v-model="password" is same as :value="password" #input="password = $event.target.value" (for text and textarea - see here)
So using it together with another :value makes no sense...

Related

Computed prop returning undefined

I have a prop in a component that I want to pass with eventHub to a sibling component where I want to display the prop. However I can't make it to work and it always returns undefined.
<div class="user-menu">
{{getUsername != undefined ? 'Logged in as ' + getUsername + '!' : 'Not logged in'}}
</div>
computed: {
getUsername(){
var getUser;
this.$eventHub.$on('current-user', username => {
getUser = username
})
return getUser;
}
}
A computed property is not the right tool for the job here. You just need a data property:
data () {
return {
getUsername: null
}
},
created () {
this.$eventHub.$on('current-user', username => {
this.getUsername = username
})
}
Depending on how the event hub is created you would likely also need to remove the event listener when the component is destroyed.

How do I go about creating an always capitalized input in vue.js?

I'm creating a form using vue.js and I need to create inputs in vue that is always capitalized. I know I could use the css property
text-transform: uppercase;
and then transform the data before sending using
data.someData.toUpperCase()
But I wonder if there is a more intelligent way of doing that in vue.js. In react we can create controlled inputs and easily do it. Is there anything like that in Vue.js?
I managed to do it using computed fields, however, I would have to create computed getter and setter for each input in the form. Is there a better way of doing it?
You could create a custom directive.
Vue.directive( 'touppercase', {
update (el) {
el.value = el.value.toUpperCase()
},
})
And then use it where you need. For example:
<input type="text" v-model="modelfield" v-touppercase>
Since you don't have a lot of code to run, you should manually bind events to your textfield and then handle the uppercasing there.
Handling events from a text field can be done by adding an input event handler on them, and then updating the initial state again.
<input :value="text" #input="updateText($event.target.value)"/>
export default {
data() {
return {
text: '',
}
},
methods: {
updateText(newValue) {
this.value = newValue.toUpperCase();
},
}
}
You can also do it inline in a template, but this might make it harder to read depending on your code style preferences
<input :value="text" #input="text = $event.target.value.toUpperCase()"/>
This directive works fine with v-model (last character is in upper case too):
Vue.directive('uppercase', {
update(el) {
const sourceValue = el.value;
const newValue = sourceValue.toUpperCase();
if (sourceValue !== newValue) {
el.value = newValue;
el.dispatchEvent(new Event('input', { bubbles: true }));
}
},
});
Usage:
<input type="text" v-model="myField" v-uppercase />
You can do this:
<input :value="theValue" #input="theValue = theValue.toUpperCase()"/>
as a fix for asologor's answer you should reach input element to change it at vuetify
Vue.directive("uppercase", {
update(el) {
const sourceValue = el.getElementsByTagName("input")[0].value
const newValue = sourceValue.toUpperCase()
if (sourceValue !== newValue) {
el.getElementsByTagName("input")[0].value = newValue
el = el.getElementsByTagName("input")[0]
el.dispatchEvent(new Event("input", { bubbles: true }))
}
}
})
and usage is
<v-text-field
type="text"
outlined
placeholder="placeholder"
v-model="name"
prepend-inner-icon="edit"
v-uppercase
/>
this is defnetely working

Vue.js dynamic binding v-model and value

I use django-rest-framework + vue.js
My goal is to make a Form to edit user-profile.
Here is what i have:
<input type="email" v-model="userEdit.email">
<input type="text" v-model="userEdit.location">
<input type="text" v-model="userEdit.bio">
my inputs are bounded to data object "editUser"
data() {
return {
'editUser': {
email: '',
location: '',
bio: '',
image: '',
},
}
},
so now i can send this object to the server and change user-profile information.
sendChanges() {
const fd = new FormData();
fd.append('image', this.editUser.image, this.editUser.image.name)
fd.append('email', this.editUser.email)
fd.append('location', this.editUser.location)
fd.append('bio', this.editUser.bio)
this.axios.put(userDetailURL + this.routeUser, fd)
.then(response => {
console.log(response)
})
.catch(error => {
console.log(error.response)
})
},
this form works and updates info, but there is a thing i dont like:
The input fields are always empty, and user needs to fill all of them before can press save button.
even if the user wants to change only "location" he must fill other inputs which are empty.
adding dynamic :value="userDetail.email" to the input -- no work.
is there any other way to add current value to input field and still have v-model?
current data is here:
computed: {
userDetail() {
return this.$store.getters.userDetail;
},
},
The problem is that you are binding the values in data to the form and those values are initially empty.
The cleanest and easiest solution I can think of right now is updating the initial data in mounted lifecycle hook:
mounted () {
// Use Object.clone to prevent modifying the userDetail object directly
this.editUser = Object.clone(this.$store.getters.userDetail)
}
There are other solutions, though. You could use a computed setter whose getter defaults to whatever is in the store but can be overridden when set.

Only null or Array instances can be bound to a multi-select

I'm building a multi-step form in Aurelia where each page shows one question.
I use the same view for every question, with if statements determining what type of form field to show.
When I try to bind my question data to a multiple select element however, Aurelia throws errors and says "Only null or Array instances can be bound to a multi-select.".
What's really strange is that if the first question is a multiple select I don't get the error until I come to a non-multiselect question and then go back to the multiselect question.
I can solve this entire problem by setting activationStrategy: 'replace' for this route, but I really don't want that.
The important code follows:
import {inject} from 'aurelia-framework';
import {Router} from 'aurelia-router';
#inject(Router)
export class Form {
constructor (router) {
this.router = router;
this.active = 0;
this.field = null;
this.fields = [
{
type: 'text',
value: null
},
{
type: 'select',
value: [],
options: [
'foo',
'bar'
]
},
{
type: 'select',
value: [],
options: [
'foo',
'bar'
]
},
{
type: 'text',
value: null
},
];
}
activate (routeParams) {
this.active = routeParams.fieldIndex || 0;
this.active = parseInt(this.active);
this.field = this.fields[this.active];
}
prev () {
if (typeof this.fields[this.active - 1] !== 'undefined') {
this.router.navigateToRoute('form', {
fieldIndex: this.active - 1
});
return true;
}
else {
return false;
}
}
next () {
if (typeof this.fields[this.active + 1] !== 'undefined') {
this.router.navigateToRoute('form', {
fieldIndex: this.active + 1
});
return true;
}
else {
return false;
}
}
}
And the template:
<template>
<div class="select" if.bind="field.type == 'select'">
<select value.bind="field.value" multiple="multiple">
<option repeat.for="option of field.options" value.bind="option">${option}</option>
</select>
</div>
<div class="text" if.bind="field.type == 'text'">
<input type="text" value.bind="field.value">
</div>
<a click.delegate="prev()">Previous</a> | <a click.delegate="next()">Next</a>
</template>
But you'll probably want to check out the GistRun: https://gist.run/?id=4d7a0842929dc4086153e29e03afbb7a to get a better understanding.
Try setting the first question to a multiselect and you'll notice the error disappears (until you go back to it). You can also try activationStrategy in app.js like mentioned above.
Why is this happening and how can I solve it?
Also note that in my real app I'm actually using compose instead of ifs but have tried with both and both produce the same error. It almost seems as if the select values are bound before the if is evaluated, causing the error to show up because the text field type lacks the options array.
A little late but I wanted to give a suggestion -- for SELECT multi-selects, you should decouple the bound variable from the multi-selector to prevent those errors.
For example, if you in your custom elements that bind to 'selected', they should bind to:
<select multiple value.two-way="selectedDecoupled">
Then when the actual variable 'selected' changes, it only changes in the custom element if the bound value is an array:
selectedChanged( newV, oldV ){
if( typeof newV =='object' )
this.selectedDecoupled = newV;
else
this.selectedDecoupled = [];
$(this.SELECT).val(this.selectedDecoupled ).trigger('change');
}
Example of it in use with a custom select2 element:
https://github.com/codefreeze8/aurelia-select2
Ok so it turns out swapping the order of the HTML, and putting the select after the input solves this issue.
Jeremy Danyow explains it like this:
When Form.field changes, the bindings subscribing to that property's changes evaluate sequentially. Which means there's a period of time when the select AND the input are both on the page. The html input element coaleses null values to empty string which in turn causes field.value to be empty string, which makes the multi-select throw.
Very tricky to track down imo but I'm glad the Aurelia devs are so helpful over on Github.
Working Gist: https://gist.run/?id=3f88b2c31f27f0f435afe14e89b13d56

Vue.js 2 - $forceUpdate() on components doesn't refresh computed properties?

I'm not sure if I'm doing this right or wrong, but all the answers I seem to find how to update the dom for computed values...
I have this component:
Vue.component('bpmn-groups', {
props: ['groups', 'searchQuery'],
template: '#bpmn-groups',
computed: {
filteredGroups: function () {
var self = this;
return this.groups.filter(function(group) {
self.searchQuery = self.searchQuery || '';
return _.includes( group.name.toLowerCase(), self.searchQuery.toLowerCase() );
});
}
},
methods: {
clearFilter: function () {
this.searchQuery = '';
},
deleteGroup: function(group) {
Vue.http.delete('api/groups/'+group.id ).then(response => { // success callback
var index = this.groups.indexOf(group); // remove the deleted group
this.groups.splice(index, 1);
this.$forceUpdate(); // force update of the filtered list?
toastr.success('Schemų grupė <em>'+group.name+'</em> sėkmingai pašalinta.');
}, response => { // error callback
processErrors(response);
});
this.$forceUpdate();
},
},
});
And in the template I just have a simple v-for to go through filteredGroups:
<input v-model="searchQuery" type="text" placeholder="Search..." value="">
<div v-for="group in filteredGroups" class="item">...</div>
The deletion works fine, it removes it from groups property, however the filteredGroups value still has the full group, until I actually perform a search or somehow trigger something else...
How can I fix it so that the filteredGroup is updated once the group is updated?
Don't mutate a prop - they are not like data defined attributes. See this for more information:
https://v2.vuejs.org/v2/guide/components.html#One-Way-Data-Flow
Instead, as recommended in the link, declare a local data attribute that is initialized from the prop and mutate that.