Question

DOM for my Durandal app is structured as below:

  //shell.html
  <div id="applicationContentWrapper" >
    <!-- ko compose: {model: 'viewmodels/header', activate:true} --> <!-- /ko -->
    <div data-bind="router:{}"></div>
  </div>

Here's a snippet of my router configuration

router.map([
        { route: '', moduleId: 'viewmodels/dashboard', name: 'dashboard'},
        { route: 'form', moduleId: 'viewmodels/form', name: 'form'},
        { route: 'login', moduleId: 'viewmodels/login', name: 'login'}
]);

As you can see my login view gets inserted into the <div data-bind="router:{}"></div> which is the expected behaviour. But if I am on the login view I need to hide the header view which I have managed to do by subscribing to the router configuration in the header viewmodel:

router.activeInstruction.subscribe(function (val) {
    val.config.name === "login" ? showHeader(false) : showHeader(true);
});

But this feels a little messy and the DOM for the header view is still in the page, what I really want to do is if I am in the in the login view I just want the DOM for the login view to be there as if I am in separate page with only the login view. But so far I have not found a clean way to do it.

Was it helpful?

Solution

A few things:

  1. The ultimate responsibility for security is NEVER in your front-end. Everything you 'close off' on the front-end can easily be accessed by a malicious user. The back-end should secure all your functionality and data, the front-end only hides it to give the user a better experience. So in other words, it doesn't matter that it's still in the DOM (from a security standpoint). Even if it wasn't, it wouldn't be more secure.
  2. If you want to not have the nodes in the DOM anyway (for performance reasons), use the with or if-binding. They hide whatever's inside if the condition is falsy.
  3. The subscription on activeInstruction is indeed a bit messy. You need to improve your logic. The header isn't hidden because the user is on the login page, the header is hidden because the user ISN'T LOGGED IN. That is a BIG difference. What you are doing is essentially a dirty hack. Have an observable/computed observable in your shell viewmodel (or somewhere else) that returns whether the user is authenticated. Then based on that, show/hide your header.
  4. Using the authenticated-observable I mentioned above, you can also 'guard' your views by checking against this observable. That way you can make sure a user can't navigate to routes for which he needs to be authenticated. Again though, the ultimate responsibility for this security-check is on your backend, not your frontend.

Edit

I can understand your reasoning for wanting a whole different 'layout' without the rest of the DOM for your login view. But this goes against the whole idea of a single-page application. It is also unnecessary. A bit of smart css and data-binding (visible, if, with) will give you the same results, often providing a better user experience than a full page reload would do. If you insist on breaking the SPA though, place a router one level lower: e.g. the main router only knows a login route and an internal route. The internal route has another router which holds all the other pages. This way you can move the common HTML from your shell view to the internal view.

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