Angular: понятное введение в ngrx

Логика внутри конструктора сервиса

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

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

Этот базовый сервис создает соединение с сокетом и обрабатывает взаимодействия с ним. Однако проблема здесь в том, что при каждом создании нового экземпляра этого сервиса, открывается новое соединение.

Приложение будет использовать соединение с одним сокетом множество раз, поэтому, применяя этот сервис внутри отложено загруженных модулей, мы получим новое открытое соединение. Чтобы избежать этого, нужно удалить логику инициализации из конструктора и найти другой способ разделить соединение между отложено загруженными модулями. Также нам может потребоваться метод для перезагрузки соединения (например, чтобы открыть его повторно, если оно неожиданно закроется):

Дочерние маршруты

Последнее обновление: 17.05.2021

В прошлых темах рассматривалась работа с обычными маршрутами. То есть система маршрутизации выбирает один из компонентов на основе url и
помещает его в главный компонент AppComponent. Но что если выбранный компонент сам должен принимать в качестве внутреннего содержимого какой-то другой компонент в зависимости от
запроса. В этому случае нам надо использовать дочерние маршруты (child route).

К примеру, возьмем проект из прошлых тем:

Добавим в папку src/app два простеньких компонента. Вначале добавим файл item.details.component.ts:

import { Component} from '@angular/core';
 
@Component({
    selector: 'item-details',
    template: `<h3>Информация о товаре</h3>`
})
export class ItemDetailsComponent{}

И также добавим второй файл item.stat.component.ts:

import { Component} from '@angular/core';
 
@Component({
    selector: 'item-stat',
    template: `<h3>Статистика товара</h3>`
})
export class ItemStatComponent{}

Итак, есть два компонента ItemDetailsComponent и ItemStatComponent, которые условно раскрывают различные стороны одно и того же товара. Логически они относятся к
ItemComponent, который также связан с определенным товаром.

Мы бы могли определить прямые маршруты к этим компонентам типа:

{ path: 'item/:id/details', component: ItemDetailsComponent},
{ path: 'item/:id/stat', component: ItemStatComponent},

Где в данном случае id — также параметр маршрута, представляющий условный id товара.

Но такие маршруты будут миновать компонент ItemComponent и никак его не затрагивают. Поэтому нам надо использовать другую организацию маршрутов. Для
этого изменим модуль AppModule следующим образом:

import { NgModule }      from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import {Routes, RouterModule} from '@angular/router';
import { AppComponent }   from './app.component';
import { HomeComponent }   from './home.component';

import { ItemComponent }   from './item.component';
import { ItemStatComponent }   from './item.stat.component';
import { ItemDetailsComponent }   from './item.details.component';

// определение дочерних маршрутов
const itemRoutes: Routes = ;

const appRoutes: Routes =[

	{ path: 'item/:id', component: ItemComponent},
	{ path: 'item/:id', component: ItemComponent, children: itemRoutes},
	{ path: '', component: HomeComponent}
];

@NgModule({
    imports:      ,
    declarations: ,
    bootstrap:    
})
export class AppModule { }

Каждый из дочерних маршрутов сопоставляется не совсем адресом url, а только с его частью. Далее чтобы применить такие маршруты, у маршрута для компонента
ItemComponent применяется свойство children:

{ path: 'item/:id', component: ItemComponent, children: itemRoutes},

Теперь изменим ItemComponent:

import { Component} from '@angular/core';
import { ActivatedRoute} from '@angular/router';
import {Subscription} from 'rxjs';
 
@Component({
    selector: 'item-info',
    template: `<h2>Товар `id`</h2>
			   <router-outlet></router-outlet>`
})
export class ItemComponent{ 
	
	id: any;
	private routeSubscription: Subscription;
	
	constructor(private route: ActivatedRoute){
		
		this.routeSubscription = route.params.subscribe(params=>this.id=params);
	}
}

Для вставки компонентов ItemDetailsComponent и ItemStatComponent здесь определен элемент .

И изменим главный компонент AppComponent, добавив в него для тестирования ссылки на ItemDetailsComponent и ItemStatComponent:

import { Component} from '@angular/core';
 
@Component({
    selector: 'my-app',
    template: `<div>
					<nav>
						<a routerLink="">Главная</a> |
						<a routerLink="/item/5/details">Информация о товаре</a> |
						<a routerLink="/item/5/stat">Статистика товара</a>
					</nav>
					<router-outlet></router-outlet>
			   </div>`
})
export class AppComponent {}

При переходе по подобным ссылкам будет срабатывать маршрутизация к компоненту ItemComponent и ему будет передаваться параметр маршрута — id. И также
будет срабатывать маршрутизация к ItemDetailsComponent или ItemStatComponent, если после id идет какой-нибудь сегмент «details» или «stat»:

НазадВперед

Подводные камни Angular Universal

Не трогайте DOM

Когда мы начали тестировать компоненты нашего магазина с помощью , нам пришлось потратить некоторое время, чтобы понять, почему наш сервер падает при запуске без вывода серверной страницы. Например, у нас есть компонент Session Flow component, который отслеживает активность пользователя во время сессии (перемещения пользователя, клики, рефферер, информация об устройстве пользователя и т.д.). После поиска информации в issues на мы поняли, что в нет обертки над DOM.

.

Если вы склонируете этот Angular Universal стартер и откроете browser.module.ts вы увидите, что в массиве разработчики Universal предоставляют два:

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

автоматически добавляет , если это сервер, и , если браузер. Может быть, позже разработчики пересмотрят эту реализацию, и нам не придется беспокоиться об этом.

Если вы хотите активно взаимодействовать с элементами DOM, используйте сервисы API, такие как, или .

Правильный роутинг

Поскольку сервер отражает наше приложение, у нас была проблема с роутингом.

Поэтому не забудьте добавить тот же роутинг на сервере что и на клиенте. Например, у нас есть такие роуты на клиенте:

Тогда нужно создать файл с массивом роутов сервера. Корневой маршрут можно не добавлять:

Наконец, добавьте роуты на сервер:

Пререндеринг стартовой страницы

Одной из наиболее важных особенностей является пререндеринг. Из исследования Kissmetrics следует, что 47% потребителей ожидают, что веб-страница загрузится за 2 секунды или даже менее

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

Давайте подробнее рассмотрим, что это такое и как его использовать.

Когда пользователь открывает URL нашего магазина, немедленно возвращает предварительно подготовленную HTML страничку с контентом, а уже затем затем начинает загружать все приложение в фоновом режиме. Как только приложение полностью загрузится, подменяет изначальную страницу нашим приложением. Вы спросите, что будет, если пользователь начнет взаимодействовать со страницей до загрузки приложения? Не беспокойтесь, библиотека Preboot.js запишет все события, которые выполнит пользователь и после загрузки приложения выполнит их уже в приложении.

Чтобы включить пререндеринг, просто добавьте в конфигурацию сервера :

Добавление мета-тегов

В случае SEO-ориентированного сайта добавление мета-тегов очень важно. Например, у нас есть , который представляет страницу определенного продукта

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

Команда создала сервис angular2-meta, чтобы легко манипулировать мета-тегами. Вставьте мета-сервис в ваш компонент и несколько строк кода добавлят мета-теги в вашу страницу:

В следующей версии Angular этот сервис будет перемещен в@angular/platform-server

Кэширование данных

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

Но зачем нам нужно запрашивать данные на сервере дважды? PatricJs создал пример, как сделать Http-запрос на сервере один раз и закэшировать полученные данные для клиента. Посмотреть исходный код примера можно . Чтобы использовать его заинжекте и вызовите метод для выполнения http-вызовов с кешированием:

Как устроен Angular: компоненты

Angular-приложения состо­ят из неза­ви­си­мых эле­мен­тов. Эти эле­мен­ты назы­ва­ют­ся ком­по­нен­та­ми, и у каж­до­го ком­по­нен­та своё поведение. 

Напри­мер, лен­та ново­стей — один ком­по­нент. Отве­ча­ет за отоб­ра­же­ние спис­ка ново­стей на стра­ни­це. Кноп­ка «Про­чи­тать» — дру­гой ком­по­нент. Отве­ча­ет за пере­ход со стра­ни­цы спис­ка ново­стей к выбран­ной новости. 

Обыч­но ком­по­нент про­грам­ми­ру­ют так, что­бы он отоб­ра­жал эле­мент на экране и выпол­нял какое-то дей­ствие. Ком­по­нент может реа­ги­ро­вать на клик, сво­ра­чи­вать­ся, раз­во­ра­чи­вать­ся, скры­вать­ся, пере­бра­сы­вать на дру­гую стра­ни­цу и так далее. 

Ком­по­нен­ты под­чи­ня­ют­ся жиз­нен­ным цик­лам — меня­ют­ся и рабо­та­ют по несколь­ким запро­грам­ми­ро­ван­ным сце­на­ри­ям. Возь­мём ситу­а­цию, когда мы пере­хо­дим со стра­ни­цы спис­ка ново­стей к одной ново­сти. В этом слу­чае ком­по­нент «Лен­та ново­стей» уни­что­жа­ет­ся и при необ­хо­ди­мо­сти созда­ёт­ся повтор­но. Жиз­нен­ные цик­лы раз­гру­жа­ют память и уско­ря­ют приложение.

Стра­ни­ца с шап­кой, лен­той ново­стей и тре­мя кноп­ка­ми. Каж­дый эле­мент — неза­ви­си­мый ком­по­нент, кото­рый выпол­ня­ет какое-то дей­ствие внут­ри приложения 

Сам себе DevOps: строим cloud-only CI для веб приложения

Из песочницы

Привет, Хабр! Сегодня мы поговорим немного о DevOps и самоорганизации.

Мы начнем с фразы, с которой не соглашается добрая половина разработчиков в индустрии: «каждый разработчик должен быть сам себе DevOps». Кто-то считает, что этим должен заниматься отдельно выделенный человек, чтобы у разработчика оставалась забота только о качестве кода. Мы считаем, что в современных реалиях рынка и избытке инструментов/знаний разработчик должен уметь настроить и обслуживать конвейер быстрой и предсказуемой доставки артефакта в нужную ему среду.

И речь не идет о настройке каких-то больших и громоздких билд-систем, для которых обычно приносится в жертвую целая штатная единица. Нет. DevOps — не человек, а система ежедневных маленьких привычек, основанных на самоорганизации. Понятие, взрастающее снизу вверх, а не сверху или в бок.

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

Структура папок хранилища

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

Структура папок хранилища

Структура папок представляет фактическую структуру объекта хранилища. У вас будет главная папка с названием «store» и пять вложенных папок, которые представляют каждого из ключевых игроков хранилища: «Actions», «Effects», «Reducers», «Selectors» и «State».

Пример NGRX

  1. Установка библиотеки
  2. Создание структуры папок для хранилища
  3. Создание хранилища и начальных значений
  4. Создание действий (Actions)
  5. Создание редукторов (Reducers)
  6. Создание эффектов (Effects)
  7. Создание селекторов (Selectors)
  8. Конечная настройка
  9. Использование хранилища в компонентах

Создание хранилища и начальных значений

  1. Мы создаем и экспортируем интерфейс со структурой пользовательской среды.
  2. Мы делаем то же самое с начальным пользовательским состоянием, которое реализует недавно созданный интерфейс.
  1. Состояние приложения содержит состояние пользователя и конфигурации, а также состояние маршрутизатора.
  2. Потом задаем начальное состояние приложения.
  3. Наконец, экспортирует функцию, чтобы получить начальное состояние (мы будем использовать его позже).

Создание Действий

  1. Мы экспортируем Enum, содержащий определение для типов действий. Таким образом, мы избегаем использования и повторения строк для использования типов действий, процесс, который может легко порождаться ошибками.
  2. Потом мы создаем и экспортируем класс для каждого из ваших действий. Все они должны реализовать интерфейс Action из ngrx. Наконец, мы устанавливаем тип в одно из значений перечислений, и если вам нужно полезное содержимое для действия, вы добавляете его в конструктор класса.
  3. Наконец, мы экспортируем тип, содержащий наши классы действий. Это обеспечит нам проверку типов, которую мы можем использовать, например, в наших редукторах.

Создание Редукторов

  1. Объявление редуктора получает состояние и, в этом случае, действия пользователя и возвращает IUserState.
  2. Используя оператор switch, мы генерируем наблюдения для каждого возможного типа действия.
  3. Каждый случай возвращает новый объект, который является результатом слияния старого состояния и нового значения.
  4. Все редукторы имеют результат по умолчанию, который просто возвращает состояние без каких-либо изменений.

Добавим Эффекты

  1. Мы объявляем наши пользовательские эффекты с помощью инъекционного декоратора.
  2. Мы объявляем наши эффекты, используя декоратор эффектов, предоставленный ngrx/Effects.
  3. Используя действия, предоставленные ngrx / Effects, мы собираемся запустить конвейер нашего оператора для этого эффекта.
  4. Следующим шагом является установка типа действия эффекта с помощью оператора ofType.
  5. Следующие части представляют собой операторы rxjs, которые мы используем для получения того, что нам нужно (у нас уже есть ссылка на документацию по rxjs в этой статье).
  6. Наконец, в последнем операторе Effect отправит еще одно действие.
  7. В конструкторе мы внедряем сервисы, которые мы собираемся использовать, действия для ngrx / Effects, и в этом случае также хранилище (учтите, что это демо, и мы получаем выбранного пользователя из списка пользователей в наше хранилище).

Итоговая настройка

  1. Мы импортируем наши редукторы и предоставляем их в forRoot модуля хранилища.
  2. Мы импортируем наши эффекты и предоставляем их внутри массива в модуль forRoot эффектов.
  3. Мы устанавливаем конфигурацию для модуля состояния маршрутизатора, предоставляющего маршрутизатор stateKey.
  4. И мы добавляем инструменты разработчика магазина, если среда не является производственной.

Использование хранилища в некоторых компонентах

Во-первых, давайте получим конфигурацию при запуске приложения:

  1. Мы добавляем хранилище в наш app.component.
  2. Мы устанавливаем для свойства компонента значение селектора в конфигурации, потому что хотим отобразить часть этой информации в HTML.
  3. В onInit мы отправляем действие, чтобы получить конфигурацию.
  1. Подобно тому, как мы управляем конфигурацией, мы собираемся получить список пользователей. Сначала мы внедряем хранилище в компонент пользователя.
  2. На onInit мы отправляем действие, чтобы получить пользователей.
  3. Мы создаем свойство для компонента и присваиваем ему список пользователей, используя селектор списка пользователей.

Давайте посмотрим на компонент пользовательского контейнера:

… и недостатки

  • У , конечно, есть кривая обучения. Не большая, но и не такая маленькая, и я думаю, что это требует некоторого опыта или глубокого понимания некоторых программных шаблонов. Это не является проблемой для любого разработчик среднего уровня, но младший может поначалу немного запутаться.
  • Для меня это ощущается немного многословно (прим пер.: речь о проблеме множества заготовок кода — ). Поэтому каждый раз, когда вы добавляете какое-либо свойство в состояние (), вам нужно добавлять действия () и диспетчеры (). Вам может потребоваться обновить или добавить селекторы (), эффекты (), если таковые имеются, и обновить хранилище (). Кроме того, вы будете собирать конвейер () операторов и наблюдаемых потоков () везде где это потребуется.
  • не является частью библиотек , и не поддерживается Google. По крайней мере, не напрямую, потому что среди контрибьюторов есть разработчики из команды . Это ещё один пункт для обдумывания — вы добавляете в зависимости тяжёлую библиотеку.

От переводчиков

Всем привет, с вами Максим Иванов и Дмитрий Сергиенков, и сегодня мы выясним куда делась третья версия, и почему мы по-прежнему можем быть верны Angular. Сейчас начинающему фронтенд-разработчику, изучающему JavaScript, довольно трудно определиться: какой же фреймворк или библиотеку им взять и изучить, оставаясь в тренде? Конечно, без слова React, сейчас не обходится ни один разговор на тему фронтенда, однако, в свое время я начал с первой версии Angular, начитался много руководств по написанию фронтенда на ES6 (с классами и прочим добром) и до сих пор не жалею. Да, вы можете писать корпоративные приложения с использованием только jQuery, но по мере развития и сложности проекта вы начнете закапывать себя в спагетти-коде, смешивая логику и представление, MV*-фреймворки же этого сделать вам не дадут.

Google ненавидит SPA

Когда мы говорим про современные интернет магазины, мы представляем себе тяжелые для понимания серверы, рендрящие тысячи статических страничек. Причем именно эти тысячи отрендеренных страниц одна из причин, почему Single Page Applications не прижились в электронной коммерции. Даже крупнейшие магазины электронной коммерции по-прежнему выглядят как куча статических страниц. Для пользователя это нескончаемый цикл кликов, ожиданий и перезагрузки страниц.

Одностраничные приложения приятно отличаются динамичностью взаимодействия с пользователем и более сложным UX. Но как не прискорбно обычно пользовательский комфорт приносится в жертву SEO оптимизации. Для сеошника сайт на angular – это своего рода проблема, поскольку поисковикам трудно индексировать страницы с динамическим контентом.

Мы любим JS и Angular. Мы верим, что классный и удобный UX может быть построен на этом стеке технологий, и мы можем решить все сопутствующие проблемы. В какой-то момент мы столкнулись с . Это модуль для рендеринга на стороне сервера. Сначала нам показалось, вот оно – решение! Но радость была преждевременной — и отсутствие больших проектов с его применением тому доказательство.

Как выглядит Angular-приложение?

Теперь, когда вы имеете некоторое представление об Angular, давайте углубимся в код. Начнём с небольшого приложения “hello world”. Все приложения Angular начинаются с НТМL-страницы, которая выглядит следующим образом.

В реальном приложении тег < script > внутри тега < head > может быть немного сложным, но в высокоуровневых приложениях он такой же, как и другие веб-приложениях – вы загружаете кусок JavaScript-кода в HTML-документ, и приложение работает.  

Есть одна уникальная вещь в выше приведённом примере – элемент < my-app >. Вы не используете этот элемент регулярно в веб-приложении. Цель Angular – постоянно расширять словарь НТМL, позволяя вам определять собственные теги.

Такие пользовательские теги называют компонентами, и вы можете определять их поведение в коде. Вот простейшая реализация элемента < my-app >:

Есть несколько моментов, которые вам нужно знать, чтобы понять, что происходит в данном фрагменте кода. Первое – это код на TypeScript, а не на JavaScript. TypeScript может показаться вам пугающим, если вы не встречались с ним раньше, но его основы понять нетрудно.

TypeScript – надстройка над JavaScript, то есть весь синтаксис JavaScript доступен на TypeScript. Кстати, весь приведённый выше синтаксис – import, export, @Component и остальные – это или нынешние фичи JavaScript, или те, что появятся в ближайшем будущем. Так что, когда вы учите TypeScript, вы изучаете будущее JavaScript. TypeScript, к тому же, отличается превосходной документацией, к которой вы можете обратиться в любое время.

TypeScript был создан и поддерживается Microsoft. Он стал очень популярным за последние несколько лет, поэтому можете смело использовать его в разработке своих приложений. Он никуда не денется.

Давайте еще раз посмотрим на TypeScript-код, определяющий компонент < my-app >:

В Angular вы используете тег @Component, который известен как декоратор, чтобы отметить классы, которые учитывают элементы, которые могут быть использованы в вашей HTML-разметке. У вас есть возможность передавать свойства @Component для описания элемента. 

  • Свойство selector определяет имя тега при вводе в HTML. Использование селектора < my-app > показывает Angular, что делать, когда он видит тег < my-app > в HTML.
  • Свойство template контролирует, что HTML должен делать, когда используется компонент. Пример использования template: «< h1 >Hello World< /h1 >», тут видно, как Angular определяет, какие действия применять при < my-app > и почему это приложение представляет базовый тег < h1 > при предварительном просмотре в браузере. 

Отображение базового примера “Hello World” в браузере.

Встроенный редактор

Для второго примера, мы создадим простой встроенный редактор – при нажатии пункта меню всплывает небольшое текстовое поле с подсказкой. Мы используем контроллер, который будет инициализировать модели и задавать два разных метода отображения подсказки. Контроллеры являются стандартными функциями JavaScript, которые автоматически выполняются фреймворком Angular. Они связаны с кодом отображения страницы вашего сайта через директивы ng-controller .

HTML:

<!-- Когда элемент выбран, всплывающая подсказка скрывается-->
<div id="main" ng-app ng-controller="InlineEditorController" ng-click="hideTooltip()">
    <!-- Это всплывающая подсказка. Она показывается только, когда значение переменной "showtooltip" – «истина» -->
    <div class="tooltip" ng-click="$event.stopPropagation()" ng-show="showtooltip">
        <!-- ng-модель связывает содержание текстового поля с моделью "value".
         Любые изменения текстового поля будут автоматически задаваться, как значение этой модели, а также вызывать изменения других элементов страницы, связанных с ней.  -->
        <input type="text" ng-model="value" />
    </div>
    <!-- Выбор метода отображения подсказки из вариантов заданных в InlineEditorController (контроллере встроенного редактора), он зависит от значения переменной "showtooltip". -->
    <p ng-click="toggleTooltip($event)">`value`</p>
</div>

JS:

// Контроллер – стандартная функция. Она инициируется, когда 
AngularJS при обработке кода находит атрибут ng-controller.

function InlineEditorController($scope){

	// $scope – специальный объект, который задает параметры отображения 
	// переменной. Здесь вы можете задать некоторые значения по умолчанию	
	$scope.showtooltip = false;
	$scope.value = 'Edit me.';

	// Некоторые вспомогательные функции, которые доступны после инициации	// Аngular.

	$scope.hideTooltip = function(){

		// Когда значение модели меняется, AngularJS автоматически вносит		// изменения в формат вывода. И всплывающее меню скрывается с экрана.


		$scope.showtooltip = false;
	}

	$scope.toggleTooltip = function(e){
		e.stopPropagation();
		$scope.showtooltip = !$scope.showtooltip;
	}
}

CSS:

*{
	margin:0;
	padding:0;
}

body{
	font:15px/1.3 'Open Sans', sans-serif;
	color: #5e5b64;
	text-align:center;
}

a, a:visited {
	outline:none;
	color:#389dc1;
}

a:hover{
	text-decoration:none;
}

section, footer, header, aside, nav{
	display: block;
}

/*-------------------------
	Всплывающее меню редактора.
--------------------------*/

.tooltip{
	background-color:#5c9bb7;

	background-image:-webkit-linear-gradient(top, #5c9bb7, #5392ad);
	background-image:-moz-linear-gradient(top, #5c9bb7, #5392ad);
	background-image:linear-gradient(top, #5c9bb7, #5392ad);

	box-shadow: 0 1px 1px #ccc;
	border-radius:3px;
	width: 290px;
	padding: 10px;

	position: absolute;
	left:50%;
	margin-left:-150px;
	top: 80px;
}

.tooltip:after{
	/* The tip of the tooltip */
	content:'';
	position:absolute;
	border:6px solid #5190ac;
	border-color:#5190ac transparent transparent;
	width:0;
	height:0;
	bottom:-12px;
	left:50%;
	margin-left:-6px;
}

.tooltip input{
	border: none;
	width: 100%;
	line-height: 34px;
	border-radius: 3px;
	box-shadow: 0 2px 6px #bbb inset;
	text-align: center;
	font-size: 16px;
	font-family: inherit;
	color: #8d9395;
	font-weight: bold;
	outline: none;
}

p{
	font-size:22px;
	font-weight:bold;
	color:#6d8088;
	height: 30px;
	cursor:default;
}

p b{
	color:#ffffff;
	display:inline-block;
	padding:5px 10px;
	background-color:#c4d7e0;
	border-radius:2px;
	text-transform:uppercase;
	font-size:18px;
}

p:before{
	content:'✎';
	display:inline-block;
	margin-right:5px;
	font-weight:normal;
	vertical-align: text-bottom;
}

#main{
	height:300px;
	position:relative;
	padding-top: 150px;
}

Когда функция контроллера запускается на исполнение, для нее в качестве параметра задается специальный объект $scope. Он отвечает за ввод текста в текстовый редактор. Для того, чтобы вывести его на экран, нужно прописать дополнительные свойства и функции, которые описывают отображение его элементов. С помощью NG-моделей осуществляется связь кода сайта с текстом, который вводится в рабочее поле редактора. При вводе текста Angular задает соответствующие изменения переменных.

Построение проекта

Последнее обновление: 13.05.2021

В прошлой теме был создан первый простейший проект.

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

В прошлой теме мы определили следующий файл angular.json:

{
  "version": 1,
  "projects": {
    "helloapp": {
      "projectType": "application",
      "root": "",
      "sourceRoot": "src",
      "architect": {
        "build": {
          "builder": "@angular-devkit/build-angular:browser",
          "options": {
            "outputPath": "dist/helloapp",
            "index": "src/index.html",
            "main": "src/main.ts",
            "polyfills": "src/polyfills.ts",
            "tsConfig": "tsconfig.json",
            "aot": true
          }
        },
        "serve": {
          "builder": "@angular-devkit/build-angular:dev-server",
          "options": {
            "browserTarget": "helloapp:build"
          }
        }
      }
    }},
  "defaultProject": "helloapp"
}

Секция описывает конфигурацию команды построения «ng build». В частности, параметр
задает каталог для компилируемых файлов приложения.

То есть мы можем ввести в командную строку команду «ng build» для компиляции проекта

C:\angular\helloapp>ng build

После выполнения этой команды в каталоге проекта появится папка dist/helloapp, где мы сможем увидеть все файлы приложения. Мы можем расположить эти файлы на любой веб-сервер и так же обращаться к главной странице приложения.

Настройка построения

По умолчанию Angular CLI использует ряд настроек при построении. Например, файлы компилируются в режиме production, то есть в режиме, когда они
уже непосредственно готовы к развертыванию. Но мы также можем все настройки построения задать явным образом. Так, изменим файл angular.json следующим образом:

{
  "version": 1,
  "projects": {
    "helloapp": {
      "projectType": "application",
      "root": "",
      "sourceRoot": "src",
      "architect": {
        "build": {
          "builder": "@angular-devkit/build-angular:browser",
          "options": {
            "outputPath": "dist/helloapp",
            "index": "src/index.html",
            "main": "src/main.ts",
            "polyfills": "src/polyfills.ts",
            "tsConfig": "tsconfig.json",
            "aot": true
          },
          "configurations": {
            "production": {
              "optimization": true,
              "outputHashing": "all",
              "sourceMap": false,
              "namedChunks": false,
              "vendorChunk": false,
              "buildOptimizer": true
            }
          }
        },
        "serve": {
          "builder": "@angular-devkit/build-angular:dev-server",
          "options": {
            "browserTarget": "helloapp:build"
          }
        }
      }
    }},
  "defaultProject": "helloapp"
}

Теперь мы добавили для команды build подсекцию «configurations», которая задает дополнительные конфигурации проекта. И здесь указана одна конфигурация —
«production» — то есть набор настроек, которые применяются к построению приложения, когда оно уже готово к развертыванию и полноценному использованию. И здесь определяется следующий набор настроек:

  • : указывает, будет ли использоваться оптимизация

  • : указывает, будет ли в название генерируемого файла добавляться хеш-значение. Значение
    говорит, что в названия всех генерируемых файлов добавляется хеш

  • : определяет, будут ли генерироваться файлы sourceMap

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

  • : определяет, будет ли создаваться для сторонних используемых в приложении библиотек отдельный файл

  • : подключает пакет
    для оптимизации при использовании опции

Стоит отметить, что эти конфигурации никак не влияют на процесс разработки и тестирования приложения. Мы также сможем запускать проект при помощи команды «ng serve». Она не будет применять никаких оптимизаций из конфигурации «production»,
иначе это бы увеличило процесс перекомпиляциии приложения при каждом изменении кода.

Теперь используем конфигурацию production для компиляции. Для этого команде build надо передать флаг :

C:\angular\helloapp>ng build --configuration production

После этого мы увидим в папке dist/helloapp те же файлы, но которые были сгенерированы с помощью настроек из файла angular.json. Изменяя эти настройки,
мы можем задать нужные нам в конкретной ситуации параметры построения проекта.

НазадВперед

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Adblock
detector