0

I have a form that contains a main view with a grid and several (bootstrap) modal forms. The modal forms are quite complex and take quite a while to download. Everything on the form is currently bound to a single knockout viewmodel. That viewmodel contains (among other things) arrays of dropdown items and subclasses of observable properties that are bound to the modal form elements.

Currently the entire form and data must download before the user can interact with the page. I'd like to break it down so that the loading flows in the following manner:

  1. Basic form elements and css
  2. VM is instantiated
  3. data for the grid downloads
  4. grid is bound to vm
  5. user can begin to interact
  6. elements of most commonly used modal form download
  7. modal form is bound to the VM
  8. repeat 6 & 7 for all modal forms

I've successfully been able to download the modal forms after the grid is bound as partial views, but now I get an error that "You cannot apply bindings multiple times to the same element." Is there a way to bind my partial views to the same viewmodel sequentially?

Here's my code that binds the base page to the vm. Note that it then calls loadQuoteModal which loads the Quote Modal Form.

$(document).ready(function () {
     var vm = new ClientListViewModel();
     ko.applyBindings(vm, document.getElementById("#baseClient"));
     vm.loadQuoteModal();
});

Here's loadQuoteModal on my vm.

self.loadQuoteModal = function () {
        var d = $.Deferred();
        var xhr = $.get('/Home/QuoteModalForm');
        xhr.done(function (partialViewResult) {
            $("#LoadQuote").html(partialViewResult);
            ko.applyBindings(this, document.getElementById("#quoteDiv"));
            self.quoteLoaded = true;
        });
        return d;
    }

And here's my page.

<div id="baseClient">
    <div class="col-md-12" id="clientHeader">
        //Many elements not relevent
    </div>

    <div class="col-md-12" id="clientDiv">
        //Many elements not relevent
    </div>
</div>

<div id="quoteDiv">
</div>
Steve Wash
  • 986
  • 4
  • 23
  • 50

4 Answers4

1

I'd recommend that you keep the single ViewModel, but not waiting until all data is loaded. The places where you are doing multiple ko.applyBindings, you can have as empty ko.observables, which are only visible once the data/sub models are set inside them. The data for these submodels can be loaded and set asynchronously after page load.

7zark7
  • 10,015
  • 5
  • 39
  • 54
0

Not sure if this is what you are looking for, but you can bind different ViewModels to different areas of your page. Like so:

<div id="customerPanel"></div>
<div id="productsPanel"></div>

ko.applyBindings(new CustomerViewModel(), document.getElementById("customerPanel"));

ko.applyBindings(new ProductsViewModel(), document.getElementById("productsPanel"));

And so on...

PercivalMcGullicuddy
  • 5,263
  • 9
  • 46
  • 65
  • Thanks. I was hoping to avoid multiple ViewModels only because it means a larger refactoring at this point, but I'm starting to think that its going to be neccessary. – Steve Wash Aug 01 '14 at 15:40
0

There's a good article by Ryan Niemeyer discussing this problem. What he suggests to do is to create this simple custom binding:

ko.bindingHandlers.stopBinding = {
    init: function() {
        return { controlsDescendantBindings: true };
    }
};

ko.virtualElements.allowedBindings.stopBinding = true;

And then in your markup you just mark the elements you don't want to bind first time like this:

<div id="baseClient">
    <!-- ko stopBinding: true -->
    <div class="col-md-12" id="clientHeader">
        //Many elements not relevent
    </div>
    <!-- /ko -->
    <!-- ko stopBinding: true -->
    <div class="col-md-12" id="clientDiv">
        //Many elements not relevent
    </div>
    <!-- /ko -->
</div>

In your ajax callback you just run applyBindings for the specific DOM elements, as you did before.

Ilya Luzyanin
  • 7,910
  • 4
  • 29
  • 49
0

Have a look at this fiddle.

    function addDynamicContent() {
        $("#main").append("<div id='dynamic' data-bind='text: fullName'></div>");
        ko.applyBindings(viewModel, $("#dynamic")[0]);
    }

You can use this to create multiple partial views that are loaded dynamically but are still bound to the one view model.

user1958555
  • 26
  • 1
  • 3