1

I am having a problem in that I am getting a memory leak in IE with Knockout 2.3.0. I have a list of results that are displayed on the screen and that result set has paging capabilities. There is an AJAX call back to the controller to get the next set of paged results. When initially binding the first page to the screen IE has a memory load of about 150Mb but each page adds about 50Mb to 100Mb, this issue is not happening in Chrome or FireFox.

This issue seems to be common and I have looked at many examples, such as: KnockoutJS Memory Leak I tried to copy this model to no avail, this is what my KO script file looks like:

var householdViewModel = ko.observable();

var HouseholdViewModel = function (data) {
    ko.mapping.fromJS(data, mapping, this);    
};

var AccountViewModel = function (data) {
    ko.mapping.fromJS(data, mapping, this);
};

var mapping = {
    'Households': {
        create: function(options) {
            return new HouseholdViewModel(options.data);
        }
    },
    'Accounts': {
        create: function (options) {
            return new AccountViewModel(options.data);
        }
    }
};

I am using MVC framework to do my AJAX call which looks like the following:

@Ajax.ActionLink(">", "PageInfo", new {pageOption = "Next"}, new AjaxOptions {UpdateTargetId = "SearchResults", HttpMethod = "GET", OnBegin = "OnBeginAjaxListManipulation", OnComplete = "OnCompleteAjaxListManipulation"}, new {@class = "textNavigationElement"})

With the callback JS method that looks like this:

    function OnCompleteAjaxListManipulation() {
        //ko.cleanNode(document.getElementById("SearchResults"));
        var HHVM = { Households: ko.mapping.fromJS(@Html.Raw(Json.Encode(Model.Households))) };
        householdViewModel(new HouseholdViewModel(HHVM));
        //ko.applyBindings(householdViewModel, document.getElementById("SearchResults"));
    }

I suppose I need to meantion that I have a Page called Info.cshtml and a partial page called results.cshtml that will display the results of the search. In the info page I make the following call;

    var HHVM = { Households: ko.mapping.fromJS(@Html.Raw(Json.Encode(Model.Households))) };
    householdViewModel(new HouseholdViewModel(HHVM));
    ko.applyBindings(householdViewModel, document.getElementById("SearchResults"));

The search result will be displayed fine for the first page but the second page, nothing displays. If I add a second applyBindings in the JS call back it works but I get the memory leak issue. I can't figure this one out, Any help would be greatly appreciated.

Josh

Community
  • 1
  • 1
Joshy
  • 657
  • 8
  • 20

1 Answers1

0

I fixed it by manually controlling the observable array which is probably the way in which your supposed to do it. Firstly I change my model to be:

var Households = ko.observableArray(HouseholdViewModel);

var HouseholdViewModel = function (data) {
    ko.mapping.fromJS(data, mapping, this);    
};

var AccountViewModel = function (data) {
    ko.mapping.fromJS(data, mapping, this);
};

var mapping = {
    'Households': {
        create: function(options) {
            return new HouseholdViewModel(options.data);
        }
    },
    'Accounts': {
        create: function (options) {
            return new AccountViewModel(options.data);
        }
    }
};

Then changed the way in which the Ajax called were triggered, I removed the UpdateTargetId and decided to control this myself the code looks like this:

@Ajax.ActionLink(">>", "PageInfo", new {pageOption = "Last"}, new AjaxOptions {HttpMethod = "GET", OnBegin = "OnBeginAjaxListManipulation", OnComplete = "OnCompleteAjaxListManipulation", OnSuccess = "BindModel"}, new {@class = "textNavigationElement"}) 

    function BindModel(e) {
        var isBound = !!ko.dataFor(document.getElementById("SearchResults"));
        if (isBound)
            Households.removeAll();

        $.each(e.Households, function (index, value) {
            var hh = new HouseholdViewModel(value);
            Households.push(hh);
        });

        if (!isBound)
            ko.applyBindings(Households, document.getElementById("SearchResults"));
    }

and that's it! The result is that each time the model is bound it will clear the observable array and reads the items and because were binding the observable array to the page we don't need to do any ko.CleanNode and rebind :-)

I hope this can help someone else.

Joshy
  • 657
  • 8
  • 20