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.
Related
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.
I'm trying to implement a custom binding for an accordion-like document layout on a webpage, but I'm encountering an issue I can't easily solve.
Immediately on page load, I am presented with the error:
Uncaught TypeError: Unable to process binding "accordion: function (){return currentAccordionSection }"
Message: undefined is not a function
I have tried declaring my observable as both a function and normally in the data-bind syntax without success. I have initialized my observable with a default value (null) and it has not fixed this issue. Below is my entire ViewModel:
var libraryViewModel = function () {
var self = this;
ko.bindingHandlers.accordion = {
update: function (element, valueAccessor) {
console.log(ko.unwrap(valueAccessor()));
var value = ko.unwrap(valueAccessor());
var section = $(element.text());
//ko.bindingHandlers.css.update(element, function () {
// if (value === section) {
// return 'library-section-active';
// }
//});
//ko.bindingHandlers.css.update($(element).children('i:last-child').get(0), function () {
// if (value === section) {
// return 'fa fa-chevron-up';
// } else {
// return 'fa fa-chevron-down';
// }
//});
}
}
self.currentAccordionSection = ko.observable(null);
self.updateAccordionSection = function (section) {
self.currentAccordionSection(section);
}
}
Some of the code above is commented out as it is not relevant to the problem at hand and I have disabled it to implement a reduced test case to narrow down the problem. Here is my binding declaration:
<h2 class="library-header" data-bind="accordion: currentAccordionSection, click: updateAccordionSection.bind('Text')">
What exactly am I doing wrong?
The problem is this line:
var section = $(element.text());
as per knockout's documentation
element — The DOM element involved in this binding
text is a jQuery function not a DOM function so I think you are looking for something like:
$(element).text() or $($(element).text()) instead? I'd assume the former since it makes more sense.
As for the nested binding handler I'm not sure why that is in the viewmodel since it's exposed on the knockout global object you're not protecting yourself from anything just making your code more unreadable. They are designed to be resuable so you can use them with different viewModels
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.
I'm trying to get my custom binding to work with both observables and plain objects. I followed the answer in this question:
writeValueToProperty isn't available
However, if I look at the object returned if I execute the allBindingsAccessor, the property '_ko_property_writers' is undefined.
Does anyone know if this has changed at all in version 3 of knockout?
edit
Sorry I should have stated, I am trying to 'write' the value back to the model, in an observable agnostic way
This was helpful for me:
ko.expressionRewriting.twoWayBindings.numericValue = true;
ko.bindingHandlers.numericValue = {
...
}
It is defined after specifying binding as two-way.
So I can use something like that inside my custom binding:
ko.expressionRewriting.writeValueToProperty(underlying, allBindingsAccessor, 'numericValue', parseFloat(value));
writeValueToProperty is defined internally as:
writeValueToProperty: function(property, allBindings, key, value, checkIfDifferent) {
if (!property || !ko.isObservable(property)) {
var propWriters = allBindings.get('_ko_property_writers');
if (propWriters && propWriters[key])
propWriters[key](value);
} else if (ko.isWriteableObservable(property) && (!checkIfDifferent || property.peek() !== value)) {
property(value);
}
}
The standard way to do this is with ko.unwrap as described here: http://knockoutjs.com/documentation/custom-bindings.html
For example:
ko.bindingHandlers.slideVisible = {
update: function(element, valueAccessor, allBindings) {
// First get the latest data that we're bound to
var value = valueAccessor();
// Next, whether or not the supplied model property is observable, get its current value
var valueUnwrapped = ko.unwrap(value);
// Grab some more data from another binding property
var duration = allBindings.get('slideDuration') || 400; // 400ms is default duration unless otherwise specified
// Now manipulate the DOM element
if (valueUnwrapped == true)
$(element).slideDown(duration); // Make the element visible
else
$(element).slideUp(duration); // Make the element invisible
}
};
In that example valueUnwrapped is correct whether the user bound to an observable or a normal object.
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.