Nidificazione complessa di parziali e modelli


503

La mia domanda riguarda come affrontare la complessa nidificazione di modelli (detti anche parziali ) in un'applicazione AngularJS.

Il modo migliore per descrivere la mia situazione è con un'immagine che ho creato:

Diagramma della pagina di AngularJS

Come puoi vedere, questo ha il potenziale per essere un'applicazione abbastanza complessa con molti modelli nidificati.

L'applicazione è a pagina singola, quindi carica un index.html che contiene un elemento div nel DOM con l' ng-viewattributo.

Per la cerchia 1 , vedi che esiste una navigazione principale che carica i modelli appropriati nella cartella ng-view. Lo sto facendo passando $routeParamsal modulo dell'app principale. Ecco un esempio di ciò che è nella mia app:

angular.module('myApp', []).
    config(['$routeProvider', function($routeProvider) {
        $routeProvider.                     
            when("/job/:jobId/zones/:zoneId", { controller: JobDetailController, templateUrl: 'assets/job_list_app/templates/zone_edit.html' }).
            when("/job/:jobId/initial_inspection", { controller: JobDetailController, templateUrl: 'assets/job_list_app/templates/initial_inspection.html' }).
            when("/job/:jobId/zones/:zoneId/rooms/:roomId", { controller: JobDetailController, templateUrl: 'assets/job_list_app/templates/room_edit.html' })       

    }]);

Nel cerchio 2 , il modello che viene caricato in ng-viewha una navigazione secondaria aggiuntiva . Questo sub-nav deve quindi caricare i template nell'area sottostante, ma poiché ng-view è già in uso, non sono sicuro di come procedere.

So che posso includere modelli aggiuntivi nel 1 ° modello, ma questi modelli saranno tutti piuttosto complessi. Vorrei mantenere separati tutti i modelli per rendere l'applicazione più facile da aggiornare e non avere una dipendenza dal modello principale che deve essere caricato per accedere ai suoi figli.

Nel cerchio 3 , puoi vedere le cose diventare ancora più complesse. Esiste il potenziale che i modelli di navigazione secondaria abbiano una seconda navigazione secondaria che dovrà caricare i propri modelli anche nell'area del cerchio 4

Come si fa a strutturare un'app AngularJS per gestire un annidamento di modelli così complesso, mantenendoli separati l'uno dall'altro?


3
Se stai ancora seguendo questo thread, ho aggiunto un link a un nuovo progetto AngularUI per affrontare questo e un terzo demo che è alimentato da subroutes senza bisogno di una direttiva per la mia risposta.
ProLoser,

2
che dire di questa? bennadel.com/blog/…
ericbae

82
la domanda più bella che ho visto da molto tempo :)
Mark Nadig

2
concordato! Adoro l'uso del mockup
Bryan Hong

4
Sarebbe bello aggiungere collegamenti alle alternative direttamente nella domanda qui. github.com/angular-ui/ui-router github.com/artch/angular-route-segment github.com/dotJEM/angular-routing
Jens

Risposte:


171

Bene, dato che al momento puoi avere solo una direttiva ngView ... Uso controlli di direttiva nidificati. Ciò consente di impostare modelli e ereditare (o isolare) ambiti tra loro. A parte ciò, utilizzo ng-switch o anche solo ng-show per scegliere quali controlli visualizzare in base a ciò che proviene da $ routeParams.

EDIT Ecco alcuni pseudo-codici di esempio per darti un'idea di cosa sto parlando. Con una navigazione secondaria nidificata.

Ecco la pagina dell'app principale

<!-- primary nav -->
<a href="#/page/1">Page 1</a>
<a href="#/page/2">Page 2</a>
<a href="#/page/3">Page 3</a>

<!-- display the view -->
<div ng-view>
</div>

Direttiva per la navigazione secondaria

app.directive('mySubNav', function(){
    return {
        restrict: 'E',
        scope: {
           current: '=current'
        },
        templateUrl: 'mySubNav.html',
        controller: function($scope) {
        }
    };
});

modello per la navigazione secondaria

<a href="#/page/1/sub/1">Sub Item 1</a>
<a href="#/page/1/sub/2">Sub Item 2</a>
<a href="#/page/1/sub/3">Sub Item 3</a>

modello per una pagina principale (dal nav principale)

<my-sub-nav current="sub"></my-sub-nav>

<ng-switch on="sub">
  <div ng-switch-when="1">
      <my-sub-area1></my-sub-area>
  </div>
  <div ng-switch-when="2">
      <my-sub-area2></my-sub-area>
  </div>
  <div ng-switch-when="3">
      <my-sub-area3></my-sub-area>
  </div>
</ng-switch>

Controller per una pagina principale. (dal nav principale)

app.controller('page1Ctrl', function($scope, $routeParams) {
     $scope.sub = $routeParams.sub;
});

Direttiva per una sottozona

app.directive('mySubArea1', function(){
    return {
        restrict: 'E',
        templateUrl: 'mySubArea1.html',
        controller: function($scope) {
            //controller for your sub area.
        }
    };
});

9
Mi piace di più la soluzione di ProLoser, facciamo qualcosa del genere sulla nostra app e ha funzionato bene. Il problema con la soluzione di blesh è il codice del controller che entra nelle direttive. Di solito il controller specificato in una direttiva è uno che lavora a stretto contatto con la direttiva per es: ngModelCtrl che lavora a stretto contatto con l'input e altre direttive. Nel tuo caso inserire il codice del controller all'interno di una direttiva sarebbe un odore di codice, in realtà è un controller indipendente.
ChrisOdney,

@blesh, bello! Sembra davvero ben spiegato, ma come un buon programmatore JS e principiante in AngularJS non riesco a catturare così bene ... Io e la community apprezzeremmo molto un collegamento JSFiddle a un campione funzionante usando questo approccio. Diggin in giro sarebbe facile da capire! :) Grazie
Roger Barreto,

8
Penso che questa soluzione dovrebbe essere la prima risposta per qualsiasi tipo di domanda "la vista nidificata non ha funzionato". Solo perché è molto più vicino all'ideologia angolare invece di usare l'interfaccia utente e così via. Grazie.
Sergei Panfilov,

Quindi la risposta è direttive?
Marc M.

per evidenziare gli oggetti sub-nav potresti usare questo: coder1.com/articles/angularjs-managing-active-nav-elements è un buon modo per farlo?
Escapedcat,

198

AGGIORNAMENTO: Scopri il nuovo progetto di AngularUI per risolvere questo problema


Per le sottosezioni è facile come sfruttare le stringhe in ng-include:

<ul id="subNav">
  <li><a ng-click="subPage='section1/subpage1.htm'">Sub Page 1</a></li>
  <li><a ng-click="subPage='section1/subpage2.htm'">Sub Page 2</a></li>
  <li><a ng-click="subPage='section1/subpage3.htm'">Sub Page 3</a></li>
</ul>
<ng-include src="subPage"></ng-include>

Oppure puoi creare un oggetto nel caso in cui tu abbia collegamenti a sottopagine dappertutto:

$scope.pages = { page1: 'section1/subpage1.htm', ... };
<ul id="subNav">
  <li><a ng-click="subPage='page1'">Sub Page 1</a></li>
  <li><a ng-click="subPage='page2'">Sub Page 2</a></li>
  <li><a ng-click="subPage='page3'">Sub Page 3</a></li>
</ul>
<ng-include src="pages[subPage]"></ng-include>

Oppure puoi persino usare $routeParams

$routeProvider.when('/home', ...);
$routeProvider.when('/home/:tab', ...);
$scope.params = $routeParams;
<ul id="subNav">
  <li><a href="#/home/tab1">Sub Page 1</a></li>
  <li><a href="#/home/tab2">Sub Page 2</a></li>
  <li><a href="#/home/tab3">Sub Page 3</a></li>
</ul>
<ng-include src=" '/home/' + tab + '.html' "></ng-include>

Puoi anche mettere un ng-controller al livello più alto di ogni parziale


8
Mi piace di più la tua soluzione. sono un novellino di Angular e questo sembra molto più comprensibile da come vedo il web fino ad oggi. chissà, può essere carne usa più della struttura angolare per farlo, ma sembra che tu l'abbia inchiodato con meno righe di codice in un modo più sensato. Grazie!
Gleeb

2
@ProLooser forse intendevi questo link github.com/angular-ui/ui-router ... quello che hai incollato è rotto
simonC

4
Sto riscontrando lo stesso problema di progettazione dell'OP. Qualcuno ha provato il meccanismo dello stato AngularUI? Sono piuttosto riluttante a usare un'altra libreria di terze parti e preferirei attenermi al "modo di fare" di AngularJS. D'altra parte, il sistema di routing sembra essere il suo tallone d'Achille ... @PhillipKregg, cosa hai usato per risolvere questo scenario?
Sam,

4
È possibile specificare <div ng-include="'/home/' + tab + '.html'" ng-controller="SubCtrl"></div>per utilizzare un controller / ambito separato per il modello secondario. O semplicemente specifica la ngControllerdirettiva in qualsiasi punto all'interno dei tuoi modelli secondari per utilizzare un controller diverso per ogni parziale.
Colllin,

2
@DerekAdair il progetto è abbastanza stabile (nonostante il numero di versione) e in uso in produzione in pochi punti. Impedisce il ricaricamento inutile del controller ed è un'alternativa molto migliore alla mia soluzione suggerita.
ProLoser

26

Puoi controllare questa libreria per lo stesso scopo anche:

http://angular-route-segment.com

Sembra quello che stai cercando ed è molto più semplice da usare rispetto all'Ui-router. Dal sito demo :

JS:

$routeSegmentProvider.

when('/section1',          's1.home').
when('/section1/:id',      's1.itemInfo.overview').
when('/section2',          's2').

segment('s1', {
    templateUrl: 'templates/section1.html',
    controller: MainCtrl}).
within().
    segment('home', {
        templateUrl: 'templates/section1/home.html'}).
    segment('itemInfo', {
        templateUrl: 'templates/section1/item.html',
        controller: Section1ItemCtrl,
        dependencies: ['id']}).
    within().
        segment('overview', {
            templateUrl: 'templates/section1/item/overview.html'}).

HTML di primo livello:

<ul>
    <li ng-class="{active: $routeSegment.startsWith('s1')}">
        <a href="/section1">Section 1</a>
    </li>
    <li ng-class="{active: $routeSegment.startsWith('s2')}">
        <a href="/section2">Section 2</a>
    </li>
</ul>
<div id="contents" app-view-segment="0"></div>

HTML nidificato:

<h4>Section 1</h4>
Section 1 contents.
<div app-view-segment="1"></div>

Artem, la tua è la migliore soluzione che ho trovato fino ad oggi! Mi piace il fatto che estenda la rotta angolare piuttosto che sostituirla come fa la UI angolare.
LastTribunal,

17

Anch'io stavo lottando con le visioni nidificate in Angolare.

Dopo aver acquisito l' interfaccia utente, sapevo che non sarei più tornato alla funzionalità di routing predefinita angolare.

Ecco un'applicazione di esempio che utilizza più livelli di nidificazione delle viste

app.config(function ($stateProvider, $urlRouterProvider,$httpProvider) {
// navigate to view1 view by default
$urlRouterProvider.otherwise("/view1");

$stateProvider
    .state('view1', {
        url: '/view1',
        templateUrl: 'partials/view1.html',
        controller: 'view1.MainController'
    })
    .state('view1.nestedViews', {
        url: '/view1',
        views: {
            'childView1': { templateUrl: 'partials/view1.childView1.html' , controller: 'childView1Ctrl'},
            'childView2': { templateUrl: 'partials/view1.childView2.html', controller: 'childView2Ctrl' },
            'childView3': { templateUrl: 'partials/view1.childView3.html', controller: 'childView3Ctrl' }
        }
    })

    .state('view2', {
        url: '/view2',
    })

    .state('view3', {
        url: '/view3',
    })

    .state('view4', {
        url: '/view4',
    });
});

Come si può vedere, ci sono 4 viste principali (view1, view2, view3, view4) e view1 ha 3 viste figlio.


2
Qual è lo scopo dell'iniezione $httpProvider? Non lo vedo usato da nessuna parte.
Aaron,

@Aaron app.config non termina con questo codice e potrebbe essere $ httpIl provider viene utilizzato da qualche altra parte
Vlad

4

È possibile utilizzare ng-include per evitare di utilizzare ng-views nidificate.

http://docs.angularjs.org/api/ng/directive/ngInclude
http://plnkr.co/edit/ngdoc:example-example39@snapshot?p=preview

La mia pagina indice Uso ng-view. Quindi sulle mie pagine secondarie che ho bisogno di avere cornici nidificate. Uso ng-include. La demo mostra un menu a discesa. Ho sostituito il mio con un link ng-click. Nella funzione metterei $ scope.template = $ scope.templates [0]; oppure $ scope.template = $ scope.templates [1];

$scope.clickToSomePage= function(){
  $scope.template = $scope.templates[0];
};

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.