การทำรังที่ซับซ้อนของชิ้นงานและเทมเพลต


503

คำถามของฉันที่เกี่ยวข้องกับวิธีการเกี่ยวกับการจัดการกับการทำรังที่ซับซ้อนของแม่ (ที่เรียกว่าpartials ) ในโปรแกรมประยุกต์ AngularJS

วิธีที่ดีที่สุดในการอธิบายสถานการณ์ของฉันคือภาพที่ฉันสร้างขึ้น:

แผนภาพหน้า AngularJS

อย่างที่คุณเห็นสิ่งนี้มีความเป็นไปได้ที่จะเป็นแอพพลิเคชั่นที่ค่อนข้างซับซ้อนและมีหลายรุ่นซ้อนกัน

แอปพลิเคชันเป็นหน้าเดียวจึงโหลดindex.htmlที่มีองค์ประกอบ div ใน DOM พร้อมกับng-viewแอตทริบิวต์

สำหรับแวดวง 1 , ng-viewคุณจะเห็นว่ามีการนำหลักที่โหลดแม่แบบที่เหมาะสมลงใน ฉันกำลังทำสิ่งนี้โดยส่งผ่าน$routeParamsไปยังโมดูลแอพหลัก นี่คือตัวอย่างของสิ่งที่อยู่ในแอพของฉัน:

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

    }]);

ในวงกลม 2 , แม่แบบที่ถูกโหลดลงในng-viewมีเพิ่มเติมย่อยนำทาง การนำทางย่อยนี้จำเป็นต้องโหลดแม่แบบลงในพื้นที่ด้านล่าง แต่เนื่องจาก ng-view กำลังถูกใช้งานอยู่ฉันไม่แน่ใจว่าจะทำเช่นนี้ได้อย่างไร

ฉันรู้ว่าฉันสามารถรวมแม่แบบเพิ่มเติมภายในแม่แบบที่ 1 แต่แม่แบบเหล่านี้ทั้งหมดจะค่อนข้างซับซ้อน ฉันต้องการแยกแม่แบบทั้งหมดเพื่อให้แอปพลิเคชันง่ายต่อการอัปเดตและไม่ต้องพึ่งพาแม่แบบแม่แบบที่ต้องโหลดเพื่อเข้าถึงลูก ๆ

ในวงกลม 3คุณจะเห็นสิ่งต่าง ๆ มีความซับซ้อนยิ่งขึ้น มีความเป็นไปได้ที่เทมเพลตการนำทางย่อยจะมีการนำทางย่อยที่ 2ที่จะต้องโหลดเทมเพลตของตัวเองรวมถึงพื้นที่ในวงกลม 4

เราจะสร้างแอพ AngularJS เพื่อจัดการกับการซ้อนเทมเพลตที่ซับซ้อนได้อย่างไรในขณะที่ทำให้พวกมันแยกจากกัน


3
หากคุณยังคงติดตามหัวข้อนี้อยู่ฉันได้เพิ่มลิงก์ไปยังโครงการ AngularUI ใหม่เพื่อแก้ไขปัญหานี้และการสาธิตครั้งที่สามที่ขับเคลื่อนโดยรูทีนย่อยโดยไม่ต้องใช้คำสั่งในคำตอบของฉัน
ProLoser

2
แล้วอันนี้หล่ะ? bennadel.com/blog/…
ericbae

82
คำถามที่สวยที่สุดที่ผมเคยเห็นในระยะเวลานาน :)
มาร์ค Nadig

2
ตกลงกัน! รักการใช้งาน mockup
ไบรอันหง

4
จะเป็นการดีหากเพิ่มลิงก์ไปยังทางเลือกอื่น ๆ ในคำถามโดยตรงที่นี่ github.com/angular-ui/ui-router github.com/artch/angular-route-segment github.com/dotJEM/angular-routing
Jens

คำตอบ:


171

เนื่องจากปัจจุบันคุณสามารถมีเพียงคำสั่ง ngView เดียว ... ฉันใช้การควบคุมคำสั่งแบบซ้อน สิ่งนี้ช่วยให้คุณสามารถตั้งค่า templating และสืบทอดขอบเขต (หรือแยก) ในหมู่พวกเขา นอกเหนือจากนั้นฉันใช้ ng-switch หรือแม้แต่แค่ ng-show เพื่อเลือกว่าฉันกำลังแสดงตัวควบคุมใดโดยดูจากสิ่งที่มาจาก $ routeParams

แก้ไขนี่คือตัวอย่างรหัสหลอกเพื่อให้คุณมีความคิดในสิ่งที่ฉันกำลังพูดถึง ด้วยการนำทางย่อยแบบซ้อน

นี่คือหน้าแอพหลัก

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

คำสั่งสำหรับการนำทางย่อย

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

แม่แบบสำหรับการนำทางย่อย

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

เทมเพลตสำหรับหน้าหลัก (จาก nav หลัก)

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

ตัวควบคุมสำหรับหน้าหลัก (จาก nav หลัก)

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

คำสั่งสำหรับพื้นที่ย่อย

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

9
ฉันชอบโซลูชันของ ProLoser ที่ดีกว่าเราทำบางอย่างเช่นนั้นในแอปของเราและทำงานได้ดี ปัญหาเกี่ยวกับการแก้ปัญหาของ blesh คือรหัสของตัวควบคุมที่เข้าไปในคำสั่ง โดยปกติแล้วคอนโทรลเลอร์ที่คุณระบุใน directive นั้นจะทำงานร่วมกับ directive สำหรับ ex: ngModelCtrl ซึ่งทำงานใกล้เคียงกับอินพุตและคำสั่งอื่น ๆ อย่างใกล้ชิด ในกรณีของคุณการวางรหัสคอนโทรลเลอร์ไว้ในคำสั่งจะเป็นกลิ่นรหัสจริงๆแล้วมันเป็นคอนโทรลเลอร์อิสระ
ChrisOdney

@ เนื้อดี! ดูเหมือนว่าจะอธิบายได้ดีมาก แต่ในฐานะที่เป็นโปรแกรมเมอร์ JS ที่ดีและเป็นมือใหม่ใน AngularJS ฉันไม่เข้าใจเลยว่า ... ฉันและชุมชนจะสนุกกับลิงค์ JSFiddle กับตัวอย่างการทำงานโดยใช้วิธีการนั้น การขุดรอบ ๆ นั้นจะเข้าใจง่าย! :) ขอบคุณ
Roger Barreto

8
ฉันคิดว่าวิธีแก้ปัญหานี้ควรเป็นคำตอบแรกสำหรับคำถามใด ๆ ที่ 'มุมมองแบบซ้อนไม่ทำงาน' เพียงเพราะมันใกล้กับอุดมการณ์เชิงมุมมากกว่าการใช้ ui-router และอื่น ๆ ขอบคุณ
Sergei Panfilov

ดังนั้นคำตอบคือคำสั่ง?
Marc M.

เพื่อเน้นรายการย่อยที่คุณสามารถใช้นี้: coder1.com/articles/angularjs-managing-active-nav-elementsนี่เป็นวิธีที่ดีที่จะทำหรือไม่
escapedcat

198

ปรับปรุง: ตรวจสอบโครงการใหม่ของ AngularUI เพื่อแก้ไขปัญหานี้


สำหรับส่วนย่อยมันง่ายเหมือนกับการใช้ประโยชน์จากสายอักขระใน 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>

หรือคุณสามารถสร้างวัตถุในกรณีที่คุณมีลิงค์ไปยังหน้าย่อยทั่วสถานที่:

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

หรือคุณสามารถใช้ $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>

คุณยังสามารถวาง ng-controller ไว้ที่ระดับบนสุดของแต่ละส่วน


8
ฉันชอบทางออกของคุณดีกว่า ฉันเป็น newb ถึง Angular และดูเหมือนว่าจะเข้าใจได้ดีกว่าว่าฉันจะดูเว็บได้อย่างไรจนถึงทุกวันนี้ ใครจะรู้อาจเป็น blesh ใช้กรอบงานเชิงมุมมากขึ้นในการทำมัน แต่ดูเหมือนว่าคุณจับมันด้วยรหัสบรรทัดน้อยลงในทางที่สมเหตุสมผลมากขึ้น ขอบคุณ!
Gleeb

2
@ProLooser คุณอาจหมายถึงลิงค์นี้github.com/angular-ui/ui-router ... สิ่งที่คุณวางถูกทำลาย
simonC

4
ฉันมีปัญหาการออกแบบเหมือนกับ OP มีใครลองใช้กลไกของรัฐ AngularUI หรือไม่? ฉันค่อนข้างลังเลที่จะใช้ห้องสมุดของบุคคลที่สามอีกแห่งและฉันก็อยากจะใช้วิธีการทำสิ่งต่าง ๆ ของ AngularJS แต่ในทางกลับกันระบบเส้นทางดูเหมือนว่าจะเป็นจุดอ่อนของ Achilles ... @PhillipKregg คุณใช้อะไรเพื่อแก้ไขสถานการณ์นี้?
แซม

4
คุณสามารถระบุ<div ng-include="'/home/' + tab + '.html'" ng-controller="SubCtrl"></div>เพื่อใช้คอนโทรลเลอร์ / ขอบเขตแยกต่างหากสำหรับเทมเพลตย่อย หรือเพียงแค่ระบุngControllerคำสั่งที่ใดก็ได้ภายในเทมเพลตย่อยของคุณเพื่อใช้คอนโทรลเลอร์ที่แตกต่างกันสำหรับแต่ละส่วน
colllin

2
@DerekAdair โครงการมีความเสถียรพอสมควร (แม้จะมีหมายเลขเวอร์ชั่น) และมีการใช้งานในการผลิตไม่กี่แห่ง มันป้องกันการรีโหลดคอนโทรลเลอร์ที่ไม่จำเป็นและเป็นทางเลือกที่ดีกว่าโซลูชันที่แนะนำของฉัน
ProLoser

26

คุณสามารถชำระเงินห้องสมุดนี้เพื่อจุดประสงค์เดียวกันได้เช่นกัน:

http://angular-route-segment.com

ดูเหมือนว่าคุณกำลังมองหาและใช้งานง่ายกว่า ui-router จากเว็บไซต์ตัวอย่าง :

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 ระดับบนสุด:

<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 ที่ซ้อนกัน:

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

Artem ของคุณเป็นทางออกที่ดีที่สุดที่ฉันเคยพบมา! ฉันชอบความจริงที่ว่าคุณขยายเส้นทางเชิงมุมมากกว่าที่จะแทนที่มันตามที่ UI เชิงมุมทำ
LastTribunal

17

ฉันก็กำลังดิ้นรนกับมุมมองซ้อนในเชิงมุม

เมื่อฉันได้รับการจับui- เราเตอร์ฉันรู้ว่าฉันไม่เคยกลับไปที่ฟังก์ชั่นการกำหนดเส้นทางเริ่มต้นเชิงมุม

นี่คือตัวอย่างแอปพลิเคชันที่ใช้การซ้อนมุมมองหลายระดับ

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

ในขณะที่มันสามารถเห็นได้มี 4 มุมมองหลัก (มุมมอง 1, มุมมอง 2, มุมมอง 3, มุมมอง 4) และมุมมอง 1 มีมุมมองที่เด็ก 3


2
จุดประสงค์ของการฉีด$httpProviderคืออะไร? ฉันไม่เห็นมันใช้ที่ใดก็ได้
Aaron

@Aaron app.config ไม่ได้จบในชิ้นส่วนของรหัสนี้และอาจจะเป็น $ httpProviderจะใช้ที่อื่น
Vlad

4

คุณสามารถใช้ ng-include เพื่อหลีกเลี่ยงการใช้ ng-views ที่ซ้อนกัน

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

หน้าดัชนีของฉันฉันใช้มุมมอง ng จากนั้นในหน้าย่อยของฉันซึ่งฉันต้องมีเฟรมซ้อนกัน ฉันใช้ ng-include ตัวอย่างแสดงรายการแบบเลื่อนลง ฉันแทนที่ฉันด้วยลิงค์ ng-click ในฟังก์ชั่นฉันจะใส่ $ scope.template = $ scope.templates [0]; หรือ $ scope.template = $ scope.templates [1];

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

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.