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
Related
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');
}
}
};
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();
}
}
}
};
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());
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
}
}
I am playing around with Knockout.js and created this simple example: http://jsfiddle.net/JcTxT/30/
<div id="term_grp" data-role="fieldcontain"><a>Semester:</a>
<fieldset id="term_fields" data-role="controlgroup" data-type="horizontal">
<input type="radio" name="term" id="ss" value="ss" data-bind="checked: term" />
<label for="ss">Sommersemester</label>
<input type="radio" name="term" id="ws" value="ws" data-bind="checked: term" />
<label for="ws">Wintersemester</label>
</fieldset>
Term is <span data-bind="text: pommes"></span>
var aResult = {
term: ko.observable("ss"),
pommes: "TEST"
};
$(document).on('pagebeforeshow', '#mainPage', function () {
ko.applyBindings(aResult);
});
I expected one of the radio button to be checked (the one with the value "ss" but this is not the case. Does anyone know, why?
Cheers
It works, if you use:
$(function () {
ko.applyBindings(aResult);
});
And turn off jquery mobile.
I tried in your jsfiddle.
If you need jquery mobile, this link works:
http://www.codesizzle.com/jquery-mobile-radio-with-knockout-js/
OK, what needs to be done?
Add another event handler and add it to the binding:
var aResult = {
term: ko.observable("ws"),
pommes: "TEST2"
};
ko.bindingHandlers.mobileradio = {
init: function (element, valueAccessor) {},
update: function (element, valueAccessor) {
var value = valueAccessor();
var valueUnwrapped = ko.utils.unwrapObservable(value);
if (valueUnwrapped == $(element).val()) {
$(element).prop("checked", "true").checkboxradio("refresh");
} else {
$(element).removeProp("checked").checkboxradio("refresh");
}
}
};
$(function () {
ko.applyBindings(aResult);
});
Working fiddle: http://jsfiddle.net/JcTxT/35/