I am trying to create a utility method in Vue.js to validate a decimal number from any input field but I'm not sure how to set the value in Vue.js internally.
This is what I did in jQuery before:
$('body').on('blur', '.decimal', function() {
var val = $(this).val();
if ($.isNumeric(val)) {
val = parseFloat(val).toFixed(2);
$(this).val(val);
} else {
$(this).val('');
}
});
This is what I have in Vue but the value is not stored internally and is overwritten.
function isNumeric(n) {
return !isNaN(parseFloat(n)) && isFinite(n);
}
methods: {
validateDecimal: function (e) {
var val = e.target.value;
if (isNumeric(val)) {
e.target.value = parseFloat(val).toFixed(2);
} else {
e.target.value = '';
}
}
}
HTML
<input class="ui-input" :value="some.value" placeholder="0.00" #blur="validateDecimal">
<input class="ui-input" :value="some.othervalue" placeholder="0.00" #blur="validateDecimal">
<input class="ui-input" :value="another.dynamic.input" placeholder="0.00" #blur="validateDecimal">
Apparently you can pass the data object reference to the handler method like so:
(Note you can't just pass the data property, because I don't believe it will be a reference.)
new Vue({
el: '#app',
data: {
message: 'Hello Vue.js!',
inputs:{
'v1':{
value:'1.256'
},
'v2':{
value:'1.521'
}
},
someInput:{value:'1.7125'}
},
methods:{
validateDecimal: function (o,p,e) {
console.log('validateDecimal',o);
var val = e.target.value;
console.log(val);
if (Number(parseFloat(val)) == val) {
val = parseFloat(val).toFixed(2);
this.$set(o, p, val);
} else {
this.$set(o, p, '');
e.target.value = '';
}
},
foo: function(){
console.log(this.inputs.v1.value)
console.log(this.inputs.v2.value)
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div v-for="i,key in this.inputs">
<input class="ui-input" :value="i.value" placeholder="0.00" #blur="validateDecimal(i, 'value', $event)">
</div>
<div>
<input class="ui-input" :value="this.someInput.value" placeholder="0.00" #blur="validateDecimal(someInput,'value', $event)">
</div>
<button #click="foo">Click</button>
</div>
Edit by OP: Adding an extra parameter for the name of the property and using $set to make the dynamic property reactive. This should make the method more general purpose for any dynamic input fields with any property name.
Related
I'm trying to bind focusout event to my knockout js. here is the example:
<div class="form">
<label>
Country:
</label>
<input type="text" id="countryName" name="countryId._autocomplete" data-bind="value: countryName,event: { blur: onBlurCountryEvent }" />
</div>
<div class="form" data-bind="visible: onBlurCountryEvent">
<label>
Time zone:
</label>
<input type="text" id="timeZoneName" name="timeZoneId._autocomplete" data-bind="value: timeZoneName" />
</div>
and this is my knockoutjs:
define(['viewmodels/shell', 'durandal/system', 'durandal/services/logger', 'plugins/router', 'knockout', 'common', 'jqueryform', 'toastr', 'kovalidationconfig'],
function (shell, system, logger, router, ko, common, jqueryform, toastr, kvc) {
var vm = {
activate: activate,
logger: logger,
shell: shell,
countryId: ko.observable(),
countryName: ko.observable(),
timeZoneName: ko.observable(),
timeZoneId: ko.observable(),
timeZoneVisibility: timeZoneVisibility,
bindingComplete: function (view) {
bindFindCountryEvent(view);
bindFindTimeZoneEvent(view);
}
};
vm.onBlurCountryEvent = function () {
var countryVal = $('#countryName').val();
if (countryVal != undefined && countryVal != null && countryVal != '') {
console.log("trueee");
return true;
}
else {
console.log("falseee");
return false;
}
}
function bindFindCountryEvent(view) {
jQuery("#countryName", view).typeahead(
...
}
function bindFindTimeZoneEvent(view) {
jQuery("#timeZoneName", view).typeahead(
...
}
function activate(id) {
shell.isLoading(true);
...
shell.isLoading(false);
});
return true;
}
vm.save = function () {
...
};
});
So, as you can see, I want to have some event and binded function, when I do onBlur from my field country, to check, and to preview timezone field if there any selected country from dropdown search.
Also, if user skips the country, timezone filed should remain visible:false
the event works, and I can see in my console true/false values.
However, my field of timeZone is intact. No matter if this country field is empty or non-empty, the fields is visible always.
If I put visible:false (hardcoded value), it works.
Should I need to bind that function vm.onBlurCountryEvent?
the problem is that the function onBlurCountryEvent is not an observable, so knockout is not checking for changes. I would suggest adding a isTimezoneVisible : ko.observable(false) to your view model then set the isTimeZoneVisible in the onBlurCountryEvent.
In your view set the visible binding to isTimeZoneVisible. Something like the following
var vm = {
countryId: ko.observable(),
countryName: ko.observable(),
timeZoneName: ko.observable(),
timeZoneId: ko.observable(),
isTimeZoneVisible: ko.observable(false), //new property
bindingComplete: function(view) {
bindFindCountryEvent(view);
bindFindTimeZoneEvent(view);
}
};
vm.onBlurCountryEvent = function() {
var countryVal = $('#countryName').val();
if (countryVal != undefined && countryVal != null && countryVal != '') {
console.log("trueee");
vm.isTimeZoneVisible(true); //set property
} else {
console.log("falseee");
vm.isTimeZoneVisible(false); //set property
}
}
function bindFindCountryEvent(view) {
}
function bindFindTimeZoneEvent(view) {
}
ko.applyBindings(vm);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<div class="form">
<label>
Country:
</label>
<input type="text" id="countryName" name="countryId._autocomplete" data-bind="value: countryName,event: { blur: onBlurCountryEvent }" />
</div>
<div class="form" data-bind="visible: isTimeZoneVisible">
<label>
Time zone:
</label>
<input type="text" id="timeZoneName" name="timeZoneId._autocomplete" data-bind="value: timeZoneName" />
</div>
I'm using Google autocomplete address form. I found example at google official web page. Everything is fine. Everything works! but it's native Javascript,
I have Vue application and I don't like how I change text input values from JS script. The idea is that when I change something in main input, JS event listener should change values for other inputs:
document.getElementById(addressType).value = val;
Problem is that I should use "document" to change values:
document.getElementById('street_number').value
I would like to have something like tat:
<input type="text" v-model="input.address" ref="addressRef">
And to read values:
export default {
data() {
return {
input: {
address: "",
...
}
};
},
methods: {
test() {
console.log(this.input.address);
console.log(this.$refs.addressRef);
}
}
So the question is:
How to set the value from JS code to update binding values? Right now values are null because I use "getElementById("id").value = val"
You can emit input event afterwards which v-model relies on for updating its value:
let el = document.getElementById("id");
el.value = val;
el.dispatchEvent(new Event('input'));
In action:
Vue.config.devtools = false
const app = new Vue({
el: '#app',
data: {
message: null
},
methods: {
updateBinding() {
let el = document.getElementById("input");
el.value = 'Hello!';
el.dispatchEvent(new Event('input'));
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<button #click="updateBinding">Click me </button><br>
<input id="input" v-model="message" placeholder="edit me">
<p>Message is: {{ message }}</p>
</div>
all how i can watch changes in my component in data?
I need watch when user choose car brand to take from server models for that brand
this is my code
Templete
<template>
<div class="category-info">
<div v-for="input in inputs.text">
<label >{{ input.placeholder}}</label>
<input type="text" id="location" :name="input.name" v-model="input.value" #click="console">
</div>
<div class="select" v-for="select in inputs.select">
<label >{{ select.placeholder }}</label>
<my-select :data="select" v-model="select.value"></my-select>
</div>
<button #click="console">click</button>
</div>
Script
<script>
export default {
name: "profile-add-inputs",
props: ['category'],
data() {
return {
inputs: {
text : {},
select: {}
},
}
},
methods: {
getCategories(){
axios.get('/profile/inputs', {
params: {
category: JSON.stringify(this.category.href)
}
})
.then((response) => {
this.inputs.text = response.data.text;
this.inputs.select = response.data.select;
for(let key in this.inputs.text){
this.inputs.text[key].value = '';
}
for(let key in this.inputs.select){
this.inputs.select[key].value = '';
if(this.category.href.sub == 'car' && this.inputs.select[key].name == 'brand'){
console.log('CAR BREND');
this.$watch.inputs.select[key].value = function () {
console.log(this.inputs.select[key].value);
}
}
}
},this)
.catch(function (error) {
console.log(error);
});
},
console(){
console.log(this.inputs.select);
}
},
watch: {
category : function () {
this.getCategories();
console.log('categoty');
},
inputs : {
handler() {
console.log('watch inputs');
}
}
}
}
So, i tried to use watch and $watch but its not working, plz give me a reason why that not work, or maybe some another way to resolve this problem
this.$watch can i create dynamiclly watchers with this stement?
The correct syntax is
watch : {
inputs : function(val, oldVal){
//val : New value
//oldVal : Previous value.
}
}
I've a page with two input fields. I've a JS object (info) containing 'reference' and a 'value' field of each item. For each item, there's a corresponding 'input' field with matched by 'class' attribute. When a user updates a matching input field, I want to add it's 'value' in the info object.
The problem I'm experiencing is that it's putting the value in the last item in the array (location.value) for either input.
Can anyone help me with where I'm going wrong please? (I could see solutions using 'each' where data for all inputs needs to be added to an array/object. I'm stuck on getting the data for matched fields.)
$(document).ready(function() {
var info = {
name: {
ref: "a2350",
value: ""
},
location: {
ref: "a2351",
value: ""
}
};
for (var p in info) {
if (info.hasOwnProperty(p)) {
$('input.' + p).focusout(function() {
var val = $(this).val();
info[p].value = val; // Only setting value on the last item in array!
//console.log(p); /// p = always location!
out();
})
}
}
function out() {
console.log(info);
}
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<input type="text" class="name" />
<input type="text" class="location" value="" />
Your issue is because p will be location when the loop ends. Therefore all the click handlers will update the location object, regardless of which element they are attached to.
To fix this you can use a closure to retain the scope of the p at the point the event handler logic was created. Also note that hasOwnProperty() is moot when you're looping through the properties of the object; it has to exist for the iteration to happen. Try this:
$(document).ready(function() {
var info = {
name: {
ref: "a2350",
value: ""
},
location: {
ref: "a2351",
value: ""
}
};
for (var p in info) {
(function(p) {
$('input.' + p).focusout(function() {
var val = $(this).val();
info[p].value = val;
out();
})
})(p);
}
function out() {
console.log(info);
}
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<input type="text" class="name" />
<input type="text" class="location" value="" />
Alternatively, you can avoid the loop and associated closure by using the class on the input to retrieve the required object:
$(document).ready(function() {
var info = {
name: {
ref: "a2350",
value: ""
},
location: {
ref: "a2351",
value: ""
}
};
$('input').focusout(function() {
var className = $(this).attr('class');
if (info.hasOwnProperty(className))
info[className].value = this.value;
out();
});
function out() {
console.log(info);
}
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<input type="text" class="name" />
<input type="text" class="location" value="" />
You can simply use the class attribute to update the object by key:
$(document).ready(function(){
var info = {
name: {
ref: "a2350",
value: ""
},
location: {
ref: "a2351",
value: ""
}
};
$('input').focusout(function() {
var className = $(this).attr('class');
var inputValue = $(this).val();
if (info.hasOwnProperty(className)) {
info[className].value = inputValue;
} else {
// If you want to add unknown inputs to the object
info[className] = {ref: "", value: inputValue};
}
console.log(info);
});
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input type="text" class="name" />
<input type="text" class="location" value="" />
<input type="text" class="not_exists" value="" />
You need something more like:
<script
src="https://code.jquery.com/jquery-3.2.1.min.js"
integrity="sha256-hwg4gsxgFZhOsEEamdOYGBf13FyQuiTwlAQgxVSNgt4="
crossorigin="anonymous"></script>
<script>
$(document).ready(function(){
var info = {
name: {
ref: "a2350",
value: ""
},
location: {
ref: "a2351",
value: ""
}
};
for (var p in info)
{
if ( info.hasOwnProperty(p) )
{
doSomethingWith(p);
}
}
function doSomethingWith(p)
{
$('input.' + p).focusout(function()
{
var val = $(this).val();
info[p].value = val;
console.log(p); /// p = the class of the input now.
});
}
});
</script>
<input type="text" class="name" />
<input type="text" class="location" value="" />
I have two inputs, first:
<input v-model="from_amount" id="from_amount" type="text" class="form-control" name="from_amount">
And the second:
<input id="from_amount" type="text" class="form-control" name="to_amount" value="#{{ from_amount }}">
If i type number in from_amount it should be outputted in to_amount
Here's my VueJS code:
var request = new Vue({
el: '#request-creator',
data: {
from_amount: '',
to_amount: ''
},
computed: {
calculate: function() {
return (this.from_amount * 750) / 0.00024
}
}
})
But seems like it's impossible to do with Vue?
You need to use v-bind, to bind computed property to an input field like following:
<input id="from_amount" type="text" class="form-control" name="to_amount" v-bind:value="calculatedFromAmount">
or in short, you can also write
... :value="calculatedFromAmount">
See Working fiddle: http://jsfiddle.net/bvr9754h/
You have to define computed property like following in due component:
computed: {
calculatedFromAmount: function() {
return (this.from_amount * 750) / 0.00024
}
}
it's very possible.
Modify your code so that to_amount is the name of the computed property :
var request = new Vue({
el: '#request-creator',
data: {
from_amount: '',
},
computed: {
to_amount: function() {
return (this.from_amount * 750) / 0.00024
}
}
})
and the html to :
<input id="from_amount" type="text" class="form-control" name="to_amount" :value="to_amount">