Reputation: 480
I have an ASP .NET MVC application, additonally I am using Knockout 2.0.0. I created a partial view which I would like to render to the page using knockout. The partial needs to be rendered within a Knockout foreach statement. I am unable to get the knockout HTML binding to work, and so I'm currently using a hack to put the html into the div using JQuery.
There is a lot of html on this page, so it's not possible to post all of the source code, so I will try and post the pertinent code:
<div data-bind="foreach:issues">
@* SNIP - A lot of other html here*@
<div id="myPartialDiv" data-bind="html: $parent.getHtml(issueId())">
</div>
</div>
Further down I have the following javascript function on my KO View Model (I have commented out my hack and included the code that returns HTML):
var getHtml = function (issueId) {
var baseUrl = '@Url.Action("GetHtmlAction","MyController")';
$.ajax(
{
type: "POST",
url: baseUrl,
data: "&issueId=" + issueId,
success: function (data) {
//$('#mypartialDiv').html(data);
return data;
},
error: function (req, status, error) {
//$('#myPartialDiv').html('Something went wrong.');
return 'Something went wrong.'
},
dataType: "text"
});
}
The code above results in no data being rendered to the page. USing Chrome debug tools, I see that there are no javascript errors occuring, and knockout is simply not binding the html of the div to the results returned from the getHtml function.
What am I doing wrong?
Thanks
Upvotes: 4
Views: 3540
Reputation: 15999
As Miroslav Popovic explains, the problem is that the AJAX request is asynchronous, so the return data
is ignored and there is no return value from your call to getHtml
.
I would suggest using a custom binding that handles the asynchronous HTML loading (I've put a working example here).
This works by taking 2 parameters to the asyncHtml: a function to call that takes a success callback as it's final parameter (plus any other parameters) and an array of the parameters that need to be passed to that function.
<div id="myPartialDiv" data-bind="asyncHtml: { source: getHtml, params: [123] }">Loading...</div>
The custom binding then grabs these values, concats a custom callback onto the parameters that are passed to it, and calls the specified function:
ko.bindingHandlers.asyncHtml = {
init: function(element, valueAccessor) {
var value = ko.utils.unwrapObservable(valueAccessor());
var parameters = value.params.concat([function(data) {
$(element).html(data);
}]);
value.source.apply(null, parameters);
}
};
Finally we can re-implement our view model HTML-retrieving method to make the POST call and invoke the new success handler:
var ViewModel = function() {
this.getHtml = function(issueId, callback) {
$.ajax(
{
type: "POST",
url: "/echo/html/",
data: {
html: "<p>server response " + issueId + "</p>",
delay: 1
},
success: callback,
dataType: "text"
});
};
};
Note: for this example I am using the jsFiddle echo
to post back a random response
Upvotes: 4
Reputation: 12128
$.ajax
is an asynchronous call. When you call it, the execution will just continue to the next statement in the getHtml
function. Since this is the last statement, the getHtml
function will return undefined
.
About your return data;
... This return is within a success callback function. The data will be result of that function, not the parent getHtml
function. Besides, getHtml
is already completed. You can't return a result from it.
How about having an observable property in your view model called html
, and then find some other means of triggering the getHtml
function (button click, some other success callback, issueId property change...), that will in turn set the 'html' property value. Then you could simply data-bind to that property data-bind="html: html"
.
Upvotes: 3