Когда вы разрабатываете крупномасштабное приложение на основе компонентов, которое часто обменивается данными и принимает данные с сервера, структура страниц, функций и компонентов, естественно, может зависеть от данных, которые вы получаете.
Если вы модулируете свои компоненты как небольшие независимые единицы, каждая из которых отвечает за область или часть вашей страницы, тогда у вас может быть 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`.
- Создание и угловой элемент.
- Скомпилируйте элемент и получите функцию ссылки.
- Создание новой родственной области.
- Назначьте привязки к новой области.
- Свяжите новую область видимости.
- Добавить элемент к родительскому.
- Уничтожьте старый прицел.
- Удалите старый элемент.
Теперь у нас есть новый компонент без явного объявления его, и у вас есть собственная небольшая зависимость от черного ящика, которую можно перенести на следующую задачу в AngularJS - конечно, если вы все еще используете его.