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


265

Я шукаю деякі вказівки, які можна використовувати, щоб допомогти визначити, який тип сфери застосування використовувати при написанні нової директиви. В ідеалі, я хотів би щось схоже на блок-схему, яка розглядає мене через купу запитань і відображає правильну відповідь - немає нової нової сфери дії, нової сфери дії дитини чи нової області ізоляції - але це, ймовірно, вимагає занадто багато. Ось мій поточний набір керівних принципів:

Мені відомо, що використання директиви з ізольованою областю на елементі змушує всі інші директиви цього ж елемента використовувати ту саму (одну) область ізоляції, тож чи не це суворо обмежує, коли може бути використана область ізоляції?

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

Будь ласка, не додайте відповідь, у якій просто сказано: "використовувати ізольоване поле для компонентів для багаторазового використання".


Під "дочірньою сферою" ви маєте на увазі створення області в функції зв’язку за допомогою "область. Тому що, як я знаю, директива може бути ізольованою сферою дії або не мати її (так буде використовувати область, де вона використовується)
Валентин Шибанов

3
Налаштування @ValentynShybanov scope: trueстворить область для дитини, використовуючи $scope.new()автоматично.
Джош Девід Міллер

2
@Valentyn, що Джош сказав: так, три можливості - це scope: false(за замовчуванням, без нового сфери застосування), scope: true(новий діапазон, який успадковується прототипно), і scope: { ... }(нова область ізоляції).
Марк Райкок

1
Так, thnx. Я пропустив цю різницю між "справжнім" і "{}". Добре знати.
Валентин Шибанов

Є четвертий випадок, який люди, як правило, ігнорують .. це "контролер директив". Я думаю, що питання слід розширити, щоб включити їх також ... +1 до питання ..
ganaraj

Відповіді:


291

Яке чудове запитання! Я люблю , щоб почути те , що кажуть інші, але ось принципи , які я використовую.

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

Область для батьків:, scope: false отже, жодної нової сфери

Я не використовую це дуже часто, але, як сказав @MarkRajcok, якщо директива не отримує доступу до змінних областей (і, очевидно, не встановлює жодної!), То це добре, наскільки я переживаю. Це також корисно для дочірніх директив, які використовуються лише в контексті батьківської директиви (хоча з цього завжди є винятки) і не мають шаблону. В основному все, що стосується шаблону, не належить до спільного використання сфери, тому що ви по суті викриваєте це поле для доступу та маніпулювання (але я впевнений, що з цього правила є винятки).

Як приклад, я нещодавно створив директиву, яка малює (статичну) векторну графіку за допомогою бібліотеки SVG, яку я перебуваю в процесі написання. Він має $observeдва атрибути ( widthі height) і використовує їх у своїх обчисленнях, але він не встановлює і не читає жодних змінних областей і не має шаблону. Це хороший випадок використання для створення іншого обсягу; нам це не потрібно, то навіщо турбуватися?

Але в іншій директиві SVG мені потрібен набір даних для використання, а також потрібно було зберігати крихітний стан. У цьому випадку використання батьківської області було б безвідповідальним (знову ж таки, загалом кажучи). Тож замість цього ...

Область застосування дитини: scope: true

Директиви, що стосуються сфери використання дитини, усвідомлюються контекстом і призначені для взаємодії з поточною сферою застосування.

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

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

Я вважаю, що частіше використовую область застосування дітей, ніж ізолюючу та батьківську сфери.

Ізоляція сфери застосування: scope: {}

Це для багаторазових компонентів. :-)

Але серйозно, я вважаю "багаторазові компоненти" як "автономні компоненти". Намір полягає в тому, що вони повинні використовуватися для певної мети, тому поєднання їх з іншими директивами або додавання інших інтерпольованих атрибутів до вузла DOM по суті не має сенсу.

Щоб бути більш конкретним, все, що потрібно для цієї самостійної функціональності, надається через визначені атрибути, оцінені в контексті батьківської області; вони є або односторонніми рядками ('@'), односторонніми виразами ('&'), або двосторонніми змінними прив'язками ('=').

На автономних компонентах не має сенсу застосовувати інші директиви чи атрибути, оскільки вони існують самі по собі. Її стиль регулюється власним шаблоном (за необхідності) і може містити відповідний зміст (за необхідності). Це автономно, тому ми ставимо його в окрему область, щоб також сказати: "Не псуйтеся з цим. Я даю вам визначений API за допомогою цих кількох атрибутів".

Найкраща найкраща практика - виключити якомога більше матеріалів на основі шаблонів із директивних посилань та функцій контролера. Це забезпечує ще одну точку конфігурації "подібної API": користувач директиви може просто замінити шаблон! Усі функціональні можливості залишалися однаковими, і його внутрішній API ніколи не торкався, але ми можемо возитися зі стилізацією та реалізацією DOM стільки, скільки нам потрібно. ui / bootstrap - чудовий приклад того, як це зробити добре, оскільки Пітер і Пауль - приголомшливі.

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

Оточуйте його з більшою функціональністю або виключаєте більше функціональності, але директива є такою, яка вона є вже зараз.

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

Підсумок

Директиви, які не мають нової сфери застосування, є лише для читання; їм повністю довіряють (тобто внутрішні програми), і вони не торкаються гнізда. Директиви з дочірньою сферою додають функціональності, але вони не єдина функціональність. Нарешті, виділити сфери застосування для директив, які є цілою ціллю; вони є окремими, тому добре (і більшість «правильних») відпускати їх ізгоями.

Мені хотілося вияснити свої початкові думки, але, як я думаю над іншими речами, я це оновлю. Але святе лайно - це довго відповідь ТАК ...


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


Дякую Джошу, чудова відповідь. Я хотів / очікував на це довгих відповідей. Дві речі, за якими я не дотримувався: 1) дочірня область: "користувач може використовувати інтерполяцію за будь-якими атрибутами, які він хоче". 2) ізолюйте сферу дії: "чи не все, у випадку"? "" Чи можете ви трохи розробити це? (Не соромтесь редагувати свою публікацію замість того, щоб писати коментарі, якщо це простіше.)
Марк Райкок

@MarkRajcok Для (1) я змінив його, щоб зробити його трохи менш туманним - дайте мені знати, якщо я не вдався. Для (2) це було поєднанням друкарської помилки та поганого формулювання; Я переписав цей параграф, щоб бути зрозумілішим. Я також додав додатковий приклад-два, уточнив ще кілька речей і зафіксував деякі помилки.
Джош Девід Міллер

Як було сказано у відповіді - чудовий приклад для кутових є чудовим прикладом їх поєднання. Я знайшов приклад акордеона особливо корисним - GitHub - Акордеон
CalM

Ви згадали, що найбільше використовуєте дитячі сфери, я вважав, що шаблон директив, який використовується багаторазово, є найпоширенішим, і я уникав писати директиви, які мали на увазі використовуватись лише один раз. Це не потрібно? Іноді, коли мій HTML стає занадто великим, я відчуваю, що переміщую цей розділ у директиву, але він буде використаний лише один раз, тому я просто залишаю його в html.
користувач2483724

2
@ user2483724 Дуже поширене неправильне уявлення про те, що директиви "для багаторазового використання" - це ті, що використовують область ізоляції; не так. Якщо ви подивитеся на попередньо упаковані директиви, майже жодна з них не використовує ізоляційні прилади - деякі навіть не стосуються дітей - але я запевняю, що вони багаторазові! Правило повинно полягати в тому, як використовується сфера дії директиви. Якщо мова йде лише про економію місця у файлі, я не впевнений, що директива є найкращим підходом. Це збільшує час обробки заради розробника. Але якщо треба, то йдіть за цим. Або скористайтеся ngInclude. Або зробіть це як частина вашої побудови. Багато варіантів!
Джош Девід Міллер

52

Моя особиста політика та досвід:

Ізольована: приватна пісочниця

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

Дитина: підрозділ змісту

Я хочу створити методи застосування та змінні, до яких МОЖЕ отримати доступ користувач, але не стосуються оточуючих областей (братів і сестер та батьків) поза контекстом моєї директиви. Я також хотів би дозволити ВСІМ даних батьківських областей чітко простежуватись.

Ні: прості директиви, доступні лише для читання

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

Примітки

  • Ви не повинні дозволяти ngModel або іншим предметам безпосередньо впливати на ваше рішення. Ви можете обійти дивну поведінку, зробивши такі речі, як ng-model=$parent.myVal(дитина) або ngModel: '='(ізолювати).
  • Isolate + transclude відновить всю нормальну поведінку до директив побратимів і повернеться до батьківської області, тому не дозволяйте це також впливати на ваше судження.
  • Не возиться з областю ні на одній, тому що це як розміщення даних про область для нижньої половини DOM, але не верхньої половини, що має значення 0.
  • Зверніть увагу на пріоритети директив (не майте конкретних прикладів, як це може впливати на речі)
  • Введіть сервіси або використовуйте контролери для спілкування через директиви з будь-яким типом області. Ви також require: '^ngModel'можете шукати батьківські елементи.

1
Я, можливо, неправильно зрозумів цю частину: "Isolate + transclude відновить всю нормальну поведінку до настанов щодо рідних братів". Дивіться цей плаунер . Вам доведеться заглянути в консоль.
Джош Девід Міллер

1
Дякуємо ProLoser за розуміння / відповідь. Ви один із людей, яких я сподівався побачити цю публікацію, якщо я додаю тег angularjs-ui.
Марк Райкок

@JoshDavidMiller, коли говорити про директиви одного і того ж елемента DOM, справи ускладнюються, і вам слід замість цього поглянути на властивість пріоритету. Трансклюзія більше стосується вмісту дітей.
ProLoser

1
@ProLoser Правильно, але я не впевнений, що ви мали на увазі під цим твердженням. Вони, очевидно, впливають на дітей, але як сфера дії директив взагалі впливає на їх директиви щодо брати і сестер?
Джош Девід Міллер

18

Написавши багато директив, я вирішив використовувати менший isolatedобсяг. Незважаючи на те, що це круто і ви інкапсулюєте дані та не переконуєтесь, що вони не просочуються до батьківської області, це сильно обмежує кількість директив, які ви можете використовувати разом. Так,

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

Якщо директива, яку ви збираєтеся писати, збирається просто здійснити маніпуляції з домом, які не потребують внутрішнього стану обсягу або явних змін обсягу (в основному дуже простих речей); займатись новим розмахом . (Таких , як ngShow, ngMouseHover, ngClick, ngRepeat)

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

Не забудьте перевірити вихідний код щодо директив: https://github.com/angular/angular.js/tree/master/src/ng/directive
Це дуже допомагає, як думати про них


Якщо декілька компонентів потребують зв'язку один з одним, вони можуть мати ізольований обсяг та використання require, тому зберігаючи ваші директиви, як і раніше, не виводити з ладу. То як це обмежує можливості? Це ще більше робить директиви більш конкретними (тому заявляйте, від чого ви залежите). Тож я б лишив лише одне правило: якщо у вашій директиві є стан або потрібні деякі дані з області, де вона використовується, - використовуйте окрему область. В іншому випадку не використовуйте область застосування. А щодо "дитячих областей" - я також написав досить багато директив і ніколи не потребував цієї функції. Якщо "потрібно змінити деякі елементи в батьківській області" - використовуйте прив'язки.
Валентин Шибанов

А також про "потребує зміни деяких елементів у батьківській області" - якщо ви щось модифікуєте в дочірній області, зміни не заносяться в батьківський обсяг (якщо ви не використовуєте брудний $parentзлом). Тож насправді «дитячі сфери застосування» для директив - це те, що, схоже, слід використовувати досить ззаду - як, ngRepeatщо створює нові області застосування для кожного повторення (але це також створюється, використовуючи, scope.$new();а не scope: true.
Валентин Шибанов,

1
Ви не можете запитувати декілька ізольованих областей в одному елементі, ви не можете отримати доступ до функцій у батьківському діапазоні, якщо ви прямо не зв’яжете їх. (Удачі, використовуючи ngClickі т. Д.) Вимога вимагає створення певного роз'єднання, я згоден, але ви все одно повинні знати про батьківську директиву. Якщо це не як компонент , я проти йти на ізоляцію. Директиви (принаймні, більшість із них) мають бути для багаторазового використання, і ізоляція це порушує.
Umur Kontacı

Я також не використовую дочірнє поле в директивах, але оскільки дочірній області прототипічно успадковується від батьківської області, якщо доступ до власності в межах власності в батьківській області, зміни заповнюються. Автори Angular розповіли про це під час зустрічі на MTV, "добре десь мати крапку" youtube.com/watch?v=ZhfUv0spHCY
Umur Kontacı

5
По-перше, я думаю, ви трохи суворі в ізоляції. Я думаю, що вони мають більш широке застосування, ніж ти їм похвалишся за те, що існує багато способів уникнути багатьох проблем, з якими ти (правильно) зазначив, що ми стикаємось з ними. Я також не погоджуюся з "не надто налаштуванням для кінцевого розробника" - детальну інформацію див. У моїй відповіді. Це сказало, що ваша відповідь не була ні поганою, ні неправильною, і вона вирішила це питання, тому я не впевнений, чому вона визнана голосною. Отже, +1.
Джош Девід Міллер

9

Я просто подумав, що я би додав своє сучасне розуміння та як це стосується інших концепцій JS.

За замовчуванням (наприклад, не задекларовано чи сфера застосування: false)

Це по-філософськи еквівалентно використанню глобальних змінних. Ваша директива може отримати доступ до всього, що знаходиться в батьківському контролері, але це також впливає на них і впливає одночасно.

сфера дії: {}

Це як модуль, все, що він хоче використовувати, потрібно передати чітко. Якщо ВСЯКА директива, яку ви використовуєте, є ізоляційною сферою, вона може бути еквівалентом створення файлу КОЖНОГО JS, ви пишете власний модуль з великими накладними витратами на введення всіх залежностей.

сфера застосування: дитина

Це середина між глобальними змінними та явним переходом. Це схоже на ланцюжок прототипу javascript і просто розширює вам копію батьківського діапазону. Якщо ви створюєте область ізоляції та передаєте кожен атрибут та функцію батьківського діапазону, він функціонально еквівалентний цьому.


Ключовим є те, що будь-яка директива може бути написана будь-яким способом. Різні декларації про сферу існування є лише для того, щоб допомогти вам організувати. Ви можете зробити все модулем, а можете просто використовувати всі глобальні змінні та бути дуже обережними. Для зручності обслуговування, хоча краще модулювати свою логіку на логічно узгоджені частини. Існує баланс між відкритим лугом та закритим в'язничним будинком. Причина, в якій це складно, я вважаю, що коли люди дізнаються про це, вони думають, що вони дізнаються про те, як працюють директиви, але насправді вони дізнаються про організацію коду / логіки.

Ще одна річ, яка допомогла мені зрозуміти, як працюють директиви, - це вивчення ngInclude. ngInclude допомагає включати html-партії. Коли я вперше почав використовувати директиви, я виявив, що ви можете використовувати його шаблон для зменшення коду, але я не дуже прив'язував будь-яку логіку.

Звичайно, між директивами angular і роботою команди angular-ui мені ще не довелося створювати власну директиву, яка робить щось істотне, щоб мій погляд на це може бути абсолютно неправильним.


2

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

Врешті-решт, це занадто дивно, щоб передати кожне глобальне або спільне значення вниз по ланцюжку з декількома атрибутами при кожному виклику DOM директиви (як це потрібно з ізоляційною областю). Просто виглядає нерозумно багаторазово писати все, що в DOM, і це відчуває себе неефективно, навіть якщо це спільні об'єкти. Це також непотрібно ускладнює декларації директиви. Приблизний варіант використання $ parent для "досягнення вгору" та отримання значень з директиви HTML здається дуже поганою формою.

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

Десятиліттями мріяв про мрії про доменні мови протягом десятиліть, перш ніж було таке, я в захваті від того, що AngularJS надає цей варіант, і я знаю, що, як більше розробників працює в цій області, ми побачимо кілька дуже класних додатків, які їх архітектори також легко писати, розширювати та налагоджувати.

- D

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.