2013-03-07 2 views
1

Я борюсь с одним вопросом и не могу понять, почему это происходит.Как действуют директивы в Angularjs?

У меня есть модуль (портфолиоModule), сервис (меню) и директива (niMenu). Директива должна отображать пункт меню с его подпунктами.

HTML:

<div ng-app="portfolioModule"> 
    <script id="menuItem" type="text/ng-template"> 
    <li> 
     <a>{{item.name}}</a> 
     <ul> 
      <li ng-repeat="item in menu.getKids(item.id)" ng-include="'menuItem'"></li> 
     </ul> 
    </li> 
    </script> 

    <div> 
     <div ni-menu="test">test1</div> 
    </div> 
</div> 

JavaScript:

var portfolioModule = angular.module('portfolioModule', []); 

portfolioModule.factory('Menu', function() { 
     var items = [ 
      { 
       "parent_id": null, 
       "id": 1, 
       "name": "foo" 
      }, { 
       "parent_id": 1, 
       "id": 2, 
       "name": "foo-bar" 
      } 
     ]; 
     this.getKids = function(parent_id) { 
      var result = []; 
      parent_id = parent_id || null; 

      console.log('getKids', parent_id); 
      angular.forEach(items, function(value) { 
       if (value.parent_id === parent_id) { 
        result.push(value); 
       } 
      }); 
      return result; 
     }; 

     return this; 
    } 
); 

portfolioModule.directive('niMenu', function(Menu) { 
     var niMenu; 
     return niMenu = { 
      template: '<ul ng-include="\'menuItem\'" ng-repeat="item in menu.getKids()"></ul>', 
      restirt: 'A', 
      link: function(scope, element, attrs) { 
       console.log('link'); 
       scope.menu = Menu; 
      } 
     }; 
    } 
); 

Работа демо: http://jsfiddle.net/VanSanblch/Ng7ef.

В html Я называю niMenu ni-menu. Директива имеет шаблон и функцию связи, которые помещают Menu-service в объем модуля. В шаблоне директивы я использую Menu.getKids() и получаю все элементы верхнего уровня. Позже, в шаблоне, который используется ng-include, я вызываю Menu.getKids (item.id) и получаю всех детей определенного элемента.

Все работает отлично, за исключением небольшой детали. Если вы откроете консоль, вы можете заметить, что есть гораздо больше вызовов getKids, чем я ожидаю. Например, для массива из двух элементов число вызовов getKids равно девяти.

Может ли кто-нибудь объяснить, почему на земле это происходит?

+1

К сожалению, вы отказались от ссылки на свой Скрипт. :-) –

+1

@JoshDavidMiller исправно. –

ответ

6

Хорошо, поэтому причина, по которой он выполняется более одного раза, состоит в том, что так работает цикл дайджест: он непрерывно выполняет все выражения вида (например, тот, который вы передаете ngRepeat), пока он не выражает никаких изменений. См. this answer для получения дополнительной информации, но вынос таков: он всегда будет выполнять хотя бы один раз, но часто больше.

При использовании ngRepeat вы обычно хотите избежать сбора данных из функции, поскольку это отрицательно влияет на производительность; зачем вызывать одну и ту же функцию более одного раза, когда данные никогда не менялись? Лучший подход заключается в том, чтобы ваш контроллер (или в этом случае директива) выполнял вашу функцию и сохранял результаты в области, поэтому ваш ngRepeat выглядит как ng-repeat="item in items" вместо ng-repeat="item in getItems()".

К сожалению, это означает, что вам необходимо реструктурировать способ работы вашей директивы. В любом случае это будет хорошей идеей, потому что ваша директива может быть переписана, чтобы быть немного проще - например, вам не нужен какой-либо ngInclude.

Что вы хотите сделать, это создать две директивы, потому что у вас есть два шаблона: один для представления общего меню и один для представления дочернего элемента (который, в свою очередь, может иметь дочерние элементы). Директива menu должна повторяться над пунктами меню верхнего уровня, рисуя menuItem для каждого из них. Директива menuItem должна проверять наличие детей и повторять их, если необходимо, с более menuItem с. И т. Д. Вот пример, созданный Энди Джослином о том, как вы можете выполнить рекурсивный каталог: http://plnkr.co/edit/T0BgQR.

Чтобы реализовать что-то подобное, выходит за рамки этого вопроса, но сделайте удар по нему и опубликуйте новый вопрос, если вам нужна помощь.

Удачи вам!

+0

Удивительный! Это определенно проясняет все. Спасибо за ваш ответ. – vansanblch

+0

Отличная директива! Просто заметьте всем, кто хочет использовать этот пример в плункере.Было бы лучше определить «choiceDirective» длинный ручной способ избежать проблем при использовании миниуровщиков и улугентов. Поэтому вместо 'app.directive ('choice', function ($ compile)' use 'app.directive ('choice', ['$ compile', function ($ compile)'. Не забывайте, что вам нужно будет добавьте закрытие ']' в конце метода директивы. –

Смежные вопросы