Komplexes Verschachteln von Partials und Templates


503

Meine Frage betrifft den Umgang mit der komplexen Verschachtelung von Vorlagen (auch Partials genannt ) in einer AngularJS-Anwendung.

Ich beschreibe meine Situation am besten mit einem Bild, das ich erstellt habe:

AngularJS-Seitendiagramm

Wie Sie sehen, kann dies eine ziemlich komplexe Anwendung mit vielen verschachtelten Modellen sein.

Die Anwendung ist einseitig und lädt daher eine index.html , die ein div-Element im DOM mit dem ng-viewAttribut enthält.

Für Kreis 1 sehen Sie, dass es eine primäre Navigation gibt, die die entsprechenden Vorlagen in die lädt ng-view. Ich gehe $routeParamsdazu zum Haupt-App-Modul über. Hier ist ein Beispiel dafür, was in meiner App enthalten ist:

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' })       

    }]);

In Kreis 2 verfügt die Vorlage, die in die geladen wird, ng-viewüber eine zusätzliche Unternavigation . Dieses Sub-Navi muss dann Vorlagen in den Bereich darunter laden - aber da ng-view bereits verwendet wird, bin ich mir nicht sicher, wie ich das machen soll.

Ich weiß, dass ich zusätzliche Vorlagen in die erste Vorlage aufnehmen kann, aber diese Vorlagen werden alle ziemlich komplex sein. Ich möchte alle Vorlagen getrennt halten, damit die Anwendung einfacher aktualisiert werden kann und keine Abhängigkeit von der übergeordneten Vorlage besteht, die geladen werden muss, um auf ihre untergeordneten Vorlagen zugreifen zu können.

In Kreis 3 sehen Sie, wie die Dinge noch komplexer werden. Es besteht das Potenzial, dass die Subnavigationsvorlagen eine zweite Subnavigation haben , die auch ihre eigenen Vorlagen in den Bereich in Kreis 4 laden muss

Wie strukturiert man eine AngularJS-App, um mit solch komplexen Verschachtelungen von Vorlagen umzugehen und sie alle voneinander zu trennen?


3
Wenn Sie diesem Thread immer noch folgen, habe ich einen Link zu einem neuen AngularUI-Projekt hinzugefügt, um dies zu beheben, und eine dritte Demo, die von Subrouten unterstützt wird, ohne dass eine Anweisung für meine Antwort erforderlich ist.
ProLoser

2
was ist mit diesem? bennadel.com/blog/…
Ericbae

82
schönste Frage, die ich seit langer Zeit gesehen habe :)
Mark Nadig

2
einverstanden! Ich liebe die Verwendung von Mockup
Bryan Hong

4
Es wäre toll, hier Links zu den Alternativen direkt in der Frage hinzuzufügen. github.com/angular-ui/ui-router github.com/artch/angular-route-segment github.com/dotJEM/angular-routing
Jens

Antworten:


171

Nun, da Sie derzeit nur eine ngView-Direktive haben können ... verwende ich verschachtelte Direktivensteuerelemente. Auf diese Weise können Sie Vorlagen einrichten und Bereiche zwischen diesen erben (oder isolieren). Abgesehen davon verwende ich ng-switch oder auch nur ng-show, um auszuwählen, welche Steuerelemente ich anzeigen möchte, basierend auf den Eingaben von $ routeParams.

BEARBEITEN Hier ist ein Beispiel für einen Pseudocode, der Ihnen eine Vorstellung davon gibt, wovon ich spreche. Mit einer verschachtelten Subnavigation.

Hier ist die Haupt-App-Seite

<!-- 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>

Richtlinie für die Subnavigation

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

Vorlage für die Subnavigation

<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>

Vorlage für eine Hauptseite (von der primären Navigation)

<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 für eine Hauptseite. (vom primären Navi)

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

Richtlinie für einen Unterbereich

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

9
Die Lösung von ProLoser gefällt mir besser, wir machen so etwas in unserer App und es hat gut funktioniert. Das Problem bei der Lösung von blesh ist der Controller-Code, der in die Anweisungen eingeht. Normalerweise arbeitet der Controller, den Sie in einer Direktive angeben, eng mit der Direktive zusammen, z. B. ngModelCtrl, die eng mit der Eingabe und anderen Direktiven zusammenarbeitet. In Ihrem Fall wäre das Einfügen des Controller-Codes in eine Direktive ein Code-Geruch, es handelt sich tatsächlich um einen unabhängigen Controller.
ChrisOdney

@blesh, schön! Es scheint wirklich gut erklärt zu sein, aber als guter JS-Programmierer und Anfänger in AngularJS konnte ich das nicht gut verstehen ... Ich und die Community würden einen JSFiddle-Link zu einem funktionierenden Beispiel mit diesem Ansatz wirklich genießen. Diggin es wäre leicht zu verstehen! :) Danke
Roger Barreto

8
Ich denke, diese Lösung sollte eine erste Antwort auf alle Fragen sein, bei denen eine verschachtelte Ansicht nicht funktioniert hat. Nur weil es der Angular-Ideologie viel näher kommt, anstatt UI-Router usw. zu verwenden. Danke.
Sergei Panfilov

Die Antwort lautet also Direktiven?
Marc M.

Um die Sub-Nav-Elemente hervorzuheben, können Sie Folgendes verwenden: coder1.com/articles/angularjs-managing-active-nav-elements Ist dies ein guter Weg, dies zu tun?
entkommen Katze

198

UPDATE: Schauen Sie sich das neue Projekt von AngularUI an, um dieses Problem zu beheben


Für Unterabschnitte ist es so einfach wie das Verwenden von Zeichenfolgen 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>

Oder Sie können ein Objekt erstellen, falls Sie überall Links zu Unterseiten haben:

$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>

Oder Sie können sogar verwenden $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>

Sie können auch einen ng-Controller auf die oberste Ebene jedes Teils setzen


8
Ich mag deine Lösung besser. Ich bin ein Neuling in Angular und dies scheint viel verständlicher zu sein, wenn ich das Web bis heute sehe. Wer weiß, vielleicht verwendet blesh mehr des eckigen Rahmens, um es zu tun, aber es scheint, als hätten Sie es mit weniger Codezeilen auf vernünftigere Weise genagelt. Vielen Dank!
Gleeb

2
@ProLooser vielleicht meintest du diesen Link github.com/angular-ui/ui-router ... der, den du eingefügt hast, ist kaputt
simonC

4
Ich habe das gleiche Designproblem wie das OP. Hat jemand den AngularUI-Statusmechanismus ausprobiert? Ich bin ziemlich zurückhaltend bei der Verwendung einer weiteren Bibliothek von Drittanbietern und bleibe lieber bei AngularJS 'Vorgehensweise'. Andererseits scheint das Routing-System die Achillesferse zu sein ... @PhillipKregg, was haben Sie letztendlich verwendet, um dieses Szenario zu lösen?
Sam

4
Sie können angeben <div ng-include="'/home/' + tab + '.html'" ng-controller="SubCtrl"></div>, um einen separaten Controller / Bereich für die Untervorlage zu verwenden. Oder geben Sie einfach die ngControllerDirektive an einer beliebigen Stelle in Ihren Untervorlagen an, um für jeden Teil einen anderen Controller zu verwenden.
Colllin

2
@DerekAdair das Projekt ist ziemlich stabil (trotz Versionsnummer) und wird an einigen Stellen in der Produktion eingesetzt. Es verhindert unnötiges Neuladen des Controllers und ist eine viel bessere Alternative zu meiner vorgeschlagenen Lösung.
ProLoser

26

Sie können diese Bibliothek auch aus demselben Grund auschecken:

http://angular-route-segment.com

Es sieht so aus, wie Sie es suchen, und es ist viel einfacher zu bedienen als der UI-Router. Von der Demo-Site :

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 der obersten Ebene:

<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>

Verschachteltes HTML:

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

Artem, deine ist die beste Lösung, die ich bisher gefunden habe! Ich mag die Tatsache, dass Sie die Winkelroute verlängern, anstatt sie wie die Winkel-Benutzeroberfläche zu ersetzen.
LastTribunal

17

Auch ich hatte mit verschachtelten Ansichten in Angular zu kämpfen.

Nachdem ich mir den UI-Router besorgt hatte, wusste ich, dass ich nie mehr zur Standard-Routing-Funktionalität zurückkehren würde.

Hier ist eine Beispielanwendung, die mehrere Ebenen von verschachtelten Ansichten verwendet

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',
    });
});

Wie zu sehen ist, gibt es 4 Hauptansichten (Ansicht1, Ansicht2, Ansicht3, Ansicht4) und Ansicht1 hat 3 untergeordnete Ansichten.


2
Was ist der Zweck der Injektion $httpProvider? Ich sehe es nirgendwo benutzt.
Aaron

@ Aaron app.config endet nicht in diesem Code und möglicherweise wird $ httpProvider woanders verwendet
Vlad

4

Sie können ng-include verwenden, um die Verwendung verschachtelter ng-Ansichten zu vermeiden.

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

Meine Indexseite verwende ich ng-view. Dann auf meinen Unterseiten, die ich verschachtelte Frames haben muss. Ich benutze ng-include. Die Demo zeigt ein Dropdown. Ich habe meine durch einen Link ng-click ersetzt. In die Funktion würde ich $ scope.template = $ scope.templates [0] setzen; oder $ scope.template = $ scope.templates [1];

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

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.