2013-07-15 13 views
7

Ho appena iniziato a lavorare con Durandal e tutti i pezzi stanno cadendo sul posto, e sto utilizzando il modello Hot Towel per accelerare le cose.Come utilizzare i menu a discesa per la navigazione Durandal?

Una cosa che mi ostacola è come creare un sistema di navigazione gerarchico più complesso di un gruppo di testa. Ecco quello che voglio finire con:

ABC
A1 B1 C1
A2 B2 C2

A, B, e C sono i menu di primo livello che non hanno percorsi ad essi connessi - sono semplicemente segnaposto. Avrò viste e modellini visivi per A1, A2, B1, B2, C1 e C2 e ho bisogno che quei tag hash siano link attivi.

La mia migliore idea al momento è quella di aggiungere il menu genitore nell'URL di ciascuna rotta e avere il codice in nav.html che aggiunge dinamicamente ciascun nodo al genitore appropriato in base all'analisi dell'URL. Per essere completamente dinamico, aggiungerebbe sia i nodi padre che i nodi figli in modo dinamico.

 { 
      url: 'A_A1', 
      moduleId: 'viewmodels/A_A1', 
      name: 'A1', 
      visible: true 
     } 

ho fatto un sacco di ricerca di esempi di navigazione gerarchica con Durandal, ma non hanno ancora visto nulla. Esiste una best practice per espandere la funzionalità di navigazione oltre la semplice lista monodimensionale? Sto trascurando alcune funzionalità del router che non vedo che mi permetterebbe di farlo senza incorporare le informazioni di gerarchia nei nomi delle viste?

MODIFICA: Ho appena contrassegnato una risposta corretta, anche se non ero soddisfatto delle soluzioni presentate. Quando si seleziona un framework per astrarre e separare la logica, la presentazione e il controllo, sembra sciocco iniziare a unire questi costrutti solo per fornire più di una shell di navigazione di base. Ho spostato i miei sforzi di sviluppo in angularjs dove cose come questa diventano molto più intuitive e possono mantenere la separazione. Speriamo che Durandal possa andare avanti un po 'di più nel prossimo futuro e lo valuterò sicuramente per progetti futuri.

risposta

6

Si potrebbe loro hard-codice nella tua vista shell, ma se non avete voglia di fare che si può fare questo -

Secondo lei, fare un lavoro non-ancora con/che fa # niente, con una discesa di una rotta e un'altra con una discesa di b percorsi.

 <div class="nav-collapse collapse main-nav"> 
      <div class="btn-group"> 
       <a class="btn" href="/#"> 
        <span class="text">A Routes</span> </a> 
       <button class="btn dropdown-toggle" data-toggle="dropdown"><span class="caret"></span></button> 
        <ul class="dropdown-menu pull-right"> 
         <!-- ko foreach: aRoutes --> 
         <li data-bind="css: { active: isActive }"> 
          <a data-bind="attr: { href: hash }, html: name"></a> 
         </li> 
         <!-- /ko --> 
        </ul> 
      </div> 
      <div class="btn-group"> 
       <a class="btn" href="/#"> 
        <span class="text">B Routes</span> </a> 
       <button class="btn dropdown-toggle" data-toggle="dropdown"><span class="caret"></span></button> 
        <ul class="dropdown-menu pull-right"> 
         <!-- ko foreach: bRoutes --> 
         <li data-bind="css: { active: isActive }"> 
          <a data-bind="attr: { href: hash }, html: name"></a> 
         </li> 
         <!-- /ko --> 
        </ul> 
      </div> 
     </div> 

fare alcune osservabili calcolati per i percorsi nella vista guscio modello

var aRoutes = ko.computed(function() { 
     return router.allRoutes().filter(function (r) { 
      return r.settings.aroute; 
     }); 
    }); 

    var bRoutes = ko.computed(function() { 
     return router.allRoutes().filter(function (r) { 
      return r.settings.broute; 
     }); 
    }); 

e nella definizione percorso -

{ 
    url: 'a1', 
    moduleId: 'viewmodels/a1', 
    name: 'A1', 
    visible: false, 
    settings: {aroute: true} 
}, 

Questo imposta tutti i percorsi su false, e quindi dà loro un altro attributo di aroute che è impostato su true. I filtri calcolati verso il basso solo per rotte con quell'impostazione impostata su true.

+0

Questa è un'idea eccellente: tagga il percorso con il suo genitore. Ho intenzione di lavorare su questo un po 'per renderlo più dinamico, e forse anche come un widget di Durandal. – Graham

+0

Spero che ti aiuti, sto usando questo metodo da un po 'e funziona molto bene ed è dinamico allo stesso tempo. Ho appena visto che non avevo la giusta indentazione sui b percorsi, quindi l'ho risolto. In bocca al lupo. –

3

Ho ideato una soluzione generale dopo aver letto questo post. Con questa soluzione puoi aggiungere dinamicamente qualsiasi rotta a un menu a discesa nel nav.

In main.js, dove specificano i miei percorsi, ho aggiunto una funzione per mappare l'oggetto del sottomenu all'oggetto delle impostazioni della rotta principale.

function mapSubNav(parentRouteInfo) { 
    var subroutes = []; 
    var length = arguments.length; 
    for (var i = 0; i < length; i++) { 
     subroutes.push(arguments[i]); 
    } 
    parentRouteInfo.settings.subroutes = subroutes; 
} 

var page = router.mapNav('page', null, 'Page'), 
    sub1 = router.mapRoute('page/sub1', null, 'Sub1'), 
    sub2 = router.mapRoute('page/sub2', null, 'Sub2'); 
mapSubNav(nav, sub1, sub2); 

Spiegazione:

La funzione router mapNav restituisce una definizione della rotta che assomiglia a questo:

{ 
    url:'flickr', //you provided this 
    name: 'Flickr', //derived 
    moduleId: 'flickr', //derived 
    caption: 'Flickr', //derived (uses to set the document title) 
    settings: {}, //default, 
    hash: '#/flickr', //calculated 
    visible: true, //from calling mapNav instead of mapRoute 
    isActive: ko.computed //only present on visible routes to track if they are active in the nav 
} 

la funzione di supporto, mapSubNav, sarà posto un elenco di riferimenti ai percorsi che si desidera visualizzare nel menu a discesa nell'oggetto impostazioni. In questo esempio, il risultato sarà:

{ 
    url:'page', 
    name: 'Page', 
    moduleId: 'page', 
    caption: 'Page', 
    settings: { subroutes: [nav, sub1, sub2] }, 
    hash: '#/page', 
    visible: true, 
    isActive: ko.computed 
} 

ho esteso il mio guscio ViewModel per assomigliare a questo:

define(function (require) { 
    var router = require('durandal/plugins/router'); 
    var app = require('durandal/app'); 

    var ViewModel = function() { 
     var self = this; 
     self.router = router; 
     self.dataToggle = function (route) { 
      return !!route.settings.subroutes ? 'dropdown' : ''; 
     }; 
     self.html = function (route) { 
      return !!route.settings.subroutes ? route.name + ' <b class="caret"></b>' : route.name; 
     }; 
     self.hash = function (route) { 
      return !!route.settings.subroutes ? '#' : route.hash; 
     }; 
     self.divider = function (route, parent) { 
      system.log('Adding', route, 'to dropdown', 'Parent', parent); 
      return route.hash === parent.hash; 
     }; 
     self.activate = function() { 
      return router.activate('welcome'); 
     } 
    }; 

    return new ViewModel(); 
}); 

Nota:

Le funzioni extra in shell.js deciderà quali attributi aggiungere gli elementi DOM nel nav.


Infine, ho modificato la vista shell in modo che assomigli a questa;

<div class="nav-collapse collapse"> 
    <ul class="nav navbar-nav" data-bind="foreach: router.visibleRoutes()"> 
     <li data-bind="css: { active: isActive, dropdown: !!settings.subroutes }"> 
      <a data-bind="css: { 'dropdown-toggle': !!settings.subroutes }, 
          attr: { href: $root.hash($data), 'data-toggle': $root.dataToggle($data) }, 
          html: $root.html($data)"></a> 
      <ul data-bind="foreach: settings.subroutes" class="dropdown-menu"> 
       <li><a data-bind="attr: { href: hash }, 
            html: name"></a></li> 
       <li data-bind="css: { divider: $root.divider($data, $parent) }"></li> 
      </ul> 
     </li> 
    </ul> 
</div> 

Risultato:

Il risultato finale è una voce di menu con un interruttore a discesa. Il menu a discesa conterrà un collegamento al genitore e ad un collegamento ciascuno ai subroutes. Qualcosa di simile a questo:

--------------------------------------------- 
Menu items... | Page v | More menu items... 
--------------------------------------------- 
       | Page | 
       ---------- 
       | Sub 1 | 
       | Sub 2 | 
       ---------- 

jQuery non consente di mappare un percorso ad un pulsante a discesa ginocchiera, che è il motivo per cui ho fatto il percorso genitore mantenere un riferimento a se stesso nella lista dei subroutes Ci sarà ancora esistere un collegamento al percorso principale nel nav.

+0

Cosa c'è di diverso dalla risposta che ho fornito? –

+0

@PWKad Con la mia soluzione, una volta che tutto è stato impostato, puoi semplicemente continuare a usare 'mapSubNav' in main.js, e i sottomenu saranno aggiunti automaticamente, senza bisogno di modificare altri file. Non ho visto come ciò potrebbe essere ottenuto con la tua soluzione. Mi sono perso qualcosa? –