1
votes

I am trying to make the body element of a Durandal app (a SPA framework which employs Knockout) by checking a module's state (which account type users are logged in as). In the viewmodel of the app's shell, I have the following code:

function bindingComplete() {
  viewhelper.accountVisualTreatment();
}

And accountVisualTreatment is defined in the viewhelper module as such:

if (typeof(appsecurity.userInfo()) == 'undefined') {
  $("body").addClass("notloggedin");
  $(".container").addClass("notloggedin");
} else {
  if (appsecurity.isUserInRole(['Account Manager']) && !appsecurity.isUserInRole(['Administrator'])) {
    $("body").addClass("accountmanager");
    $("nav").addClass("hidden");
  } else {
    $("body").removeClass();
    $(".container").removeClass("notloggedin");
    $("nav").removeClass("hidden");
  }
}

Everything works fine if I refresh the pages when logged in as the diff account types. But as a SPA framework, there's no page refreshes while being used. Hence, the classes are not being applied as I want it to. How do I make it so the body, container, and nav elements' classes are being bound as I want?

Edit: I have tried to call viewhelper.accountVisualTreatment() in viewmodels of the landing page of the different account types but to not avail. Still needs page refresh.

Edit: I ugly-ly fixed it by applying a css binding to the container div in my shell.html

<div class="container" data-bind="css: viewhelper.accountVisualTreatment()"> ... </div>
1
Your code is only running once on document load which is why the classes are never applied. Your answer is definitely on the right track. You can definitely use a helper like that or you could also do a conditional binding like css: { 'notloggedin': appsecurity.userInfo() == 'undefined' } if you wanted it right there in the markup - PW Kad
Not sure I understand the question... the whole purpose of knockout bindings is to make your page dynamic without needing to refresh pages. You just bind visibility or css or whatever to observables and your page changes when the observables do. - Will Jenkins
I am trying to bind it to the body element, which in my Durandal deployment is defined outside of the viewmodel's entrance point, main/shell.js & shell.html. I solved it by calling my helper function in a css binding on the container div in shell.html. The css binding checks the binding and runs the helper function which checks which account type the user was logged in as and applies the correct CSS classes. - Xchai

1 Answers

0
votes

Because you want to do this in the body (so globally). So my assumption is you want to update the css classes as soon the accountinfo changes. You could create a singleton observable with the accountinfo. When it changes you just subscribe to the observable.

The only problem is with elements which aren't in the dom. (like between loading pages). Because you use Durandal you have different hooks to change the dom with jQuery.

To do this in the viewmodels you should use attached() or compositionComplete() see: http://durandaljs.com/documentation/Hooking-Lifecycle-Callbacks.html. This is the callback you get from durandal when the dom is completly attached to the viewmodel and all the observables.

I would say you don't want to do this in every viewmodel. So I would suggest to hook on the router:navigation:composition-complete event on the router, see: http://durandaljs.com/documentation/api.html#class/Router/event/router:navigation:composition-complete. This will make sure you will get the callback for every new hash navigation.

You can register to the global event in the data-main where the app is started:

app.on('router:navigation:composition-complete').then(viewhelper.accountVisualTreatment());

I hope this helped.