KnockoutJS setting focus after adding to array/collection - javascript

I have viewmodel with an array of diagnosis codes. In my html I have a button data-bound to a click that adds a blank diagnosis code to the array. This all works.
What I cant figure out, is how to set focus to the dynamically added textbox when a code is added. What can I add :
<h3>Diagnosis Codes<input type="button" value="Add" data-bind="click:AddDiagnosisCode"/></h3>
<div data-bind="foreach:DiagnosisCodes">
<div><input type="text" data-bind="value:$data"/>
</div>
</div>
<script type="text/javascript">
function AddDiagnosisCode(item)
{
item.DiagnosisCodes.push("");
}
var vm = {
"DiagnosisCodes": ["2345","6789"]
};
var viewModel = ko.mapping.fromJS(vm);
ko.applyBindings(viewModel);
</script>

Use the built-in binding hasFocus and set it to true
<input type="text" data-bind="value:$data, hasFocus: true">
See http://jsfiddle.net/eT3Y8/

It can be done with a custom binding. The harder part in this approach is to not focus on the boxes of the elements that are initially in the list. That's why I needed an extra isNew property, which is false for the already existing elements. I also used jquery to focus :) Fiddle: http://jsfiddle.net/hv9Dx/1/
html:
<h3>Diagnosis Codes<input type="button" value="Add" data-bind="click:AddDiagnosisCode"/></h3>
<div data-bind="foreach:DiagnosisCodes">
<div><input type="text" data-bind="value:value, focusOnCreate:isNew()"/>
</div>
</div>
js:
var Record = function(value, isNew){
var self = this;
self.value = ko.observable(value);
self.isNew = ko.observable(isNew || false);
}
var VM = function() {
var self = this;
self.DiagnosisCodes = ko.observableArray([
new Record("2345"),
new Record("6789")]);
self.enableFocus = ko.observable(true);
self.AddDiagnosisCode = function(){
self.DiagnosisCodes.push(new Record("", true));
}
}
ko.bindingHandlers.focusOnCreate = {
init:function(element, valueAccessor, allBindings, viewModel, bindingContext) {
if(valueAccessor()){
$(element).focus();
}
}
}
ko.applyBindings(new VM());

Related

ICheck plugin not working with sublist in Knockout.js

I have a Knockout model containing a bool observable and a list of objects that contain a bool observable.
I have custom binding iCheckedSys, for the bool in the model, to work with iCheck, and custom binding iCheckedPrimary to work with iCheck on the bool in the objects in the model's list.
iCheckedSys functions correctly, and valueAccessor() returns observable.
However in the list, valueAccessor() returns false in iCheckedPrimary.
If I just use checkbox for the list objects, it works just fine.
How can I have iCheck work with the list objects?
Thanks much.
<div class="form-horizontal no-margin form-border" data-bind="UserViewModel">
<label>
<input type="checkbox" data-bind="iChecked: IsSysAdmin">
</label>
<tbody data-bind="foreach: RolesList">
<td><input type="checkbox" data-bind="checked: IsPrimary" /></td>
</tbody>
</div>
<script>
var Role = function () {
var self = this;
self.ID = ko.observable();
self.IsPrimary = ko.observable();
};
var UserViewModel = function () {
var self = this;
self.ID = ko.observable(#Html.Raw(Json.Encode(Model.ID)));
self.IsSysAdmin = ko.observable(#Html.Raw(Json.Encode(Model.IsSysAdmin)));
self.RolesList = ko.observableArray();
ko.bindingHandlers.iChecked = {
init: function (element, valueAccessor) {
$(element).iCheck({
checkboxClass: "icheckbox_minimal-green",
radioClass: "iradio_minimal-green", increaseArea: "20%"
});
$(element).on('ifChanged', function () {
var observable = valueAccessor();
observable($(element)[0].checked);
});
},
update: function (element, valueAccessor) {
var value = ko.unwrap(valueAccessor());
if (value) {
$(element).iCheck('check');
} else {
$(element).iCheck('uncheck');
}
}
};
};
var viewModel = new UserViewModel();
ko.applyBindings(viewModel);
</script>
I realized that regardless of with record I changed iCheckedPri in, only the last record in viewModel.RolesList was being passed in valueAccessor. So the current record and it's value wasn't being accessed.
I don't know if this is the proper way...
but instead of the value, I passed iCheck with the record object itself and updated it with the current checkbox value, and updated iCheck (check/uncheck) with the object value.
<td><input type="checkbox" data-bind="iCheckedPri: $rawData"></td>
ko.bindingHandlers.iCheckedPri = {
$(element).on('ifChanged', function (event) {
var observable = valueAccessor();
observable = $(element)[0].checked;
valueAccessor().IsPrimary = this.checked;
});
},
update: function (element, valueAccessor) {
var value = ko.unwrap(valueAccessor());
if (value.IsPrimary) {
$(element).iCheck('check');
} else {
$(element).iCheck('uncheck');
}
}
};

Datetimepicker binding in knockout.js not working

I have a view with a datetimepicker. I am not able to bind it in Knockout. Since the datetime picker end of the day has a textbox, I bind it the same way as other textboxes(which works fine) and assume it to contain the datetime value in text format but it does not get binded.
<div class='input-group date' id='datetimepicker1'>
<input type="text" class="form-control" id="estimatedTime" data-bind="value: estimatedTime">
<span class="input-group-addon">
<span class="glyphicon glyphicon-calendar"></span>
</span>
</div>
<input type="number" class="form-control" id="estimatedAmt" data-bind="value: estimatedAmount">
This is my script which does not work for datetimepicker(estimatedTime) but works for a normal textbox(EstimatedAmount).
var Model = function () {
var self = this;
self.estimatedTime= ko.observable(initialData.estimatedTime);
self.estimatedAmount= ko.observable(initialData.EstimatedAmount);
self.update = function () {
});
}
ko.applyBindings(new Model());
Why is there a difference and what is the easiest way to fix it?
You need a binding handler to incorporate another DOM-manipulating entity into Knockout. See this datetimepicker fiddle for an example.
ko.bindingHandlers.datepicker = {
init: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
//initialize datepicker with some optional options
var options = {
format: 'DD/MM/YYYY HH:mm',
defaultDate: valueAccessor()()
};
if (allBindingsAccessor() !== undefined) {
if (allBindingsAccessor().datepickerOptions !== undefined) {
options.format = allBindingsAccessor().datepickerOptions.format !== undefined ? allBindingsAccessor().datepickerOptions.format : options.format;
}
}
$(element).datetimepicker(options);
var picker = $(element).data('datetimepicker');
//when a user changes the date, update the view model
ko.utils.registerEventHandler(element, "dp.change", function (event) {
var value = valueAccessor();
if (ko.isObservable(value)) {
value(event.date);
}
});
var defaultVal = $(element).val();
var value = valueAccessor();
value(moment(defaultVal, options.format));
},
update: function (element, valueAccessor) {
var widget = $(element).data("datepicker");
//when the view model is updated, update the widget
if (widget) {
widget.date = ko.utils.unwrapObservable(valueAccessor());
if (widget.date) {
widget.setValue();
}
}
}
};

2 states roundable numeric text box with knockoutjs

I want to have an html numeric text box with 2 states, when focused, it has to show all decimal places, and when focus is lost, only show 2 decimals. I've almost achieved it.
HTML:
<input data-bind="attr: { 'data-numericvalue': valueToRound}" class="numerictextbox"
type="number"/>
Javascript:
var viewModel = {
valueToRound: ko.observable(7.4267),
};
//NUMERIC TEXTBOX BEHAVIOUR
$('.numerictextbox').focusout(function () {
$(this).attr("data-numericvalue", this.value); //this line does not update the viewModel
this.value = parseFloat($(this).attr("data-numericvalue")).toFixed(2);
});
$('.numerictextbox').focusin(function () {
if ($(this).attr("data-numericvalue") !== undefined) this.value = $(this).attr("data-numericvalue");
});
ko.applyBindings(viewModel);
Jsfiddle: https://jsfiddle.net/7zzt3Lbf/64/
But my problem is that when focusout occurs, it doesn't update bound property, viewModel in this case. This is a simplified version of my code, so I want it to be generic for a lot of properties in my real scenario.
You're mixing in too much jQuery :)
Knockout has event bindings and a hasFocus binding to deal with UI input.
In the example below I've made a viewmodel that has a hidden realValue observable which stores the unmodified input. The displayValue limits this number to a 2 digit number when showDigits is false.
I've used hasFocus to track whether we want to show the whole number: it's linked to showDigits.
var ViewModel = function() {
this.showDigits = ko.observable(true);
var realValue = ko.observable(6.32324261);
this.displayValue = ko.computed({
read: function() {
return this.showDigits()
? realValue()
: parseFloat(realValue()).toFixed(2);
},
write: realValue
}, this);
};
ko.applyBindings(new ViewModel());
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<input data-bind="value: displayValue, hasFocus: showDigits" type="number"/>
Edit: After comment that a computed is too much extra code: here's how to wrap the computed logic in a reusable extender:
ko.extenders.digitInput = function(target, option) {
var realValue = target,
showRealValue = ko.observable(false),
displayValue = ko.computed({
read: function() {
return showRealValue()
? realValue()
: parseFloat(realValue()).toFixed(2);
},
write: realValue
}, this);
displayValue.showRealValue = showRealValue;
return displayValue;
};
var ViewModel = function() {
this.value1 = ko.observable(6.452345).extend({ digitInput: true });
this.value2 = ko.observable(4.145).extend({ digitInput: true });
};
ko.applyBindings(new ViewModel());
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<input data-bind="value: value1, hasFocus: value1.showRealValue" type="number"/>
<input data-bind="value: value2, hasFocus: value2.showRealValue" type="number"/>

How to applyBindings and keep input values with Knockout JS?

I'm building a HTML/KnockoutJS application. My webserver returns a form with input fields with information. When I new up my model and do an ko.applyBindings, naturally the input values are overwritten by the model.
Is there a way to do an ko.applyBindings in which the model is automatically loaded with the data of the input fields?
Example: https://jsfiddle.net/KeesCBakker/p7ygq5y2/1/
HTML:
Title: <input data-bind="textInput: title" value="MyTitle" placeholder="Nothing here!" /><br/>
Text: <input data-bind="textInput: text" value="MyText" placeholder="Nothing here!" /><br/>
<button id="bind">Bind!</button>
JS:
ko.bindingHandlers.initFromInput = {
init: function(element, valueAccessor) {
valueAccessor()(element.value);
}
};
function Model() {
this.title = ko.observable();
this.text = ko.observable();
}
document.getElementById('bind').onclick = function() {
var model = new Model();
ko.applyBindings(model);
};
You could use a custom binding, that tells Knockout to use the input values as default, like this:
ko.bindingHandlers.initFromInput = {
init: function(element, valueAccessor) {
valueAccessor()(element.value);
}
};
Here's a jsfiddle: http://jsfiddle.net/kv3zras3/3/
EDIT:
With the new binding, your data-binds should look something like this:
<input data-bind="initFromInput: title, value: title" value="MyTitle" placeholder="Nothing here!" />
<input data-bind="initFromInput: text, value:text" value="MyText" placeholder="Nothing here!" />
EDIT:
There's an abit nicer way of achieving this, if you make like binding look like this:
var origValueInput = ko.bindingHandlers.value.init;
ko.bindingHandlers.value.init = function(element, valueAccessor, allBindings) {
if (allBindings.has('initValueFromInput')) {
valueAccessor()(element.value);
}
origValueInput.apply(this, arguments);
};
You can write your data-binds like this:
<input value="MyTitle" data-bind="initValueFromInput, value: title"/>
<input value="MyText" data-bind="initValueFromInput, value: text"/>
Here's a fiddle: https://jsfiddle.net/yy51kok5/
I've ended up improving the answer from clean_coding. Add the following anonymous method to a script after loading KnockoutJS. It will reroute both textInput and value handlers.
(function () {
var z = ko.bindingHandlers.textInput.init;
ko.bindingHandlers.textInput.init = function (element, valueAccessor, allBindings) {
if (allBindings.has('initWithElementValue')) {
valueAccessor()(element.value);
}
z.apply(this, arguments);
};
var y = ko.bindingHandlers.value.init;
ko.bindingHandlers.value.init = function (element, valueAccessor, allBindings) {
if (allBindings.has('initWithElementValue')) {
valueAccessor()(element.value);
}
y.apply(this, arguments);
};
}())
It can be used, by specifying it after the textInput or value declaration:
<input type="text" data-bind="textInput: title, initWithElementValue" />
Ended up creating a plugin for Knockout. Added it to GitHub as well.
<script type="text/javascript" src="https://cdn.rawgit.com/KeesCBakker/KnockoutAutomaticFormValueBinding/master/knockout-automatic-form-value-binding-1.0.min.js"></script>
Include the plugin
Set ko.automaticFormValueBinding = true;
Bind ko.applyBindings({yourmodel});
More info at Git: https://github.com/KeesCBakker/KnockoutAutomaticFormValueBinding

Convert Javascript onkeypress to knockoutjs to call on enter

I am trying to do everthing I can in KnockoutJS however I am having a hard time getting this to convert to knockoutjs.
I have an input box that upon enter press I need to call addInputName(). This is kind of the old school way I think to do it. Is there a way to do this all in knockout?
<input id="inputName" onkeypress="addInputName(this, event);" />
<input id="addInputName" type="button" data-bind="event: { click: addInputName }" value="Add" />
self.addInputName = function (inputElement, event) {
if (event.keyCode == 13) {
$('#addInputName').click();
}
};
// View
<input id="inputName" data-bind="value: name, enterKey: addInputName" />
<input id="addInputName" type="button" data-bind="click: addInputName" value="Add" />
// ViewModel
function ViewModel() {
var self = this;
self.name = ko.observable();
self.names = ko.observableArray();
self.addInputName = function () {
self.names.push(self.name());
self.name("");
};
}
// Custom Binding
ko.bindingHandlers.enterKey = {
init: function (element, valueAccessor, allBindings, data, context) {
var wrapper = function (data, event) {
if (event.keyCode === 13) {
valueAccessor().call(this, data, event);
}
};
ko.applyBindingsToNode(element, { event: { keyup: wrapper } }, context);
}
};
Custom Bindings #20:05
Look into Custom Bindings. It's an invaluable tool to help get UI logic out of your ViewModel's business logic.
Why not just wrap the inputs inside a form? Then you can change your HTML to
<form data-bind="submit: addInputName">
<input id="inputName" type="text" data-bind="value: name" />
<input id="addInputName" type="submit" value="Submit" />
</form>
Then your KO viewmodel looks something like
var ViewModel = function()
{
var self = this;
self.name = ko.observable();
self.addInputName = function() {
// do stuff
}
}

Categories

Resources