Question

I'm having some difficulty transitioning from Knockout's template binding to the Durandal compose binding.

In my old project, I have a list of tabs which can be swapped into center stage by placing them into the "selectedTabSection" observable. The templateId was a property of the sub-view. So in my parent view, I created instances of my child models like this:

self.tabSections([
                    new BasicTabViewModel(self, db),
                    new BiometricTabViewModel(self, db),
                    new ActivityTabViewModel(self, db),
                    new SurveyTabViewModel(self, db),
                    new CommunicationTabViewModel(self, db),
                    new ReferralTabViewModel(self, db),
                    new GoalTabViewModel(self, db),
                    new NcpTabViewModel(self, db),
                    new CriticalValuesViewModel(self, db),
                    new ConditionManagement(self, db)
                ]);

Then when I wanted to show one, I'd place it into the active observable:

self.selectedTabSection(self.tabSections()[0]);

When I change to the compose binding, it seems that Durandal cannot find the associated Views for my ViewModels because I'm binding instances of the models rather than the constructor of the ViewModel itself. In other words,

self.selectedTabSection(BasicTabViewModel);

Finds the appropriate view, whereas

self.selectedTabSection(new BasicTabViewModel(self, db));

does not.

How can I get the viewLocator to understand that I'm passing an instance rather than the ViewModel constructor itself? If I cannot, how do I pass parameters to my child views since they haven't been initialized until they are composed?

EDIT/UPDATE:

It looks like there's something to do with how I've composed my child ViewModels. Durandal appears to have issue when you return an object from the ViewModel's constructor.

This seems to work as expected:

var viewModel = function (parentVm, db) {
    var self = this;
} 

Whereas this:

var viewModel = function(parentVm, db){
     var self = this;
     //public api
     return {};
}

Does not. Something about returning an object from the constructor make DurandalJS get lost when trying to locate the View and also makes a mess of various scopes. I'm considering re-writing my scripts to fit, but this pattern of returning a object from a constructor has served me well for many moons (prior to Durandal) Curious...

Was it helpful?

Solution

The reason this works -

var viewModel = function (parentVm, db) {
    var self = this;
}

Whereas this does not -

var viewModel = function(parentVm, db){
     var self = this;
     //public api
     return {};
}

Is because you are erasing everything that your function did to create an object.

Consider this -

function newModule (path, params){
     var self = this;
     self.modulePath = path;
     self.activationData = params;
}

Now you can create instances of this anonymous function and pass in the parameters to you want to bind your view / view model to.

var theseChildViewModels = ko.observableArray();
var someData = getDataFromSomewhere();
theseViewModels.push(new newModule('viewmodels/myViewModelOne', { data: someData }));
theseViewModels.push(new newModule('viewmodels/myViewModelTwo', { data: someData }));

Now you can bind to these in your parent view like this -

<ul data-bind="foreach: theseChildViewModels">
    <li>
        <!-- ko compose: { model: modulePath, activationData: activationData} -->
        <!-- /ko -->
    </li>
</ul>

And dynamically declare your paths and what data is passed into the activate callback during composition.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top