HTML text input not updating with Knockout Bindings - javascript

I am trying to automatically add slashes for a DOB input field but Knockout bindings are not playing nice.
<input
name=x
size=10
maxlength=10
class="span12"
placeholder="Date of birth (MM/DD/YYYY)"
onkeyup="this.value=this.value.replace(/^(\d\d)(\d)$/g,'$1/$2').replace(/^(\d\d\/\d\d)(\d+)$/g,'$1/$2').replace(/[^\d\/]/g,'')"
data-bind="value:visitor().dateOfBirth, valueUpdate:'keyup'" />
The issue now is that the Knockout Js bindings are not allowing the slashes value to be automatically updated. When I enter dates in the text box no slashes show up. How do I set up a subscriber for myViewModel to modify the value whenever it changes?

You should do something along these lines - note you should remove the inlined onkeyup from your input tag and let your view model handle it:
var vm = function () {
var self = this;
self.dateOfBirth = ko.observable();
self.insertSlashes = function () {
var currentValue = self.dateOfBirth();
self.dateOfBirth(currentValue
.replace(/^(\d\d)(\d)$/g,'$1/$2')
.replace(/^(\d\d\/\d\d)(\d+)$/g,'$1/$2')
.replace(/[^\d\/]/g,'')
);
}
}
ko.applyBindings(new vm());
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<input
placeholder="Date of birth (MM/DD/YYYY)"
data-bind="value: dateOfBirth, valueUpdate:'keyup', event: { keyup: insertSlashes } "
/>

Related

How to make data input readonly, but showing calendar?

I can't find any solution for this anywhere. I would like to allow user to see calendar when clicking on my input, but when user will try to change the value, it shouldn't change (or immediately change back for value, what was at the beggining).
Readonly attribute removes calendar, so I was trying to do it different ways like use onchange/onclick functions to restore my value after event, but the value was changing anyway.
So for now my input looks like this:
<input class='test' type='date' value='2020-06-04' onkeydown='return false' >
So at least user can't change value of my input using keyboard. Could you help me?
You might try to set a time limit
<html>
<body>
<input type="date" value="2020-06-04">
<input type="date" value="2020-06-04" min="2020-06-04" max="2020-06-04">
</body>
</html>
If I understood correctly, this is what you want.
HTML
<input class='test' type='date' value='2020-06-04' />
JavaScript
const date = "2020-06-04";
const input = document.querySelector('.test');
input.onkeydown = function(e) {
return false;
}
input.onchange = function(e) {
this.value = date;
}
Check out this JSFiddle
EDIT
function preventInputChange(input, staticValue) {
input.onchange = function(e) {
this.value = staticValue;
}
}

How can i know change in knockout.js ViewModel just by change in its observable property?

I have a ViewModel in knockout.js for personal information.
I want javascript to think that whole PersonViewModel is changed if just a single observable property in that model is changed.
I also have another ViewModel for address and i want the same for it.
As an end user of program I want program to tell which view model is changed by change in any of the observable property.
I could use "subscribe" but that means i would have to subscribe every observable inside the view model and i don't want to do that.
Figuratively, i want to subscribe the whole ViewModel instead of each observable inside it.
What should i do?
function PersonViewModel() {
this.firstName = ko.observable("John");
this.lastName = ko.observable("Doe");
this.middleName = ko.observable();
this.userName = ko.observable("Johnny");
this.dateOfBirth = ko.observable("12/12/2012");
this.firstName.subscribe(function () {
alert("fisrtName changed");
});
}
function AddressViewModel() {
this.city = ko.observable("#Model.City");
this.street = ko.observable("#Model.Street");
}
var pvm = new PersonViewModel();
var avm = new AddressViewModel();
var pNode = $("#personal-information").get(0);
var aNode = $("#address-information").get(0);
ko.applyBindings(pvm, pNode);
ko.applyBindings(avm, aNode);
My HTML:
<div id="personal-information">
<input data-bind="value: firstName" type="text" >
<input data-bind="value: lastName" type="text" >
<input data-bind="value: middleName" type="text" >
<input data-bind="value: username" type="text" >
<input data-bind="value: dateOfBirth" type="text" >
</div>
Any help will be appreciated.
Thanks.
Knockout includes a ko.toJS function that "clones your view model’s object graph, substituting for each observable the current value of that observable, so you get a plain copy that contains only your data and no Knockout-related artifacts." If you call ko.toJS in a computed, that computed will update whenever any observable in the view model is changed.
var p = ko.computed(function () {
return ko.toJS(pvm);
});
var log = ko.observableArray();
p.subscribe(function (value) {
log.push("Person changed");
});
https://jsfiddle.net/mbest/ubLzwerp/
Also see https://stackoverflow.com/a/7850364/1287183

Knockout validation throttle

Hi i have a css binding on a input type which adds the class CircleErrors if it matches my function. My problem is it has a delay on taking the class off it only happens when i tab off the input box. I want the class to be removed on key down of the keyboard.. i know there is a throttle you can use for knockout but i am not sure how to go about doing it.
<input id="firstName" type="text" placeholder="First name" data-bind="value: Registration.FirstName, css: { CircleErrors: Registration.FirstName().length == 0 && Registration.FirstNameValidation(), valueUpdate: 'afterkeydown' }">
You've misplaced your valueUpdate parameter. It's inside the css parameter - you need to move it outside the }:
<input id="firstName" type="text" placeholder="First name" data-bind="value: Registration.FirstName, css: { CircleErrors: Registration.FirstName().length == 0 && Registration.FirstNameValidation() }, valueUpdate: 'afterkeydown'">
Here's a demo with it working
Use the textInput binding instead of the value binding for the first name property (and for any text input fields for that matter). To quote the docs
Unlike the value binding, textInput provides instant updates from the
DOM for all types of user input, including autocomplete,
drag-and-drop, and clipboard events.
You don't need the valueUpdate binding any more, however it was inside your CSS binding so would not have had any effect.
<input id="firstName" type="text" placeholder="First name" data-bind="textInput: Registration.FirstName, css: { CircleErrors: Registration.FirstName().length == 0 && Registration.FirstNameValidation() }">
Demo
this is what you looking for
` var reg = new (function() {
var self = this;
this.FirstName = ko.observable('');
this.checkifEmpty = ko.observable(false);
this.check=function(){
if(this.FirstName()!=null&&this.FirstName()!=undefined && this.FirstName() !=''){
this.checkifEmpty(true);
}else{
this.checkifEmpty(false);
}
}
this.FirstNameValidation = function() {
return true;
}
})();
ko.applyBindings(reg);
`

Keyup function into form element

I have a script I am using to copy a field into another input field using keyup blur paste. This script works, however I need to modify it to also go into two different form elements which are named data-cost="" and debt="", instead of the <div id="viewer">
This is the script as I have it now :
$(function () {
$('#account_balance1').on('keyup blur paste', function() {
var self = this;
setTimeout(function() {
var str = $(self).val();
$("#viewer").text(str.replace(/^\$/, ''));
}, 0);
});
$("#viewer").text($('#Website').val().replace(/^\$/, ''));
});
This is the html :
<!--This where I get the value from -->
<input type="text" class="balance" id="account_balance1" name="cardb" value=""/>
<!--the first 2 elements are where I need the values to go -->
<input data-cost="" debt="" value="" type="checkbox" name="f_2[]"/>
if you need the two attributes (data-cost and debt) to be each set to your value you need:
$("input[data-cost][debt]").attr('data-cost',str.replace(/^\$/, ''));
$("input[data-cost][debt]").attr('debt',str.replace(/^\$/, ''));
Just use that selector then
$("input[data-cost][data-debt]")
I think you're maybe having a fundamental misunderstanding of what the data attributes are for? You're aware that they will not be posted with the form? I think what you're looking for is the data function which will allow you to set the data attributes http://api.jquery.com/data/.
Perhaps you want data-cost and data-debt?
So if your input looks like this:
<input data-cost="" data-debt="" value="" type="checkbox" name="f_2[]" id="checkboxId" />
Then you can set the values in your javascript like this:
var value1="some value";
var value2="another value";
$('#checkboxId').data('cost', value1);
$('#checkboxId').data('debt', value2);
I don't believe having an attribute named simply "debt" as you have it above is valid.
I'd do it this way (setTimeout was useless) :
$(function () {
$('#account_balance1').on('keyup blur paste', function () {
var self = this;
var nextCheckbox = $(self).next("input[type='checkbox']");
var str = $(self).val();
$("#viewer").text(str.replace(/^\$/, ''));
nextCheckbox.data({
cost: str,
debt: str
});
/*
You won't be able to see changes if you inspect element,
so just check in console
*/
console.log(nextCheckbox.data());
});
});
And your html markup must be slightly modified :
<!--This where I get the value from -->
<input type="text" class="balance" id="account_balance1" name="cardb" value="" />
<!--the first 2 elements are where I need the values to go -->
<input data-cost="" data-debt="" value="" type="checkbox" name="f_2[]" />
<!--I added the viewer to check if everything works properly -->
<div id="viewer"></div>

copy form to Backbone.js model

I have a form:
<form>
<input type="text" name="email" >
<input type="text" name="phone" >
<input type="button" value="ok" />
</form>
When clicking the button, I'd like to copy the form values to a corresponding model.
I've found Backbone.ModelBinder which will automatically copy values to model whenever the values are changed, but that's not what I want, I just want to copy the values when the button is clicked.
write a custom function into the view where the form is located and bind it to the ok click event:
events: {
...
'click input[name="ok"]': 'copyFormToModel'
...
},
...
copyFormToModel: function() {
var email = $('input[name="email"]').val();
var phone = $('input[name="phone"]').val();
// Perform some sort of validation
this.model.email = email;
this.model.phone = phone;
}
This isn't the prettiest answer, but if you have just one small form in your page, then using some library or plugin might be a bit overkill. If you want to use a plugin or library, then for your case I think backbone-forms could do the trick. It features updating the model bound to the form with a method call rather than every time fields are updated.
This code may be you need:
events: {
...
'click input[value="ok"]': 'collectData2Model'
...
},
...
//suppose employee is your model
collectData2Model: function(e) {
var employee = new Employee();
var attr = {};
$('input').each(function(){
var input = $(this);
attr[input.attr('name')] = input.val();
});
employee.bind('error',function(model,error){
alert(error);
});
// set method will automatically call the model's validate method
employee.set(attr);
}

Categories

Resources