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();
}
}
}
};
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 use the Bootstrap DatePicker to use the calender. I, have two input box in table with multiple rows. In, each row the user can select start date and end date. Default value of start Date is new Date(). I, have disable the pass date on first load of the screen. I, have a requirement of disable the pass dates based on the user selection.
Here is the Html Code
<tbody data-bind="foreach: responseUserSetUpData().userListViewModel">
<tr>
<td><input class="form-check-input" type="checkbox" data-bind="checked: SelectedUser, visible: !SelectedUser"> <span data-bind="text: FirstName"></span></td>
<td><select class= "form-control" data-bind="options: Roles,optionsText: 'Name',value: SelectedRoleId,optionsValue:'Id',optionsCaption: '-- Select Role --'"></select></td>
<td><input type="text" class="datepicker form-control" data-bind="datePicker: ko.observable(new Date(FormatStartDate)) , textInput : FormatStartDate , attr: {id: UserId}, datePickerOptions: {startDate: new Date()}"></td>
<td><input type="text" class="datepicker form-control" data-bind="datePicker: ko.observable(new Date(FormatEndDate)), textInput: FormatEndDate , attr :{id: UserId}, datePickerOptions: {startDate: new Date(FormatStartDate)}" ></td>
</tr>
</tbody>
Custom Knockout Binding
ko.bindingHandlers.datePicker = {
init: function (element, valueAccessor, allBindingsAccessor, viewModel) {
var unwrap = ko.utils.unwrapObservable;
var dataSource = valueAccessor();
var binding = allBindingsAccessor();
var options = {
keyboardNavigation: true,
todayHighlight: true,
autoclose: true,
daysOfWeekDisabled: [0, 6],
format: 'dd/mm/yyyy'
//startDate: userManagementVM.StartMeetingDate()
};
if (binding.datePickerOptions) {
options = $.extend(options, binding.datePickerOptions);
}
$(element).datepicker(options);
$(element).datepicker('update', dataSource());
$(element).on("changeDate", function (ev) {
var observable = valueAccessor();
if ($(element).is(':focus')) {
$(element).one('blur', function (ev) {
var dateVal = $(element).datepicker("getDate");
observable(dateVal);
});
}
else {
observable(ev.date);
}
});
//handle removing an element from the dom
ko.utils.domNodeDisposal.addDisposeCallback(element, function () {
$(element).datepicker('remove');
});
},
update: function (element, valueAccessor) {
var value = ko.utils.unwrapObservable(valueAccessor());
$(element).datepicker('update', value);
}
};
Screen Shot of the UI
The existing code only disable on first load of the screen. However when user change the value it doesn't disable on the new date selected value.
Can please anyone let me know how can I resolve this issue.
I have a div bound to an observable array like so:
<div data-bind="foreach: currentSelected().Tags " contenteditable="true">
<span data-bind="text:$data"></span>
</div>
I made the div's content editable so that any changes (user input) get reflected in the Tags array, but that isn't working as I assumed. There seems to be no automatic push for observable arrays. My question is, how do I get new values inside the Tags array using binding?
Here is how I am setting currentSelected:
var newBlogEntry = new NewBlogEntry();
var newBlogEntryObservable = new NewBlogEntryObservable(newBlogEntry);
self.currentSelected(newBlogEntryObservable);
The function for NewBlogEntry is as follows:
function NewBlogEntry()
{
return { "Id": 0, "Title": "Title", "Description": "Description", "Tags": [] };
}
I managed to solve the problem by adding an extra input field:
<input type="text" placeholder="new tag" data-bind="value:newTag, valueUpdate: 'afterkeydown', enterkey: addNewTag" id="addTag" />
The binding for the enter key is as follows:
ko.bindingHandlers.enterkey = {
init: function (element, valueAccessor, allBindings, viewModel) {
var callback = valueAccessor();
$(element).keypress(function (event) {
var keyCode = (event.which ? event.which : event.keyCode);
if (keyCode === 13) {
callback.call(viewModel);
return false;
}
return true;
});
}
};
And this is the observable for the newTag:
self.newTag = ko.observable();
self.addNewTag = function () {
if (self.newTag() == '')
alert("Enter something in input field");
else {
var tag = self.newTag();
self.currentSelected().Tags.push(tag);
$("#addTag").val("");
}
}
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
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());