knockout js bind with datetimepicker gives an exception - javascript

I think I can easily bind a date data with jquery ui calendar and knockout.js thanks to this answer.
Now I need to bind a date data as well as its time. Of course, I can use timepicker. But I am not sure how I can bind its data with knockout.js. I expected it'd be similar to datepicker so I made following script
ko.bindingHandlers.datetimepicker = {
init: function (element, valueAccessor, allBindingsAccessor) {
//initialize datepicker with some optional options
var options = allBindingsAccessor().datetimepickerOptions || {};
$(element).datetimepicker(options);
//handle the field changing
ko.utils.registerEventHandler(element, "change", function () {
var observable = valueAccessor();
observable($(element).datetimepicker("getDate"));//****
});
//handle disposal (if KO removes by the template binding)
ko.utils.domNodeDisposal.addDisposeCallback(element, function () {
$(element).datetimepicker("destroy");
});
},
update: function (element, valueAccessor) {
var value = ko.utils.unwrapObservable(valueAccessor()),
current = $(element).datetimepicker("getDate");
if (value - current !== 0) {
$(element).datetimepicker("setDate", value);
}
}
};
But when I ran the script, I get an error in line of //**** in javascript
TypeError: observable is not a function
But I can't find what I did wrong here.

This particular error is due to the observable = valueAccessor() line. You are assigning to observable the value of valueAccessor by adding the () to the end. In order to pass a value to observable in this way, you would need to write instead:
var observable = valueAccessor;
Otherwise, observable is not an 'observable function'.

I just found following code is working. As few open source code do, this addon is not very stable and will call change event with null observable sometimes. So I made the code to catch the exception and move on.
ko.bindingHandlers.datetimepicker = {
init: function (element, valueAccessor, allBindingsAccessor) {
//initialize datepicker with some optional options
var options = allBindingsAccessor().datetimepickerOptions || {};
$(element).datetimepicker(options);
//handle the field changing
ko.utils.registerEventHandler(element, "change", function () {
var observable = valueAccessor();
try {
observable($(element).datetimepicker("getDate"));//****
}
catch(ex) {}
});
//handle disposal (if KO removes by the template binding)
ko.utils.domNodeDisposal.addDisposeCallback(element, function () {
$(element).datetimepicker("destroy");
});
},
update: function (element, valueAccessor) {
var value = ko.utils.unwrapObservable(valueAccessor()),
current = $(element).datetimepicker("getDate");
if (value - current !== 0) {
$(element).datetimepicker("setDate", value);
}
}
};

Replace this line
var observable = valueAccessor();
With
var xxxx= valueAccessor();
Because you cannot use the observable, because it is reserved keyword in knockout.
Also, you may get error somewhere in future if you use observable as variable name.

Related

Failed to read the 'selectionStart' property from 'HTMLInputElement':

I'm using binding handler.if i remove this code my code is saved.but if i use
this code it will throw error.
Uncaught InvalidStateError: Failed to read the 'selectionStart' property from 'HTMLInputElement': The input element's type ('checkbox') does not support selection.
ko.bindingHandlers.wysiwyg = {
init: function (element, valueAccessor, allBindingsAccessor) {
debugger;
var options = allBindingsAccessor().wysiwygOptions || {};
var value = ko.utils.unwrapObservable(valueAccessor());
//value = value.text();
//var v = value[0].childNodes[0].data;
var $e = $(element);
$.extend(true, {
initialContent: value
}, options);
$e.wysiwyg(options);
//handle the field changing
function detectFn() {
var observable = valueAccessor();
var newvalue = $e.wysiwyg("getContent");
observable(newvalue);
}
var current = $e.wysiwyg('document');
var timer;
current.bind({
keyup: function () {
clearTimeout(timer);
timer = setTimeout(detectFn, 1000);
}
});
//handle disposal (if KO removes by the template binding)
ko.utils.domNodeDisposal.addDisposeCallback(element, function () {
$e.wysiwyg('destroy');
});
},
update: function (element, valueAccessor) {
var value = ko.utils.unwrapObservable(valueAccessor());
$(element).wysiwyg("setContent", value);
ko.bindingHandlers.value.update(element, valueAccessor);
}
};
You have an input element of type="checkbox" in your HTML, where the Javascript code expects a type="text".
A checkbox has no text, thus it cannot have any selection. But your code tries to access the non-existent property selectionStart.
Please have a look at https://jsfiddle.net/u99f0q1j/ to see the issue demonstrated.
As you did not post your HTML, it is hard to see what is causing the error.
But in your browser dev tools, you should be able to click on the line number next to your "Uncaught InvalidStateError" message in order to see the Javascript line that tried to access the selectionStart property on your checkbox.
There are some DOM elements whose value cannot be obtained by using jQuery's .val() or the DOM's .value. One example is the HTML5 number field; another is the checkbox.
I'm not certain where this exception is occurring - you can easily find out by commenting-out lines in your binding handler and using trial-and-error. I could copy your binding handler into a test page but I don't know what HTML you're binding it to. However, I suspect you'll find that you have to do some element-type checking, and implement different logic for different types of DOM element.
My suggestion would be to change your init function to
init: function(element, valueAccessor, allBindingsAccessor) {
var options = allBindingsAccessor().wysiwygOptions || {},
value = ko.utils.unwrapObservable(valueAccessor());
/*
the rest of your init function, commented-out
*/
}
Verify that this doesn't throw an exception, then gradually uncomment the remaining script, block by block, reloading your page after each modification until you find the cause of the exception.
$.ajax({
processData: false,
contentType: false,
type:"POST",
add processData =false and contentType = false. This works for me.

Custom Binding not working updating from Knockout 2.3 to 3.3

My Windows 8.1 App was working fine on Knockout 2.3 but after updating to 3.3 it seems like I get the wrong Context in my custom binding.
First here is how I apply binding for individual elements in the command bar of my app :
var cmdArray = [];
var commandIsRunning = function() {
return _.any(cmdArray, function(command) {
return command.isRunning();
});
};
_.each(_bottomCommands, function (row) {
if(row.command) {
// command wrapper
var commandWrapper = ko.command({
action: function() {
var rowCommand = row.command();
if (rowCommand) {
return rowCommand();
}
return WinJS.Promise.as();
},
canExecute: function() {
var rowCommand = row.command();
if (rowCommand) {
return rowCommand.canExecute() && !commandIsRunning();
}
return false;
}
});
cmdArray.push(commandWrapper);
//Bind the command
var element = document.querySelector('#' + row.id);
if (element) {
element.setAttribute('data-bind', 'command: $data');
ko.applyBindings(commandWrapper, element);
}
}
});
Here is my custom binding code
ko.bindingHandlers.command = {
init: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
var command = ko.utils.unwrapObservable(valueAccessor());
ko.bindingHandlers.click.init.call(this, element, ko.observable(command), allBindingsAccessor, viewModel, bindingContext);
},
update: function (element, valueAccessor, allBindingsAccessor) {
var command = ko.utils.unwrapObservable(valueAccessor());
ko.bindingHandlers.enable.update.call(this, element, command.canExecute, allBindingsAccessor);
}
};
The problem is in:
ko.bindingHandlers.enable.update.call(this, element, command.canExecute, allBindingsAccessor);
canExecute is undefined which I think is because I am not getting the right context in the init and update handlers. So what am I doing wrong in my code? Again the code was working in Knockout 2.3 , so could it be a Knockout issue?
UPDATE:
I created jsFiddle to show the problem. It contains the definition for ko.command because I thought that could be the cause of problem
JSFiddle
The error is caused because Knockout 3.x binds to functions differently. In 2.x, you could bind directly to a function, but in 3.x, Knockout calls the function to get the viewmodel. You can still bind to a function in Knockout 3.x, but you'll need to wrap it in an observable or in another function.
ko.applyBindings(function() { return commandWrapper }, element);
https://jsfiddle.net/mbest/nrb97g7e/38/

KnockoutJS custom binding scoping issue

I have a custom binding that overrides knockout's click handler like so:
var originalInit = ko.bindingHandlers.click.init,
originalUpdate = ko.bindingHandlers.click.update;
ko.bindingHandlers.click = {
init: function(element, valueAccessor, allBindingsAccessor, viewModel, context) {
var wrappedValueAccessor = function() {
return function(data, event) {
var disabled = allBindingsAccessor.get('disabled');
var clickResult = valueAccessor().call(viewModel, data, event);
if (clickResult && typeof clickResult.always === "function") {
$(element).attr('disabled','disabled');
clickResult.always(function(){
$(element).removeAttr('disabled');
});
}
};
};
originalInit(element, wrappedValueAccessor, allBindingsAccessor, viewModel, context);
},
update: originalUpdate
};
Find the fiddle here: http://jsfiddle.net/92q5vgfp/
The problem is when I try to access allBindingsAccessor inside the click from the chrome debugger, it's not available.
However, if i have a console.log(allBindingsAccessor), chrome's debugger can see it.
Update So, while I was writing this, we tried a random thing, which was to assign the function to a variable before returning it. That worked. Don't know why or how.
var wrappedValueAccessor = function() {
var test = function(data, event) {
...
};
return test;
};
So that's my question, WHY would assigning the function to a local var and returning it work but not directly returning it? Is this a bug in chrome or expected (somehow)?
In the linked snippet allBindingsAccessor is not accessed inside the inner function so v8 simply optimizes it out and don't add to the function closure. See crbug.com/172386 for more details.

Select 2 deferred Loading data with knockout

I've a knockout.js view which shows 20 rows of data. each row has a select 2 control bound with knockout.js. (Below you can see my bindinghandler)
Now each select 2 points to the same array of items . This array has about 10.000 entries. This results in a slowdown of the whole page (about 2-3 seconds freezetime)
I'm thinking about to only load the options when the user clicks the row. like this:
self.setSelectedRow = function (entry) {
entry.options(allOptions);
var value = entry.intialValue;
entry.StationdId(value);
};
After this the select 2 is expandable and i can choose options, but the initialvalue is not applied.
Any hints on what i'm doing wrong?
Binding handler:
ko.bindingHandlers.select2 = {
init: function (el, valueAccessor, allBindingsAccessor, viewModel) {
ko.utils.domNodeDisposal.addDisposeCallback(el, function () {
$(el).select2('destroy');
});
var allBindings = allBindingsAccessor(),
select2 = ko.utils.unwrapObservable(allBindings.select2);
$(el).select2(select2);
}
};
Binding handlers usually have two functions:
An init function that is called when the binding is created (note that it can be called more than once as it is called each time you create/recreate the binding -- example: when node is in an if binding). This function should contain the code to setup the binding (which ou did well)
An update function which is called every time the observables inside your binding markup change. Note that this function is also called on init (right after the init function) so in certain cases you won't need an init function.
More info in the custom binding doc.
In your case, I think the init function is fine.
The problem is nothing is set up to handle the changes on your observables.
You can add an update function that would look like this (untested):
ko.bindingHandlers.select2 = {
init: function (el, valueAccessor, allBindingsAccessor, viewModel) {
/* your code is fine */
},
update: function (el, valueAccessor, allBindingsAccessor, viewModel) {
var allBindings = allBindingsAccessor(),
select2 = ko.utils.unwrapObservable(allBindings.select2);
$(el).select2(select2); //update the select2
}
};
Unless you are using a very outdated knockout version, I think your binding syntax is wrong.
This part is wrong:
var allBindings = allBindingsAccessor(),
select2 = ko.utils.unwrapObservable(allBindings.select2);
If you read http://knockoutjs.com/documentation/custom-bindings.html
the correct way to use allBindingsAccessor (it should be named allBindings anyway) is
var select2 = allBindingsAccessor.get('select2') || {};
BUT even this is unnecessary, valueAccessor gives you what is under current binding (select2).
So just try this:
ko.bindingHandlers.select2 = {
init: function (el, valueAccessor, allBindingsAccessor, viewModel) {
ko.utils.domNodeDisposal.addDisposeCallback(el, function () {
$(el).select2('destroy');
});
$(el).select2(ko.unwrap(valueAccessor()));
}
};

Spinning into trouble with knockout-utils

I have this spinner.
When set it is ok, control shows the right value, no error message, but if I change it, every time I get this error message:
"TypeError: observable is not a function"
in this line:
observable($(element).spinner("value"));
This is a line in my customize binding, which goes like this:
ko.bindingHandlers.spinner = {
init: function (element, valueAccessor, allBindingsAccessor) {
//initialize datepicker with some optional options
var options = allBindingsAccessor().spinnerOptions || {};
$(element).spinner(options);
//handle the field changing
ko.utils.registerEventHandler(element, "spinchange", function () {
//var observable = valueAccessor();
var observable = ko.utils.unwrapObservable(valueAccessor());
observable($(element).spinner("value"));
});
//handle disposal (if KO removes by the template binding)
ko.utils.domNodeDisposal.addDisposeCallback(element, function () {
$(element).spinner("destroy");
});
},
update: function (element, valueAccessor) {
var value = ko.utils.unwrapObservable(valueAccessor()),
current = $(element).spinner("value");
if (value !== current) {
$(element).spinner("value", value);
}
}
};
If I swap the line that fails with the one that is commented out:
var observable = valueAccessor();
it works fine.
Why does the first one fail?
In a custom binding the valueAccessor is a function which returns of the value of the binding, so what you have on the "right side" in the binding.
So in the case of data-bind="spinner: yourValue you will get the content of the yourValue property.
If your yourValue is ko.observable then in var observable = valueAccessor(); the observable will contain your actual observable function from yourValue what you can set it now to any value with:
var observable = valueAccessor();
observable($(element).spinner("value"));
So this is the correct usage in this case because you need to reference and use your observable function and not its value.
However if you use ko.utils.unwrapObservable(valueAccessor()); then its is automatically unwraps your observable so you will end with a value not a function. So this call is roughly equivalent to write valueAccessor()();
So if your view model looks like this:
vm = {
yourValue: ko.observable(5);
}
When you write:
var observable = ko.utils.unwrapObservable(valueAccessor());
observable($(element).spinner("value"));
observable will contain the value 5 and not a function so you get the above mentioned exception.

Categories

Resources