I have a question regarding Timeline.js and Knockout.js with ASP.NET MVC 4. I'm hoping that my problem is actually related to improper use of Javascript that someone can spot, as I'm not too sure how many Timeline+Knockout+Datepicker people are out there.
I have a view that contains the following, with a div and an ajax link that ends up populating that div with a partial view.
<div>Starting Order Date: <input data-bind="datepicker: StartDate, datepickerOptions: { maxDate : $.now() }" /></div>
<div id="Status"></div>
<a data-ajax="true" data-ajax-method="GET" data-ajax-mode="replace" data-ajax-update="#Status" href="/Track/Status/1">Blarg</a>
<script src="~/ViewModels/TrackModels.js"></script>
Over in TrackModels.js I have a knockout extender for the datepicker. I removed the 'update' and dom disposal part for brevity, but it's essentially what's in knockoutjs databind with jquery-ui datepicker
ko.bindingHandlers.datepicker = {
init: function (element, valueAccessor, allBindingsAccessor) {
var options = allBindingsAccessor().datepickerOptions || {};
$(element).datepicker(options);
ko.utils.registerEventHandler(element, "change", function () {
var observable = valueAccessor();
observable($(element).datepicker("getDate"));
});
}
};
And the partial view that gets returned by the ajax link and replacing the status div is
@model MyProject.Data.DTO.Timeline
<div id="timeline-embed"></div>
<script type="text/javascript">
var timeline_config = {
width: '100%',
height: '500',
source: {
"timeline":
@Html.Raw(@Json.Encode(Model))
}
}
</script>
<script type="text/javascript" src="~/scripts/js/storyjs-embed.js"></script>
Now, the problem! Prior to clicking the ajax link, the datepicker properly works to update the knockout model. Once you click the ajax link, the timeline does appear properly and displays what it should. However, my datepicker starts failing with an error:
TypeError: $(...).datepicker is not a function
observable($(element).datepicker("getDate"));
So, somehow, the element that I'd originally bound is losing that it's a datepicker. Can....can anyone tell me why? Is there something wrong with sticking the storyjs-embed in a partial? I don't get it...stepping through in Firebug, the datepicker is still throwing its change event handler, but for some reason it's like the element is losing that it's a datepicker. Thank you for any help!
EDIT:
Unfortunately Jeroen, that wasn't quite it (I tried the suggested change, but it still got the same error). However, I have found that if I change my init section to the following
init: function(element, valueAccessor, allBindingsAccessor) {
var options = allBindingsAccessor().datepickerOptions || {},
$el = $(element);
$el.datepicker(options);
ko.utils.registerEventHandler(element, "change", function () {
var observable = valueAccessor();
observable($el.datepicker("getDate"));
});
ko.utils.domNodeDisposal.addDisposeCallback(element, function() {
$el.datepicker("destroy");
});
},
And completely comment out my update section of:
update: function(element, valueAccessor) {
var value = ko.utils.unwrapObservable(valueAccessor()),
$el = $(element);
if (String(value).indexOf('/Date(') == 0) {
value = new Date(parseInt(value.replace(/\/Date\((.*?)\)\//gi, "$1")));
}
var current = $el.datepicker("getDate");
if (value - current !== 0) {
$el.datepicker("setDate", value);
}
}
Then my datepicker doesn't error with that same error after opening the Ajax link. If I left the init section as I originally had it (without the $el), it does still get the error.