King_Nothing
King_Nothing

Reputation: 480

Knockout html binding not working (bound to a function calling a partial view) in ASP .NET MVC

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

Answers (2)

Steve Greatrex
Steve Greatrex

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

Miroslav Popovic
Miroslav Popovic

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

Related Questions