I need a input mask that doesn't sync with knockoutJS observable variables.
Ex: if the user input is 50000000, the UI should show the masked value (500,000.00) but in the ViewModel (knockoutJS) variable it should save like 500000.00
I tried with 3 following jQuery plugins
github.com/RobinHerbots/Inputmask
github.com/igorescobar/jQuery-Mask-Plugin
digitalbush.com/projects/masked-input-plugin/
Even tried with the knockoutJS integration of RobinHerbots input mask plugin.
All those plugins were working perfectly but the problem is,
When user input the value 50000000, it shows as 500,000.00 but it has been saved to the knockoutJS observable variable the same value (500,000.00).
Do anyone facing a problem like this?
Update:
I have modified some code in RobinHerbots input mask (knockout integration) and now I'm removing all commas from the input string when the value is updating.
[JAVASCRIPT]
ko.bindingHandlers.inputmask = {
init: function (element, valueAccessor, allBindingsAccessor) {
var mask = valueAccessor();
var observable = mask.value;
if (ko.isObservable(observable)) {
$(element).on('focusout change', function () {
if ($(element).inputmask('isComplete')) {
observable($(element).val());
} else {
observable(null);
}
});
}
$(element).inputmask(mask);
},
update: function (element, valueAccessor, allBindings, viewModel, bindingContext) {
var mask = valueAccessor();
var observable = mask.value;
if (ko.isObservable(observable)) {
var valuetoWrite = observable();
$(element).val(valuetoWrite);
var value = valuetoWrite.toString().replace(/,/g ,'');
observable(value);
}
}
[HTML]
<input type="text" data-bind="inputmask: { value:ItemPrice , mask:'999,999.99'}" />
Now it works as I needed. Anyone knows how to get the currency (It doesn't work the way we used to do with jQuery) mask in the RobinHerbots input mask in knockout version?
You can create a knockout extension for masking with markup like
<input type="text" data-bind="customMasking:{}"/>
ko.bindingHandlers.customMasking={
init:function(element,valueaccessor,allbindingaccessor){
var el =$(element);
var options=allbindingaccessor();
el.inputmask({
//provide your options
})
}
}
I have also faced similar difficulties in using the masks. I prefer to keep a computed variable that cleans up the input value. See the snippet below:
var model = function() {
var self = this;
self.number = ko.observable('0');
self.floatNumber = ko.computed(function() {
return self.number().split(',').join('');
});
}
ko.applyBindings(new model());
$("#test").inputmask({ alias : "currency", prefix: '' });
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery.inputmask/3.2.6/jquery.inputmask.bundle.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<input type="text" id="test" data-bind="textInput: number" />
<br />
Cleaned Number:
<span data-bind="text: floatNumber" ></span>
Related
I've got a ko.observable in my viewmodel, which is attached to an input. When user changes value of that input (per-character) I run AJAX call, which downloads suggestions from backend.
When user chooses one of suggestions, I'd like to fill the input with chosen value, but without sending the AJAX call. But setting observable's value will still trigger event and call function attached to textInput binding.
How can I set observable's value without triggering textInput?
Sample code:
var name = ko.observable();
name.subscribe(nameChanged);
var nameChanged = function() {
someService.post(name(), success, failure);
}
var someAction = function() {
name("Test"); // I don't want to trigger AJAX call here
}
<input type="text" data-bind="textInput: name" />
Another option is to use a computed observable as an intermediary. The ajax function can trigger in the write event of the computed so that direct writes to the observable bypass it.
function viewModel(){
var self = this;
self.realName = ko.observable('test');
self.Name = ko.computed({
read: function(){
return self.realName();
},
write: function(value){
self.realName(value);
nameChanged(value);
}
});
function nameChanged(newName) {
console.log("name changed:", newName);
}
self.modifyName = function(){
self.realName(self.realName() + 'z');
}
}
ko.applyBindings(new viewModel());
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<input type="text" data-bind="textInput: Name" />
<br />
<button data-bind="click: modifyName">Modify</button>
Maybe set a particular CSS class on the field after the selection is made and put some logic around the ajax call to check if the field has the CSS class.
Instead of subscribing to the change event of the observable you could tie your ajax call to the change event of the ui element instead. You would get rid of name.subscribe(nameChanged); and add an event binding:
data-bind="textInput: name, event: { changed: nameChanged }"
An "old fashioned" way that works, but is not really elegant, is to temporarily dispose the subscription and then reattach it:
var myName = ko.observable();
var nameSub = myName.subscribe(nameChanged);
function nameChanged(newName) {
console.log("name changed:", newName);
}
function changeWithoutLog() {
nameSub.dispose();
myName("Test");
nameSub = myName.subscribe(nameChanged);
}
ko.applyBindings({ name: myName });
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<input type="text" data-bind="textInput: name" />
<button data-bind="click: changeWithoutLog">change without post</button>
I'm attempting to enable / disable an input field.
<input data-bind="disable: chatDisabled" id="send-message" type="textarea" class="input-area" value="">
And my knockoutJS
function MessagesViewModel() {
var self = this;
var socket = io.connect('http://127.0.0.1:4000');
self.messages = ko.observableArray([]);
self.chatSend = ko.observable();
self.questionChoice = ko.observable();
self.chatDisabled = ko.observable(false);
socket.on('receiveMessages', function(data) {
self.messages(data.messages);
var last = data.messages[data.messages.length-1];
self.chatDisabled = last.enforce || false;
scrollToBottom();
});
}
ko.applyBindings(new MessagesViewModel());
I've managed to get other parts working such as having a list of messages on the screen populate when the array is updated. However for the life of me I cannot get the input to toggle between disabled / enabled when it is changed within socket.on(
self.messages( updates corrected so why does self.chatDisabled not? (To be clear the variable JS side is updated however it the data-bind is not.
This should do the trick:
self.chatDisabled(last.enforce || false);
You were assigning a new value to your property instead of updating the observable value.
I have model in a page that is bound to several controls. Based on some condition some of these controls will be visible or invisible. And on the final submit I should only validate those which are visible.
The following is a sample code to explain my requirement
<script src="knockout-3.4.0.js" type="text/javascript"></script>
<input type="checkbox" data-bind="checked:requireAge" >Age Required</input><br />
Name : <input data-bind="value:Name" /><br />
<div data-bind="visible:requireAge">
Age: <input data-bind="value:Age,visible:requireAge" />
</div>
<button type="button" onclick="validateModel();">Validate</button>
<script type="text/javascript">
var viewModel = { Name: ko.observable(), Age: ko.observable(),requireAge:ko.observable(false) };
ko.applyBindings(viewModel);
function validateModel() {
//validate visible properties and throw a common message that all visible fields should be filled
}
</script>
My suggestion is to use the knockout-validation library (you made no mention of it in your question so I assume you're not using it already) It ties in seamlessly with knockout and makes validation far more convenient. I've used it extensively over the past year and its make my life a whole lot easier. No need to create computeds to keep track of whether an observable contains a valid value or not. You can find the knockout-validation library on github.
In your case you can simply do the following:
var viewModel = function(){
var self = this;
self.name = ko.observable();
self.requireAge = ko.observable(false);
self.age = ko.observable().extend({
required: {
onlyIf: function() { return self.requireAge(); }
}
});
};
Validation error messages are inserted automatically below the element the observable is bound to. You can also create your own validation rules but there are many that work straight out the box including the one demonstrated above. You can even use some data attributes for some rules. This is probably the best way to go about validation in conjunction with knockout.
Based on some condition some of these controls will be visible or invisible.
It would be better if these conditions are contained in the model. And validation method too.
See snippet:
var viewModel = function() {
this.Name = ko.observable("");
this.Age = ko.observable("");
this.requireAge = ko.observable(false);
this.isValid = ko.computed(function() {
if (ko.unwrap(this.Name).length === 0) return false;
if (ko.unwrap(this.requireAge) &&
ko.unwrap(this.Age).length === 0) return false;
return true;
}, this);
};
window.onload = function() {
ko.applyBindings(new viewModel());
};
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<input type="checkbox" data-bind="checked:requireAge" >Age Required</input><br />
Name : <input data-bind="value:Name" /><br />
<div data-bind="visible:requireAge">
Age: <input data-bind="value:Age,visible:requireAge" />
</div>
<div>is valid: <span data-bind="text: isValid"></span></div>
Well in other cases i will get datepicker binded to my textbox which will be straight forward but not in this case .
Fiddle link : http://jsfiddle.net/JL26Z/1/ .. while to setup perfect seanrio i tried but unable to bind datepicker to textboxes . except that everything is in place
My code :
**<script id="Customisation" type="text/html">** // here i need to have text/html
<table style="width:1100px;height:40px;" align="center" >
<tr>
<input style="width:125px;height:auto;" class="txtBoxEffectiveDate" type="text" id="txtEffective" data-bind="" />
</tr>
</script>
The above code is used for my dynamic generation of same thing n no of time when i click each time on a button . So above thing is a TEMPLATE sort of thing .
My knockout code :
<div data-bind="template:{name:'Customisation', foreach:CustomisationList},visible:isVisible"></div>
<button data-bind="click:$root.CustomisatioAdd" >add </button>
I tried same old way to bind it with datepicker
$('#txtEffective').datepicker(); // in document.ready i placed
Actually to test this i created a textbox with some id outside script with text/html and binded datepicker to it and It is working fine sadly its not working for the textbox inside text/html and i want to work at any cost.
PS: well i haven't posted my view model as it is not required in this issue based senario
View model added with Js
var paymentsModel = function ()
{
function Customisation()
{
var self = this;
}
var self = this;
self.isVisible = ko.observable(false);
self.CustomisationList = ko.observableArray([new Customisation()]);
self.CustomisationRemove = function () {
self.CustomisationList.remove(this);
};
self.CustomisatioAdd = function () {
if (self.isVisible() === false)
{
self.isVisible(true);
}
else
{
self.CustomisationList.push(new Customisation());
}
};
}
$(document).ready(function()
{
$('#txtEffective').datepicker();
ko.applyBindings(new paymentsModel());
});
Any possible work around is appreciated
Regards
The best way I've found to do this is create a simple bindingHandler.
This is adapted from code I have locally, you may need to tweak it...
** code removed, see below **
Then update your template:
** code removed, see below **
By using a bindingHandler you don't need to try to hook this up later, it's done by knockout when it databinds.
Hope this is helpful.
EDIT
I created a fiddle, because I did indeed need to tweak the date picker binding quite a lot. Here's a link to the Fiddle, and here's the code with some notes. First up, the HTML:
<form id="employeeForm" name="employeeForm" method="POST">
<script id="PhoneTemplate" type="text/html">
<div>
<span>
<label>Country Code:</label>
<input type="text" data-bind="value: countryCode" />
</span>
<span><br/>
<label>Date:</label>
<input type="text" data-bind="datepicker: date" />
</span>
<span>
<label>Phone Number:</label>
<input type="text" data-bind="value: phoneNumber" />
</span>
<input type="button" value="Remove" data-bind="click: $parent.remove" />
</div>
</script>
<div>
<h2>Employee Phone Number</h2>
<div data-bind="template:{name:'PhoneTemplate', foreach:PhoneList}">
</div>
<div>
<input type="button" value="Add Another" data-bind="click: add" />
</div>
</div>
</form>
Note I removed the id=... from in your template; because your template repeats per phone number, and ids must be unique to be meaningful. Also, I removed the datepicker: binding from the country code and phone number elements, and added it only to the date field. Also - the syntax changed to "datepicker: ". If you need to specify date picker options, you would do it like this:
<input type="text" data-bind="datepicker: myObservable, datepickerOptions: { optionName: optionValue }" />
Where optionName and optionValue would come from the jQueryUI documentation for datepicker.
Now for the code and some notes:
// Adapted from this answer:
// https://stackoverflow.com/a/6613255/1634810
ko.bindingHandlers.datepicker = {
init: function(element, valueAccessor, allBindingsAccessor) {
//initialize datepicker with some optional options
var options = allBindingsAccessor().datepickerOptions || {},
observable = valueAccessor(),
$el = $(element);
// Adapted from this answer:
// https://stackoverflow.com/a/8147201/1634810
options.onSelect = function () {
if (ko.isObservable(observable)) {
observable($el.datepicker('getDate'));
}
};
$el.datepicker(options);
// set the initial value
var value = ko.unwrap(valueAccessor());
if (value) {
$el.datepicker("setDate", value);
}
//handle disposal (if KO removes by the template binding)
ko.utils.domNodeDisposal.addDisposeCallback(element, function() {
$el.datepicker("destroy");
});
},
update: function(element, valueAccessor) {
var value = ko.utils.unwrapObservable(valueAccessor()),
$el = $(element);
//handle date data coming via json from Microsoft
if (String(value).indexOf('/Date(') === 0) {
value = new Date(parseInt(value.replace(/\/Date\((.*?)\)\//gi, "$1")));
}
var current = $el.datepicker("getDate");
if (value - current !== 0) {
$el.datepicker("setDate", value);
}
}
};
function Phone() {
var self = this;
self.countryCode = ko.observable('');
self.date = ko.observable('');
self.phoneNumber = ko.observable('');
}
function PhoneViewModel() {
var self = this;
self.PhoneList = ko.observableArray([new Phone()]);
self.remove = function () {
self.PhoneList.remove(this);
};
self.add = function () {
self.PhoneList.push(new Phone());
};
}
var phoneModel = new PhoneViewModel();
ko.applyBindings(phoneModel);
Note the very updated binding handler which was adapted from this answer for the binding, and this answer for handling onSelect.
I also included countryCode, date, and phoneNumber observables inside your Phone() object, and turned your model into a global variable phoneModel. From a debugger window (F12 in Chrome) you can type something like:
phoneModel.PhoneList()[0].date()
This will show you the current value of the date.
I notice that your form is set up to post somewhere. I would recommend instead that you add a click handler to a "Submit" button and post the values from your phoneModel using ajax.
Hope this edit helps.
Dynamic entities need to have datepicker applied after they are created. To do this I'd use an on-click function somewhere along the lines of
HTML
<!-- Note the id added here -->
<button data-bind="click:$root.CustomisatioAdd" id="addForm" >add </button>
<script>
$(document).on('click', '#addForm', function(){
$('[id$="txtEffective"]').datepicker();
});
</script>
I am trying to store value in attribute in input tag which would like as follows.
The following code will repeat several times and the value for different radio can be saved using index.
<div data-bind="attr : { name : 'ex['+$index()+']' }>
<input type="radio" name="value" data-target="#modal" data-bind="click:fun.fill($index())"/>
</div>
From the above text box i can get the index of that input.And it points to a common modal function from bootstrap which opens a popup.
<div class="modal fade">
<select data-bind="attr:{name:'assignedResources['+$index()+'][repeatedType]'},
options : $root.repeats,value : repeatedType"></select>
</div><!-- /.modal -->
This is a sample of modal target.I want to call this modal as common.(i.e)it will be called from several places.I want to show appropriate popup for appropriate click from radio button.
But what i get is value of last index.Thats my problem
I'm not 100% sure I understand your question, but something like this...
this.abc = ko.observable('');
this.fun = {
var that = this;
fill: function(index) {
console.log(that.abc());
}
}
As far as I understand your problem, you need an observable to store your value, and then you can access it by any other function on your viewModel.
This is a jsFiddle with my initial approach, let me know if is what you needed or clarify instead:
http://jsfiddle.net/rdarioduarte/X8Rc4/
With a model like this:
var viewModel = function() {
this.abc = ko.observable('Value to store');
this.fun = function() {
alert(this.abc());
}
}
ko.applyBindings(new viewModel());
Thanks,
Dario
I do not quite understand about your question. But, maybe this may help:
1) Obtains index of your input element automatically on page load to viewModel function maybe need custom binding to handle that:
e.g custom binding:
ko.bindingHandlers.saveIndex = {
init: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
var index = ko.utils.unwrapObservable(valueAccessor());
viewModel.fun(index); // accessing fun function on viewModel
// You can also access that function using bindingContext.$root
// bindingContext.$root.fun(index);
}
};
e.g html:
<input type="text" data-bind="saveIndex: $index()"/>
2) Store index to input value attribute using attr binding:
<input type="text" data-bind="attr: { value: $index() }">
3) Send your input attr value to viewModel function onClick:
<input type="text" value="thisIsExampleValue" data-bind="click: function() { $root.fun($element.value); }">
You also can use event binding for this behavior, Knockout.Event-binding
UPDATE:
Try this:
<div data-bind="attr : { name : 'ex['+$index()+']' }>
<input type="radio" name="value" data-target="#modal" data-bind="click: function() { fun.fill($index()) }"/>