Что такое модуль angular
Основы AngularJS
Модули в AngularJS
Основной формой организации приложений в AngularJS являются модули. Модуль представляет хранилище различной информации: директив, фильтров, контроллеров и т.д. При этом одно приложение может иметь несколько модулей. Например, разные модули могут представлять какую-либо специфическую функциональность.
Модули позволяют ассоциировать определенный участок html-страницы с приложением AngularJS. Модули также позволяют организовать и структурировать различные компоненты приложения AngularJS. Кроме того, модульность архитектуры приложения повышает тестируемость, и мы можем использовать различные части-модули приложения в других приложениях.
Название модуля. Согласно соглашениям о наименовании модуль должен иметь суффикс App
Набор других модулей в виде строкового массива, от которых данный модуль зависит
Конфигурационные настройки модуля
Здесь создается модуль myApp. Вместо набора модулей, передаваемых вторым параметром, мы можем отставить пустым массивом. В то же время было бы ошибкой вообще убрать этот параметр. Так как выражение var myApp = angular.module(‘myApp’); будет представлять попытку получить модуль myApp в одноименную переменную, а не создать его.
Чтобы ассоциировать модуль с определенным куском html-страницы, нужно использовать директиву ng-app:
Используя модуль, мы можем определить ряд компонентов, таких как контроллеры, сервисы и т.д., которые затем применяются в приложении. Для этого объект Module имеет ряд методов, наиболее используемые из них:
config(callback) : регистрирует функцию callback, которая используется для его конфигурации в процессе загрузки
constant(key, value) : определяет сервис, который возвращает константное значение value
controller(name, constructor) : создает контроллер
directive(name, factory) : создает директиву, которая расширяет стандартную разметку html
factory(name, provider) : создает службу
filter(name, factory) : создает фильтр
provider(name, type) : создает сервис
service(name, constructor) : создает сервис
Angular 2 — Модули
Дата публикации: 2017-11-09
От автора: в Angular 2 модули используются для разделения приложении на логические разделы. Вместо того, чтобы писать код всего приложения в одном месте, вы можете создавать отдельные модули для разделения функционала приложения. Давайте рассмотрим код, который добавляется в демонстрационное приложение.
Через Visual Studio code перейдите в папку app.module.ts в папке приложения. Это называется классом корневого модуля.
В файле app.module.ts будет содержаться следующий код.
Бесплатный курс «Laravel + Angular. Быстрый старт»
Изучите курс и узнайте, как создать веб-приложение с нуля на Angular и Laravel
Давайте подробно рассмотрим каждую строку кода.
Оператор import используется для импорта функций из существующих модулей. Таким образом, первые 3 оператора используются для импорта в данный модуль модулей NgModule, BrowserModule и AppComponent.
Декодер NgModule используется для того, чтобы позже определить импорт, декларации и параметры начальной загрузки.
Для любого веб-приложения Angular по умолчанию требуется BrowserModule.
Параметр bootstrap указывает Angular, какой Компонент загружать в приложение.
Модуль состоит из следующих частей:
Массив Bootstrap. Используется, чтобы указать Angular JS, какие Компоненты необходимо загрузить, чтобы их функционал был доступен в приложении. После включения компонента в массив bootstrap вам необходимо объявить их, чтобы их можно было использовать для других компонентов приложения Angular JS.
Массив Export. Используется для экспорта компонентов, директив и каналов, которые затем могут использоваться в других модулях.
Массив Import. Подобно массиву Export, массив Import может использоваться для импорта функций из других модулей Angular JS.
Редакция: Команда webformyself.
Бесплатный курс «Laravel + Angular. Быстрый старт»
Изучите курс и узнайте, как создать веб-приложение с нуля на Angular и Laravel
Введение в модули Angular — корневой модуль (Root Module)
Прим. перев.: для понимания данной статьи необходимо обладать начальными знаниями Angular: что такое компоненты, как создать простейшее SPA приложение и т.д. Если Вы не знакомы с данной темой, то рекомендую для начала ознакомиться с примером создания SPA приложения из оф. документации.
Вступление
@NgModule — декоратор, добавленный в Angular 2. Из официальной документации следует, что @NgModule определяет класс, как модуль Angular. Модули Angular помогают разбивать приложение на части (модули), которые взаимодействуют между собой и представляют в конечном итоге целостное приложение. Иными словами, модуль — это упаковка или инкапсуляция части функционала приложения. Модули можно проектировать с учетом многократного использования, т.е. не зависящие от конкретной реализации приложения.
Обратите внимание, что если вы хотите реализовать lazy load в Вашем приложении, то необходимо использовать концепцию Angular Modules при проектировании приложения.
Корневой модуль (Root Module)
В качестве аргумента в декораторе @NgModule используется JavaScript объект.
Как происходит загрузка компонента в DOM?
Каким образом AppModule добавляет компонент в DOM?
AppModule выступает в роли связующего между данными, их представлением и браузером.
Компоновка приложения
В примере выше мы используем динамическую компиляцию (JIT — Just In Time). Однако в Angular можно реализовать AOT-компиляцию (Ahead of Time), однако это обширная тема и будет рассмотрена в другой статье.
Декларирование (объявление)
В наших приложения мы создаем свои компоненты, директивы и пайпы. Мы можем сообщить нашему приложению о том, какой функционал мы хотим добавить (компоненты, директивы и пайпы) путем перечисления их в свойстве declarations объекта, который является аргументом для декоратора @NgModule :
После объявления компонентов мы можем использовать их внутри других компонентов через их селектор, который указывается в описании компонента.
Импортирование и вложенные модули
Провайдеры
Часто в наших приложениях есть сервисы, которые мы хотим использовать в нескольких (или во всех) компонентах. Как предоставить всем компонентам доступ к сервису? Использовать Angular Modules.
Когда мы представили эти сервисы в корневом модуле AppModule они доступны в компонентах приложения:
Опишем, как это работает. Мы добавили в конструктор нашего компонента создание объекта провайдера, выглядит это примерно так:
Заключение
Провайдеры, на мой взгляд, одна из самых интересных и сложных концепций модульной системы Angular. Основное правило использования провайдеров (сервисов) — создать экземпляр сервиса в корневом модуле и передавать по запросу этот экземпляр в другие части (компоненты) приложения.
— Telegram русскоязычного Angular сообщества.
Что такое Angular. Начало работы с фреймворком¶
Angular представляет фреймворк от компании Google для создания клиентских приложений. Прежде всего он нацелен на разработку SPA-решений (Single Page Application), то есть одностраничных приложений. В этом плане Angular является наследником другого фреймворка AngularJS. В то же время Angular это не новая версия AngularJS, а принципиально новый фреймворк.
Angular предоставляет такую функциональность, как двустороннее связывание, позволяющее динамически изменять данные в одном месте интерфейса при изменении данных модели в другом, шаблоны, маршрутизация и так далее.
Одной из ключевых особенностей Angular является то, что он использует в качестве языка программирования TypeScript.
Но мы не ограничены языком TypeScript. При желании можем писать приложения на Angular с помощью таких языков как Dart или JavaScript. Однако TypeScript все таки является основным языком для Angular.
Официальный репозиторий фреймворка на гитхабе: https://github.com/angular/angular. Там вы можете найти сами исходные файлы, а также некоторую дополнительную информацию.
Начало работы c Angular¶
Для работы с Angular необходимо установить сервер Node.js и пакетный менеджер npm, если они отсутствуют на рабочей машине. Для установки можно использовать программу установки node.js. Вместе с сервером она также установит и npm. При этом особого какого-то знания для работы с NodeJS и npm не требуется.
Данный файл устанавливает пакеты и зависимости, которые будут использоваться проектом. В секции dependencies в основном определяются пакеты angular, которые необходимы приложению для работы. В секции devDependencies прописаны только те пакеты, которые будут использоваться для разработки. В частности, это пакеты для работы с языком typescript (так как мы будем писать код приложения на языке TypeScript), а также пакеты, необходимые для сборки приложения в один файл с помощью сборщика webpack.
Затем откроем командную строку (терминал) и перейдем в ней к папке проекта с помощью команды cd :
Создание компонента Angular¶
Компоненты представляют основные строительные блоки приложения Angular. Каждое приложение Angular имеет как минимум один компонент. Поэтому создадим в папке src/app новый файл, который назовем app.component.ts и в котором определим следующий код компонента:
Создание модуля приложения¶
Приложение Angular состоит из модулей. Модульная структура позволяет легко подгружать и задействовать только те модули, которые непосредственно необходимы. И каждое приложение имеет как минимум один корневой модуль. Поэтому создадим в папке src/app новый файл, который назовем app.module.ts со следующим содержимым:
Запуск приложения¶
Теперь нам надо указать Angular, как запускать наше приложение. Для этого создадим в папке src (на уровень выше, чем расположены файлы app.component.ts и app.module.ts ) файл main.ts со следующим содержимым:
Также в папке src определим еще один файл, который назовем polyfills.ts со следующим кодом:
Данный файл определяет полифилы — инструменты, которые необходимы для поддержки приложения на Angular старыми браузерами.
Создание главной страницы¶
Далее определим в папке src главную страницу index.html приложения:
Определение конфигурации¶
Поскольку для определения кода приложения применяется язык TypeScript, поэтому также создадим в корневой папке проекта новый файл tsconfig.json :
Angular.json¶
Проект определяет следующие опции:
Параметр options задает параметры построения файлов. Для команды » build » здесь определены следующие опции:
Последняя опция defaultProject указывает на проект по умолчанию. В данном случае это наш единственный проект.
В итоге у нас получится следующая структура проекта:
Запуск проекта¶
И теперь, когда все готово, мы можем запустить проект. Для этого в командной строке (терминале) перейдем к папке проекта с помощью команды cd и затем выполним команду ng serve:
Введем в текстовое поле какое-нибудь имя, и оно тут же отобразится в заголовке.
Важно отметить, что пока приложение запущено, мы можем поменять код, и Angular CLI почти моментально перекомпилирует и перезапустит приложение.
Подходы к управлению модулями в Angular (и не только)
Понимание организации сущностей, с которыми работаешь — не то, что сразу получается у разработчика, пишущего свои первые проекты на Angular.
И одна из проблем, к которой можно прийти — неэффективное использование Angular модулей, в частности — излишне перегруженный app модуль: создали новую компоненту, забросили в него, сервис — тоже туда. И вроде всё здорово, всё работает. Однако со временем такой проект станет тяжело поддерживать и оптимизировать.
Благо, Angular предоставляет разработчикам возможность создавать свои модули, и ещё называет их feature модулями.
Domain Feature Модули
Перегруженный app модуль нужно дробить. Поэтому первое, что стоит сделать — выделить в приложении крупные куски и вынести их в отдельные модули.
Популярный подход — разделение приложения на domain feature модули. Они призваны разделить интерфейс по признаку ключевой задачи (domain), которую выполняет каждая его часть. Примерами domain feature модулей могут быть страница редактирования профиля, страница с товарами и т.д. Проще говоря, всё, что могло бы оказаться под пунктом меню.
Все объявления в синих рамках, а также контент других пунктов меню, заслуживают своих собственных domain feature модулей.
Domain feature модули могут использовать неограниченное количество declarables (компоненты, директивы, пайпы), однако экспортируют только ту компоненту, что представляет UI данного модуля. Импортируются Domain feature модули, как правило, в один, больший модуль.
Domain Feature модули обычно не объявляют внутри себя сервисы. Однако если и объявляют, то жизнь этих сервисов должна ограничиваться жизнью модуля. Достичь этого можно при помощи lazy loading’а или объявления сервисов во внешней компоненте модуля. Эти методы будут разобраны дальше в статье.
Ленивая Загрузка
Разделение приложения на Domain Feature модули позволит использовать lazy loading. Так, вы можете убрать из первоначального бандла то, что не нужно юзеру при первом открытии приложения: профиль пользователя, страничка товаров, страничка с фотографиями и т.д. Всё это можно подгрузить по требованию.
Сервисы и Инжекторы
Приложение разделено на крупные куски — модули, и некоторые из этих модулей загружаются по требованию. Вопрос: где следует объявлять глобальные сервисы? И что делать, если мы хотели бы ограничить область видимости сервиса?
Инжекторы лениво загруженных модулей
В отличие от declarables, о существовании которых необходимо объявлять в каждом модуле, где они используются, объявленные один раз в каком-либо из модулей синглтоны сервисов становятся доступными во всём приложении.
Получается, что сервисы можно объявлять в любом модуле и не беспокоиться? Не совсем так.
Вышесказанное — правда, если в приложении используется только глобальный инжектор, однако зачастую всё несколько интереснее. Лениво загруженные модули имеют свой собственный инжектор (компоненты тоже, но об этом дальше). Почему вообще лениво загруженные модули создают свой собственный инжектор? Причина кроется в том, как работает dependency injection в Angular.
Инжектор может пополняться новыми провайдерами до тех пор, пока он не начинает использоваться. Как только инжектор создаёт первый сервис, он закрывается для добавления новых провайдеров.
Когда приложение запускается, Angular в первую очередь настраивает корневой инжектор, фиксируя в нём те провайдеры, которые были объявлены в App модуле и в импортированных в него модулях. Это просходит ещё до создание первых компонент и до предоставления им зависимостей.
В ситуации, когда лениво подгружается модуль, глобальный инжектор уже много времени как был настроен и вступил в работу. Подгруженному модулю не остаётся ничего, кроме как создать собственный инжектор. Этот инжектор будет дочерним по отношению к инжектору, использовавшимся в модуле, который инициализировал загрузку. Это приводит к поведению, знакомому javascript разработчикам по цепочке прототипов: если сервис не был найден в инжекторе лениво загруженного модуля, DI фрэймворк пойдёт искать его в родительском инжекторе и т.д.
Таким образом, лениво загруженные модули позволяют объявлять сервисы, которые будут доступны только в рамках данного модуля. Провайдеры также можно переопределять, опять же, прямо как в javascript прототипах.
Возвращаясь к domain feature модулям, описанное поведение — один из способов ограничить жизнь объявленных в них провайдеров.
Core Модуль
Так всё-таки, где следует объявлять глобальные сервисы, такие как сервисы авторизации, API сервисы, Юзер сервисы и т.д.? Простой ответ — в App модуле. Однако в целях наведения порядка в App модуле (этим то мы и занимаемся), следует объявлять глобальные сервисы в отдельном модуле, получившем название Core модуль, и импортировать его ТОЛЬКО в App модуль. Результат будет тот же, как если бы сервисы были объявлены напрямую в App модуле.
Начиная с версии 6, в ангуляре появилась возможность объявлять глобальные сервисы, никуда их не импортируя. Всё, что нужно сделать — добавить в Injectable опцию providedIn, и указать в ней значение ‘root’. Сервисы, объявленные таким образом, становятся доступными всему приложению, а потому отпадает необходимость объявлять их в модуле.
Помимо того, что данный подход смотрит в светлейшее будущее ангуляра без модулей, он также помогает оттришэйкать ненужный код.
Проверка на Синглтон
Но что, если кто-то в проекте захочет импортировать Core модуль ещё куда-нибудь? Можно ли от этого защититься? Можно.
Добавьте в Core модуль конструктор, который просит заинжектить в него Core модуль (всё верно, самого себя), и пометьте это объявление декораторами Optional и SkipSelf. Если инжектор положит в переменную зависимость, значит кто то пытается повторно объявить Core модуль.
Использование описанного подхода в BrowserModule.
Этот подход может использоваться как с модулями, так и с сервисами.
Объявление Сервиса в Компоненте
Мы уже рассмотрели способ ограничения области видимости провайдеров, используя lazy loading, но вот ещё один.
Каждый инстанс компоненты имеет свой собственный инжектор, и для его настройки, прямо как декоратор NgModule, декоратор Component имеет свойство providers. А ещё — дополнительное свойство viewProviders. Они оба служат для настройки инжектора компоненты, однако провайдеры, объявленные каждым из способов, имеют разную область видимости.
Для понимания разницы, нужна коротенькая предыстория.
Компонента состоит из view и контента.
Всё, что находится в html файле компоненты — это её view, тогда как то, что передаётся между открывающим и закрывающим тэгами компоненты, является её контентом.
Так вот, провайдеры, добавленные в providers, доступны как во view компоненты, в которой они объявлены, так и для контента, который передан компоненте. Тогда как viewProviders, как и заложено в названии, делает сервисы видимыми только для вью и закрывает их для контента.
Несмотря на то, что лучшая практика — объявлять сервисы в root инжекторе, существуют сценарии, когда использование инжектора компоненты приходится на руку:
Первый — это когда каждый новый экземпляр компоненты должен иметь свой собственный экземпляр сервиса. Например сервис, который хранит специфичные для каждого нового экземпляра компоненты данные.
Для другого сценария нам нужно вспомнить, что, хоть Domain feature модули и могут объявлять какие-то нужные только им провайдеры, желательно чтобы эти провайдеры умирали вместе с этими модулями. В таком случае, мы объявим провайдер в самой внешней компоненте, той самой, которая экспортируется из модуля.
Например, domain feature module, отвечающий за профиль пользователя. Нужный только этой части приложения сервис мы объявим в providers самой внешней компоненты, UserProfileComponent. Теперь все declarables, которые объявлены в разметке этой компоненты, а также переданы ей в контенте, получат один и тот же экземпляр сервиса.
Переиспользуемые Компоненты
Что делать с компонентами, которые мы хотим переиспользовать? На этот вопрос также нет однозначного ответа, но есть наработанные подходы.
Shared Модуль
Все переиспользуемые в проекте компоненты можно хранить в одном модуле, экспортируя их из него и импортируя его в те модули проекта, где эти компоненты могут понадобиться.
В такой модуль можно поместить компоненты кнопки, выпадающего списка, какого нибудь стилизованного блока текста и т.д, а также кастомные директивы и пайпы.
Такой модуль обычно имеет название SharedModule.
При этом важно заметить, что SharedModule не должен объявлять сервисов. Или объявлять, используя forRoot подход. О нём поговорим чуть позже.
Несмотря на то, что подход c SharedModules работает, к нему есть пара замечаний:
Подход, который лишён этих недостатков, есть и предполагает создание модуля для каждой компоненты.
Module Per Component или SCAM (single component angular module)
Создавая какую-либо новую компоненту, следует помещать её в свой собственный модуль. В этот же же модуль необходимо импортировать зависимости компоненты.
Каждый раз, когда определенная компонента нужна в каком-либо месте приложения, всё, что нужно сделать — это импортировать модуль данной компоненты.
На английском такой подход называется module per component или SCAM — single component angular module. Хотя в названии есть слово component, этот подход распространяется также на пайпы и директивы (SPAM, SDAM).
Наверное самое значительное преимуществ данного подхода — облегчение тестирования компонент. Так как модуль, создаваемый для компоненты, экспортирует её, а также уже содержит все нужные ей зависимости, для настройки TestBed достаточно положить этот модуль в imports.
Такой подход способствует порядку и структуре в коде проекта, а также готовит нас к будущему без модулей, где для использования одной компоненты в разметке другой нужно будет лишь объявить зависимости в директиве Component. Немного заглянуть в будущее можно через эту статью.
Интерфейс ModuleWithProviders
Если в проекте завёлся модуль, содержащий в себе объявление сервисов XYZ, и так получилось, что со временем этот модуль начал использоваться повсеместно, каждый импорт этого модуля будет пытаться добавить сервисы XYZ в соответствующий инжектор, что неизбежно приведёт к коллизиям. У Angular есть на этот случай набор правил, который может не соответствовать тому, что ожидает разработчик. Особенно это касается инжектора лениво загруженного модуля.
Для избежания проблем с коллизией, Angular предоставляет интерфейс ModuleWithProviders, который позволяет прикрепить провайдеры к модулю, оставив при этом providers самого модуля нетронутым. И это именно то, что нужно в описанном выше случае.
Стратегии forRoot(), forChild()
Для того, чтобы сервисы точно были зафиксированы в глобальном инжекторе, модуль с провайдерами импортируется только в AppModule. Со стороны импортируемого модуля нужно лишь создать статический метод, возвращающий ModuleWithProviders, который исторически получил название forRoot.
Методов, возвращающих ModuleWithProviders, может быть сколько угодно, и названы они могут быть как угодно. forRoot — это скорее удобная условность, чем требование.
Например, RouterModule имеет статический метод forChild, который используется для настройки роутинга в лениво загруженных модулях.