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

Если вы модулируете свои компоненты как небольшие независимые единицы, каждая из которых отвечает за область или часть вашей страницы, тогда у вас может быть package.json, полный зависимостей. В большинстве случаев, за исключением объявляемых вами «статических» компонентов, ваше приложение будет знать, какие из них отображать «во время выполнения», на основе реализованной вами логики и данных, которые оно получает от сервера.

Один из способов добиться этого - просто перечислить все компоненты с помощью условной директивы ng-if. У вас может получиться бесконечный скучный список всех зависимостей, которые вы указали в package.json вашего приложения. Это определенно не очень хорошая практика, за исключением случаев, когда вам следует использовать ng-if. Ваш код становится статичным и длинным, и большая его часть в конечном итоге даже не используется. Вы можете перечислить двадцать условных компонентов, чтобы использовать только один из них. Есть способ лучше, классный и изящный, сделать это с помощью замечательного инструмента AngularJS $compile

Используя $compile, мы можем динамически создавать новый контент в приложении AngularJS. Я представлю один способ добиться этого, упаковав решение в виде небольшого независимого расходного модуля.

Сначала давайте создадим простой компонент `select-items`, который позже мы будем использовать в моем примере.

select-items.html:

Здесь у нас есть простой выпадающий select элемент, который принимает список объектов со свойством `name` для представления в каждой опции, которую ng-options будет генерировать внутри <select>. Когда бы ни был выбран элемент, ng-change вызовет метод onSelect в контроллере `select-items` ниже и передаст ему выбранный объект.

select-items.js:

Директива `select-items` ограничена как element. После вызова `onSelect` будет запущен обратный вызов onItemSelected для обновления потребителя <select-items> новым выбранным объектом.

app.js:

В app.js мы устанавливаем массив, в котором перечислены все компоненты, которые мы хотим динамически компилировать в объектной форме. Для примера мы используем только один. У него есть два свойства: `name` - имя вновь скомпилированной директивы и `bindings` - мы можем назначить новой компилируемой области видимости все, что захотим: обратные вызовы, объекты и примитивы данных. Мы также можем добавить свойство orderBy для управления порядком, в котором наши будущие директивы будут компилироваться и добавляться в представление.

index.html:

Теперь, что самое интересное, мы объявили <compile-me> настраиваемую директиву, которая создается повторно с ng-repeat для каждого объекта, сформированного настраиваемой директивой, которую мы передали ему в пользовательском контроллере app.js

compile-me.js

Давайте объясним, что происходит в <compile-me> контроллере. В 1-й строке я передаю шаблон `angular.element ()`. Это имя директивы <select-items>, которое мы установили в `customDirectives` массив, который я передал в директиву <compile-me>. Возвращаемое значение $el теперь является угловым элементом.

Во второй строке угловой элемент $el передается в $compile. Компиляция - это функция внутри директивы. Управление DOM, добавление или удаление директив и других элементов DOM выполняется в функции компиляции до начала процесса `postLink`. Компиляция - это одна из четырех функций, которые выполняются в жизненном цикле директивы:

$compile проходит через дерево DOM, начиная с нашего элемента angular, и продолжается рекурсивно сверху вниз и сопоставляет элементы DOM с директивами. Все обнаруживаемые директивы также компилируются, а их функции связывания (preLink, postLink) собираются, обертываются одной и той же функцией ссылки и возвращаются.

Для каждой обнаруженной $compile директивы выполняются две функции, controller и preLink, и они выполняются рекурсивно сверху вниз. Последняя функция postLink выполняется для каждой директивы снизу вверх.

Функция controller создается перед фазой предварительного связывания и может быть доступна другим директивам. Это позволяет общаться между директивами. Родственные директивы могут запрашивать контроллеры своих братьев и сестер, а дочерние директивы могут запрашивать контроллеры своих родителей.

preLink выполняется до того, как дочерние элементы будут связаны с представлением, здесь мы можем манипулировать частной `$ scope` до того, как она будет связана с представлением и начнется postLink процесс.

Теперь, когда мы находимся в нижней части дерева DOM, запускается процесс postLink, всплывает вверх по дереву и выполняется для каждой директивы. На самом деле это сокращение от функции link(), которая используется чаще всего. Он отвечает за регистрацию слушателей DOM, а также за обновление DOM.

Порядок выполнения директивных функций:

compile: top → bottom директивы

controller & preLink: верхние → нижние директивы

postLink: bottom → top директивы

В 3-й строке мы создаем новую область видимости `$ newScope`, и она представляет новую область видимости для нового компонента <select-items>, который` compileMe `компилирует для нас в текущей итерации. из `ng-repeat`. Мы достигаем этого, вызывая $scope.$parent.$new(), тем самым мы добавляем новую область видимости к родительской области видимости `compileMe`, делая их родственниками родителя в дереве областей видимости. Позже мы уничтожим область видимости `compileMe` и удалим ее заполнитель из DOM, чтобы завершить процесс переключения между двумя директивами.

В 4-й строке мы просто назначаем все свойства и их значения из части bindings объекта customDirectives для $newScope объект.

Затем мы связываем $newScope с представлением, передавая ему функцию `link`, которую мы установили во 2-й строке.

В следующих строках мы добавляем новый элемент, который мы создали, к дереву DOM под родительским элементом <compile-me>, делая их родственными. Теперь, когда мы закончили использовать <compile-me>, мы хотим поблагодарить и избавиться от него, как от областей видимости, так и от деревьев DOM.

Вызов $scope.$destroy предотвратит запуск любых обработчиков событий Angular, а также трансляцию события `$ destroy` в области видимости и всех ее дочерних областях. $element.remove() удаляет заполнитель из DOM.

Этот процесс будет повторяться для каждого элемента, который мы добавили в объект `customDirectives`.

  1. Создание и угловой элемент.
  2. Скомпилируйте элемент и получите функцию ссылки.
  3. Создание новой родственной области.
  4. Назначьте привязки к новой области.
  5. Свяжите новую область видимости.
  6. Добавить элемент к родительскому.
  7. Уничтожьте старый прицел.
  8. Удалите старый элемент.

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