1

I am trying to use a single widget on multiple screens. However I am having an issue where when I load the second instance of this widget on a different page and then try to use the first widget again on the first page the tabs inside of this widget no longer navigate (The bootstrap active class does switch to the correct tab just the content is not switching).

After a while of debugging I realized that when I try to navigate in the widget that doesn't work, it is actually navigating the tabs on the "working" widget. It seems to be that the 2 views and the 1 viewmodel are not communication properly. I think this is due to Durandal thinking that it is on the second instance of this widget (not knowing properly how to distinguish the two).

I am using bootstrap v3.0.1 tabs and I've gone through and made all of the tab ids in the widget specific to each page that loads this widget.

Sadly I am out of ideas and have no idea how to fix this issue. Any help that can be provided is extremely appreciated.

Is there someway that I can specify a widget to not have cachedViews? The binding in the code below is not working.

Widget Implementation:

Page 1: 

<div class="modal fade" id="id1" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
    <div class="modal-dialog">
        <div class="modal-content">
            <div data-bind="widget: {kind: 'productDetails', options: productDetailsWidgetOptions, cacheViews:false }"></div>
        </div>
    </div>
</div>

Page 2:  

<div class="modal fade" id="id2" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
    <div class="modal-dialog">
        <div class="modal-content">
            <div data-bind="widget: { kind: 'productDetails', options: productDetailsWidgetOptions, cacheViews:false }"></div>
        </div>
    </div>
</div>

This is the viewmodel code that gets returned in the widget:

 var vm = {

    //#region Durandal's callbacks
    activate: activate,
    attached: attached,
    deactivate: deactivate,
    canDeactivate: canDeactivate,
    //#endregion

    //#region Properties
    title: title,
    name: name,
    productCode: productCode,
    description: description,
    //#endregion

    //#region Commands
    saveProductDetailsCommand: saveProductDetailsCommand,
    cancelProductDetailsCommand: cancelProductDetailsCommand,
    //#endregion

    }

    return vm;

I tried to change the code to return a function to not make it a singleton. This gave me the exact same result would there be anything else I could be missing or doing incorrectly?

 var vm = function(){
    //#region Durandal's callbacks
    this.activate = activate;
    this.attached = attached;
    this.deactivate = deactivate;
    this.canDeactivate = canDeactivate;
    //#endregion

    //#region Properties
    this.title = title;
    this.name = name;
    this.productCode = productCode;
    this.description = description;
    //#endregion

    //#region Commands
    this.saveProductDetailsCommand = saveProductDetailsCommand;
    this.cancelProductDetailsCommand = cancelProductDetailsCommand;
    //#endregion

};

return vm;

1 Answers1

1

Be sure to return a constructor function in your widget's viewModel. If you're returning an object literal, you've created a singleton module, not an instance-based module. Please post your viewModel code so that I can see how you're setting up the widget's viewModel.

[EDIT]

var vm = function(){
    //#region Properties
    this.title = title;
    this.name = name;
    this.productCode = productCode;
    this.description = description;
    //#endregion

    //#region Commands
    this.saveProductDetailsCommand = saveProductDetailsCommand;
    this.cancelProductDetailsCommand = cancelProductDetailsCommand;
    //#endregion

};

vm.prototype.activate = function () {}
vm.prototype.attached = function () {}
vm.prototype.compositionComplete = function () {}
vm.prototype.detached = function () {}
vm.prototype.deactivate = function () {}
vm.prototype.canDeactivate = function () {}

return vm;

Also, don't forget your enclosing define and function. So, in full:

define(['knockout'], 
function(ko) { 
    var vm = function(){
        //#region Properties
        this.title = title;
        this.name = name;
        this.productCode = productCode;
        this.description = description;
        //#endregion

        //#region Commands
        this.saveProductDetailsCommand = saveProductDetailsCommand;
        this.cancelProductDetailsCommand = cancelProductDetailsCommand;
        //#endregion

    };

    vm.prototype.activate = function () {}
    vm.prototype.attached = function () {}
    vm.prototype.compositionComplete = function () {}
    vm.prototype.detached = function () {}
    vm.prototype.deactivate = function () {}
    vm.prototype.canDeactivate = function () {}

    return vm;
}

Other pointers:

  • Use a more descriptive name than "vm" for your viewModel. If you're writing a menu widget, call it MenuWidget, using Pascal case, and replace "vm" with this name. It will make debugging a whole lot easier
  • Make sure properties that will be displaying and updating in the view are Knockout observables (e.g. change this.name = name; to this.name = ko.observable(name);
  • It's really best to initialize your observables in the activate handler, not in the constructor (at least with Durandal)
  • Agreed with this "diagnosis". This is the perfect example of where a constructor function is required, as you need multiple "instances" of the widget, rather than a single instance called many times. – Jason Clark Jun 05 '14 at 13:04
  • I was previously returning an object. I changed the widget to return a function, however I still got the same result. The code is posted above it my latest edit. – Taran Beekhuis Jun 05 '14 at 14:51
  • @Treemendus The problem is that you need to place `activate`, `attached`, `deactivate`, and `canDeactivate` on the prototype so that Durandal can find them. Also, they need to be functions. See the edit of my answer. –  Jun 05 '14 at 15:25
  • @EricTaylor Each of the callbacks are functions created as mentioned above. I've moved the Durandal callbacks to be on the prototype and I am still getting the same result. Was changing the vm to a function the main thing to do to create a instance based widget? Because seems to still be creating a singleton. – Taran Beekhuis Jun 05 '14 at 16:31
  • @Treemendus No, changing the name is only cosmetic. Make sure you clear your cache and refresh. There is no reason why it shouldn't work at this point. I have dozens of widgets and haven't had a problem. Perhaps you could zip up your code and make it available via DropBox or other cloud-based storage solution. I would be happy to take a look. –  Jun 05 '14 at 18:39