Как написать директиву в AngularJS, как мне решить, не нужна ли мне новая область, новая дочерняя область или новая изолированная область?

262

Я ищу некоторые рекомендации, которые можно использовать, чтобы определить, какой тип области применения использовать при написании новой директивы. В идеале, мне бы хотелось что-то похожее на блок-схему, которая проведет меня через кучу вопросов и выйдет правильный ответ – новой области видимости, новой области содержимого или новой области выделения; но это, скорее всего, требует слишком многого. Вот мой текущий ничтожный набор рекомендаций:

Я знаю, что использование директивы с изолированной областью действия над элементом заставляет все другие директивы того же элемента использовать одну и ту же (одну) область выделения, поэтому это не сильно ограничивает возможность использования области выделения

Я надеюсь, что некоторые из команды Angular -UI (или другие, написавшие много директив) могут поделиться своим опытом.

Пожалуйста, не добавляйте ответ, который просто говорит: "Используйте изолированную область для повторно используемых компонентов".

  • 0
    под «дочерней областью» вы подразумеваете создание области действия в функции ссылки под «областью действия. $ new ()»? Поскольку, как я знаю, директива может иметь изолированную область видимости или не иметь ее (поэтому будет использовать область видимости, где она используется)
  • 3
    @ValentynShybanov Установка scope: true автоматически создаст $scope.new() область, используя $scope.new() .
Показать ещё 3 комментария
Теги:
angularjs-scope
angularjs-directive

5 ответов

269
Лучший ответ

Какой замечательный вопрос! Мне бы хотелось услышать, что говорят другие, но вот рекомендации, которые я использую.

Высотная посылка: область используется как "клей", который мы используем для связи между родительским контроллером, директивой и шаблоном директивы.

Родительский охват: scope: false, поэтому никакой новой области вообще

Я не использую это очень часто, но, как сказал @MarkRajcok, если директива не имеет доступа к каким-либо переменным области видимости (и, очевидно, не устанавливает никаких!), то это прекрасно, насколько мне известно. Это также полезно для дочерних директив, которые используются только в контексте родительской директивы (хотя всегда есть исключения из этого) и которые не имеют шаблона. В принципе, что-либо с шаблоном не принадлежит совместному использованию области, потому что вы по сути раскрываете эту область для доступа и манипуляций (но я уверен, что есть исключения из этого правила).

В качестве примера я недавно создал директиву, которая рисует (статическую) векторную графику с использованием библиотеки SVG, которую я нахожу в процессе написания. Это $observe два атрибута (width и height) и использует их в своих вычислениях, но он не устанавливает и не читает никаких переменных области и не имеет шаблона. Это хороший прецедент для не создания другой области; нам это не нужно, так зачем беспокоиться?

Однако в другой директиве SVG мне потребовался набор данных для использования и дополнительно должен был хранить крошечный бит состояния. В этом случае использование родительской области было бы безответственным (опять же, вообще говоря). Поэтому вместо этого...

Область ребенка: scope: true

Директивы с дочерней областью являются контекстно-зависимыми и предназначены для взаимодействия с текущей областью.

Очевидно, что ключевым преимуществом этого в области выделения является то, что пользователь может свободно использовать интерполяцию по любым атрибутам, которые они хотят; например использование class="item-type-{{item.type}}" в директиве с областью выделения не будет работать по умолчанию, но отлично работает на одном с дочерней областью, потому что все, что было интерполировано, по-прежнему может быть найдено в родительской области. Кроме того, сама директива может безопасно оценивать атрибуты и выражения в контексте своей области, не беспокоясь о загрязнении или повреждении родителя.

Например, всплывающая подсказка - это то, что просто добавляется; область изоляции не будет работать (по умолчанию, см. ниже), потому что ожидается, что мы будем использовать другие директивы или интерполированные атрибуты здесь. Всплывающая подсказка - это просто усовершенствование. Но всплывающая подсказка также должна установить некоторые вещи в области использования с суб-директивой и/или шаблоном и, очевидно, управлять своим собственным состоянием, поэтому было бы очень плохо использовать родительскую область. Мы либо загрязняем его, либо наносим ему ущерб, и ни одно из них не является буено.

Я использую дочерние области чаще, чем изолировать или родительские области.

Изолировать область действия: scope: {}

Это для многоразовых компонентов.:-)

Но серьезно, я думаю о "многоразовых компонентах" как о "автономных компонентах". Цель состоит в том, что они должны использоваться для определенной цели, поэтому комбинирование их с другими директивами или добавление других интерполированных атрибутов в DOM node по сути не имеет смысла.

Чтобы быть более конкретным, все, что необходимо для этой автономной функции, предоставляется через определенные атрибуты, оцениваемые в контексте родительской области; это либо односторонние строки ('@'), односторонние выражения ('&'), либо двусторонние привязки переменных ('=').

В автономных компонентах нет смысла применять другие директивы или атрибуты, поскольку он существует сам по себе. Его стиль регулируется собственным шаблоном (при необходимости) и может содержать соответствующий контент (при необходимости). Это автономно, поэтому мы помещаем его в изоляционную область, чтобы также сказать: "Не связывайтесь с этим. Я даю вам определенный API через эти несколько атрибутов".

Хорошей практикой является исключить из набора директивных ссылок и функций контроллера как можно больше материалов на основе шаблонов. Это обеспечивает другую конфигурационную точку, подобную API-интерфейсу: пользователь директивы может просто заменить шаблон! Функциональность все осталась прежней, и ее внутренний API никогда не касался, но мы можем возиться со стилем и реализацией DOM столько, сколько нам нужно. ui/bootstrap - отличный пример того, как это сделать хорошо, потому что Питер и Павел потрясающие.

Изолирующие области также отлично подходят для использования с переходом. Взять вкладки; они являются не только всей функциональностью, но все, что внутри нее, можно свободно оценивать изнутри родительской области, оставляя вкладки (и панели) делать все, что они хотят. На вкладках явно есть собственное состояние, которое входит в сферу действия (для взаимодействия с шаблоном), но это состояние не имеет ничего общего с контекстом, в котором он был использован, - он полностью внутренне относится к тому, что делает директиву табуляции директивой табуляции. Кроме того, не имеет смысла использовать какие-либо другие директивы с вкладками. Они - вкладки - и у нас уже есть эта функциональность!

Объединить его с большей функциональностью или перекрыть больше функциональности, но директива уже такова.

Все, что было сказано, я должен отметить, что существуют некоторые способы ограничения (то есть функции) области выделения, как @ProLoser намекнул в его ответе. Например, в разделе детской области я упомянул интерполяцию по не директивным атрибутам, нарушающим при использовании области изоляции (по умолчанию). Но пользователь мог бы, например, просто использовать class="item-type-{{$parent.item.type}}", и он снова будет работать. Поэтому, если есть веская причина использовать область выделения для дочерней области, но вы обеспокоены некоторыми из этих ограничений, знайте, что вы можете обойти практически все из них, если вам нужно.

Резюме

Директивы без новой области доступны только для чтения; они полностью доверяют (т.е. внутри приложения), и они не касаются гнезда. Директивы с дочерней областью добавляют функциональность, но они не являются единственной функциональностью. Наконец, изолировать области действия для директив, которые являются цельной целью; они автономны, поэтому все в порядке (и большинство "правильных" ), чтобы позволить им идти изгоями.

Я хотел получить свои первоначальные мысли, но, как я думаю о других вещах, я обновлю это. Но святое дерьмо - это долго для ответа SO...


PS: Полностью тангенциальный, но, поскольку мы говорим о областях, я предпочитаю говорить "прототип", тогда как другие предпочитают "прототип", что кажется более точным, но просто отскакивает от языка совсем не хорошо.: -)

  • 0
    Спасибо, Джош, отличный ответ. Я хотел / ожидал длинных ответов для этого. Я не следовал двум вещам: 1) дочерняя область: «пользователь может использовать интерполяцию для любых атрибутов, которые он хочет». 2) выделить сферу: «или не все, в случае«? »» Можете ли вы немного рассказать об этом? (Не стесняйтесь редактировать свое сообщение вместо того, чтобы писать комментарии, если это проще.)
  • 0
    @MarkRajcok Для (1) я изменил его, чтобы сделать его немного менее туманным - дайте мне знать, если я потерпел неудачу. Для (2) это была комбинация опечатки и плохой формулировки; Я переписал этот абзац, чтобы было понятнее. Я также добавил дополнительный пример или два, уточнил еще несколько вещей и исправил некоторые опечатки.
Показать ещё 6 комментариев
50

Моя личная политика и опыт:

Изолирован: отдельная песочница

Я хочу создать множество методов и переменных области, которые ТОЛЬКО используются моей директивой и никогда не видны или напрямую не доступны пользователю. Я хочу, чтобы белый список доступных мне данных. Я могу использовать переход, чтобы позволить пользователю вернуться в родительскую область (без изменений). Я НЕ хочу, чтобы мои переменные и методы были доступны для заблокированных детей.

Ребенок: подраздел содержимого

Я хочу создать методы и переменные области, которые CAN будет доступен пользователю, но не имеют отношения к окружающим областям (родным и родителям) вне контекста моей директивы. Я также хотел бы, чтобы данные родительской области полностью просачивались прозрачно.

Нет: простые, доступные только для чтения директивы

Мне действительно не нужно возиться с методами или переменными области. Я, вероятно, делаю то, что не связано с областями (например, отображение простых плагинов jQuery, проверка и т.д.).

Примечания

  • Вы не должны позволять ngModel или другим вещам напрямую влиять на ваше решение. Вы можете обойти нечетное поведение, делая такие вещи, как ng-model=$parent.myVal (дочерний) или ngModel: '=' (изолировать).
  • Изоляция + transclude приведет к восстановлению всех нормальных действий директиву сиблинга и возвращается в родительскую область, поэтому не позволяйте этому влиять и на ваше мнение.
  • Не связывайтесь с областью действия none, потому что это похоже на помещение данных в область для нижней половины DOM, но не в верхнюю половину, которая имеет смысл 0.
  • Обратите внимание на приоритеты директив (у вас нет конкретных примеров того, как это может повлиять на вещи).
  • Ввести службы или использовать контроллеры для связи по директивам с любым типом области. Вы также можете сделать require: '^ngModel' для просмотра в родительских элементах.
  • 1
    Возможно, я неправильно понял эту часть: «Isolate + transclude восстановит все нормальное поведение в директивах братьев и сестер». Смотрите этот плункер . Вам придется заглянуть в консоль.
  • 1
    Спасибо ProLoser за ваши идеи / ответ. Вы один из тех, на кого я надеялся увидеть этот пост, если бы я добавил тег angularjs-ui.
Показать ещё 3 комментария
18

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

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

Если директива, которую вы собираетесь писать, будет просто делать манипуляции с dom, которая не нуждается в внутреннем состоянии области видимости или явных изменениях в сфере (в основном, очень простые вещи); не ищите новых областей. (например, ngShow, ngMouseHover, ngClick, ngRepeat)

Если директива, которую вы собираетесь писать, должна изменить некоторые элементы в родительской области, но также должна обрабатывать некоторое внутреннее состояние, перейдите к новой области содержимого. (например, ngController)

Обязательно проверьте исходный код для директив: https://github.com/angular/angular.js/tree/master/src/ng/directive
Это очень помогает в том, как думать о них.

  • 0
    Если несколько компонентов должны взаимодействовать друг с другом, они могут иметь изолированную область применения и require использования, поэтому ваши директивы по-прежнему не связаны. Так как же это ограничивает возможности? Это еще более делает директивы более конкретными (поэтому объявляйте, от чего вы зависите). Поэтому я бы оставил только одно правило: если ваша директива имеет состояние или нуждается в каких-либо данных из области видимости, где она используется - используйте изолированную область. В противном случае не используйте область. И о «детских сферах» - я также написал довольно много директив и никогда не нуждался в этой функции. Если «необходимо изменить некоторые элементы в родительской области» - используйте привязки.
  • 0
    А также о «необходимости изменить некоторые элементы в родительской области» - если вы изменяете что-либо в дочерней области, изменения не заполняются в родительской области (если вы не используете грязный $parent хак). Таким образом, на самом деле «дочерние области» для директив - это то, что выглядит так, как если бы они использовались задним ngRepeat - например, ngRepeat который создает новые дочерние области для каждого повторяемого элемента (но он также создает его с помощью scope.$new(); ngRepeat scope.$new(); а не scope: true ,
Показать ещё 8 комментариев
8

Просто подумал, что добавлю свое нынешнее понимание и как оно относится к другим концепциям JS.

Значение по умолчанию (например, не объявлено или область: ложь)

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

Объем: {}

Это похоже на модуль, все, что он хочет использовать, необходимо передать явно. Если директива EVERY, которую вы используете, является областью изоляции, она может быть эквивалентна созданию КАЖДОГО JS файла, который вы пишете свой собственный модуль с большим количеством накладных расходов при инъекции всех зависимостей.

область: дочерний

Это промежуточная точка между глобальными переменными и явной транзитной пересылкой. Он похож на цепочку прототипов javascript и просто расширяет вашу копию родительской области. Если вы создаете область изоляции и передаете каждый атрибут и функцию родительской области, она функционально эквивалентна этому.


Ключ в том, что ЛЮБАЯ директива может быть написана ЛЮБОЙ путь. Здесь вы можете различать различные объявления областей, которые помогут вам организовать. Вы можете сделать все модули, или вы можете просто использовать все глобальные переменные и быть очень осторожными. Для удобства обслуживания, хотя предпочтительнее модулярность вашей логики в логически когерентные части. Существует баланс между открытым лугом и закрытым тюремным залом. Причина, по которой это сложно, я считаю, что когда люди узнают об этом, они думают, что они узнают о том, как работают директивы, но на самом деле они изучают организацию кода/логики.

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

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

2

Я согласен с Умуром. Теоретически, изолированные области звучат замечательно и "переносимы", но при создании моего приложения для привлечения нетривиальной функциональности мне пришла в голову необходимость включения нескольких директив (некоторые вложенные внутри других или добавления атрибутов к ним), чтобы полностью написать в моем собственный HTML-код, который является целью определенного для домена языка.

В конце концов, слишком странно передавать каждое глобальное или общее значение в цепочку с несколькими атрибутами при каждом вызове DOM директивы (как это требуется с областью выделения). Он просто выглядит немым, чтобы многократно писать все это в DOM, и он чувствует себя неэффективно, даже если это общие объекты. Это также излишне усложняет декларации директив. Обходной путь использования $parent для "достижения" и получения значений из директивы HTML кажется очень плохим.

Я тоже запустил изменение своего приложения, чтобы иметь в основном директивы для дочерних областей с очень небольшим количеством изолятов - только те, которые не нуждаются в доступе к НИЧЕГО от родителя, кроме того, что они могут быть переданы простым, неповторяющимся атрибуты.

С тех пор, как я мечтал о мечтах о доменных специфических языках за десятилетия до того, как появилась такая вещь, я был в восторге от того, что AngularJS предоставляет этот вариант, и я знаю, что, поскольку больше разработчиков работают в этой области, мы увидим некоторые классные приложения, которые также легко для их архитекторов писать, расширять и отлаживать.

- D

Ещё вопросы

Сообщество Overcoder
Наверх
Меню