Что нового появилось в Angular 6?

Что нового появилось в Angular 6?

Front-end 13.11.2018

В этой статье я сделаю краткий обзор основных захватывающих обновлений, которые принес нам Angular 6, включая RxJS 6, Angular Elements, Ivy рендеринг и многое другое.

RxJS 6

Angular 6 теперь использует RxJS 6. В RxJS 6 несколько изменен способ импорта.

Если в RxJS 5 вы бы писали

import { Observable } from 'rxjs/Observable';
import 'rxjs/add/observable/of';
import 'rxjs/add/operator/map';

const squares$: Observable = Observable.of(1, 2)
  .map(n => n * n);

А в RxJS 5.5 это:

import { Observable } from 'rxjs/Observable';
import { of } from 'rxjs/observable/of';
import { map } from 'rxjs/operators';

const squares$: Observable = of(1, 2).pipe(
  map(n => n * n)
);

В RxJS 6.0 аналогичная запись выглядела бы подобным образом:

import { Observable, of } from 'rxjs';
import { map } from 'rxjs/operators';

const squares$: Observable = of(1, 2).pipe(
  map(n => n * n)
);

Но конкретно сейчас при мигрирации на Angular 6 можно не переписывать все импорты, а подключить библиотеку rxjs-compat, которая позволит вашему коду использовать старый синтаксис.

Также стоит отметить что был выпущен очень классный набор правил tslint, называемый rxjs-tslint. Он содержит 4 правила, которые при добавлении в Ваш проект автоматически переносят все импорты RxJS и код RxJS в совершенно новую версию с помощью простой консольной команды

tslint --fix

Так же подобная операция может быть выполнена глобальной установкой rxjs-tslint с помощью команды

npm i -g rxjs-tslint

и запуском с консоли с указанием пути к файлу конфигов json:

rxjs-5-to-6-migrate -p [PATH_TO_TSCONFIG]

Для проектов на Angular CLI подобная операция будет выглядеть так:

rxjs-5-to-6-migrate -p src/tsconfig.app.json

Angular Elements

Angular Elements дают возможность обернуть ваши компоненты Angular в виде веб-копонент и встроить их в non-Angular среду ( к примеру jQuery app или VueJS app).  Angular Elements существовали уже некоторые время до этого, но ранее были доступны  в экспериментальном формате. С помощью v6 эта разработка продвинулась вперед и теперь входит в состав фреймворка. Возможность создавать и публиковать веб-копоненты Angular и использовать их на любой HTML странице это революционный прорыв. Представьте себе, что теперь вы сможете использовать Angular date-picker в вашем уже готовом React app. С Angular Elements у Вас появилась такая возможность!

Я думаю, мы рассмотрим эту тему более детальней в следующих статьях – напишите в комментариях желаете ли Вы узнать больше о ‘Angular Elements’.

Регистрация сервисов при помощи providedIn

Теперь существует новый способ регистрации сервисов – непосредственно внутри декоратора @Injectable() с использованием нового атрибута providedIn вместо ранее исспользованого providers. Метод providedIn принимает в качестве значения ‘root’ или любой модуль вашего приложения. Когда вы используете ‘root’, ваш сервис будет зарегистрирован как singleton в приложении, и вам не нужно добавлять его к providers корневого модуля. Аналогичным образом, если вы используете providedIn: UsersModule, сервис регистрируется как provider модуля UsersModule без добавления его к остальным компонентам.

@Injectable({
  providedIn: 'root'
})
export class UserService {

}

Этот новый способ был введен, чтобы лучше организовать дерево модулей в приложении, он включен по умолчанию и его стоит использовать в проектах на Angular 6.

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

Теперь Вы можете объявить InjectionToken и напрямую зарегистрировать его с providedIn, а так же прописать ему factory:

export const baseUrl = new InjectionToken('baseUrl', {
    providedIn: 'root',
    factory: () => 'http://localhost:8080/'
 });

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

beforeEach(() => TestBed.configureTestingModule({
  providers: [UserService]
}));

Если UserService использует providedIn: 'root', то аналогичная запись будет выглядеть так:

beforeEach(() => TestBed.configureTestingModule({}));

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

ElementRef<T>

В случае если вам нужно получится ссылку на элемент в своем шаблоне, вы можете использовать @ViewChild , или @ViewChildren, или непосредственно создать ее при помощи ElementRef.

@ViewChild('loginInput') loginInput: ElementRef;

ngAfterViewInit() {
  // nativeElement is now an `HTMLInputElement`
  this.loginInput.nativeElement.focus();
}

Анимация

Больше нет необходимости устанавливать web-animations-js для создания анимации в Angular 6.0, за исключением случаев когда вы используете AnimationBuilder. Вы можете использовать API element.animate, а в случаях когда браузер не поддерживает API element.animate, Angular 6 будет использовать CSS keyframes.

preserveWhitespaces

preserveWhitespaces: false

Эта особенность введена еще в Angular 4.4, но так как ранее флаг по умолчанию имел значение true, то сейчас это false. Флаг был введен для увеличения производительности – со значением false он удаляет лишние пробелы, что может улучшить сгенерированный размер кода, а также ускорить создание компонентов.

ngModel и formControl

Ранее была возможность иметь ngModel и formControl в тех же полях ввода формы, но теперь это устарело, а поддержка будет удалена в Angular 7.0.  Это было несколько запутанно и, вероятно, не выполняло именно то, что вы ожидали.

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

<input [(ngModel)]="user.name" [formControl]="nameCtrl">

Вы можете настроить свое приложение для отображения подобных предупреждений always (дефолтное) / once / never таким образом:

imports: [
  ReactiveFormsModule.withConfig({
    warnOnNgModelWithFormControl: 'never'
  });
]

В любом случае для дальнейшей работы с Angular и, возможно, в дальнейшей миграции на Angular 7.0 лучше использовать или ngModel, или formControl для одного элемента.

Ivy: новый render в Angular

Ivy – следующее поколение Angular рендеринга. Это изменение на написание кода никак влиять не будет и заметить его можно будет по причине того, что Ваше приложение становится все быстрее и меньше.

Для тех кто не в курсе: Angular компилировал Ваши шаблоны с помощью View Engine в версиях Angular 2-4 и Ivy это будет 3-е изменение рендеринга за историю Angular.

Новая версия рендеринга не изменит стиль написания Ваших шаблонов, но добавит некоторые улучшения:

  • время сборки
  • размер bundle

В Angular 6 способ Ivy рендеринга не включен по дефолту, а чтобы использовать его в своем приложении нужно прописать флаг:

"angularCompilerOptions": {
  "enableIvy": true
}

Рассмотрим в чем же принципиальное отличие на простом примере: PonyComponent принимает PonyModel (с именем и цветом) в качестве ввода и отображает изображение в зависимости от цвета, а так же выводит имя.

@Component({
  selector: 'ns-pony',
  template: `<div>
    <ns-image [src]="getPonyImageUrl()"></ns-image>
    <div></div>
  </div>`
})
export class PonyComponent {
  @Input() ponyModel: PonyModel;

  getPonyImageUrl() {
    return `images/${this.ponyModel.color}.png`;
  }
}

Код, созданный с помощью старого средства визуализации
Средство рендеринга, введенное в Angular 4, создало класс для каждого шаблона, называемый ngfactory:

export function View_PonyComponent_0() {
  return viewDef(0, [
    elementDef(0, 0, null, null, 4, "div"),
    elementDef(1, 0, null, null, 1, "ns-image", View_ImageComponent_0),
    directiveDef(2, 49152, null, 0, i2.ImageComponent, { src: [0, "src"] }),
    elementDef(3, 0, null, null, 1, "div"),
    elementDef(4, null, ["", ""])
  ], function (check, view) {
    var component = view.component;
    var currVal_0 = component.getPonyImageUrl();
    check(view, 2, 0, currVal_0);
  }, function (check, view) {
    var component = view.component;
    var currVal_1 = component.ponyModel.name;
    check(view, 4, 0, currVal_1);
  });
}

Основными частями этого кода являются:

  • структуру DOM состоящая из созданных элементов (div, img), их атрибуты и определения. Каждая часть DOM структуры в массиве представлена индексом.
  • функцию обнаружения изменений, в которой записан код, используемый для проверки того, соответствуют ли выражение, используемые в шаблоне, тем же значениями, что и раньше. Здесь он проверяет результат метода getPonyImageUrl и, если он изменяется, обновляет картинку, а так же проверяет имя и, если нужно, обновляет текстовое поле.

Код, созданный с помощью Ivy
Если для приложения на Angular 6 установить флаг enableIvy в значение true, то для подобного примера не сгенерируется ngfactory, но информация поступит непосредственно в статическое поле самого компонента (упрощенный код):

export class PonyComponent {

    static ngComponentDef = defineComponent({
      type: PonyComponent,
      selector: [['ns-pony']],
      factory: () => new PonyComponent(),
      template: (renderFlag, component) {
        if (renderFlag & RenderFlags.Create) {
          elementStart(0, 'figure');
          elementStart(1, 'ns-image');
          elementEnd();
          elementStart(2, 'div');
          text(3);
          elementEnd();
          elementEnd();
        }
        if (renderFlag & RenderFlags.Update) {
          property(1, 'src', component.getPonyImageUrl());
          text(3, interpolate('', component.ponyModel.name, ''));
        }
      },
      inputs: { ponyModel: 'ponyModel' },
      directives: () => [ImageComponent];
    });

    // ... rest of the class

}

Все это теперь содержится в этом статическом поле. Атрибут template содержит эквивалент ngfactory, который мы использовали, но с немного другой структурой. Функция шаблона будет запускаться при каждом изменении, как раньше, но имеет 2 режима:

  • Статический режим выполняется когда компонент только создается и содержит статические DOM ноды
  • Динамический режим выполняется при каждом изменении (при необходимости обновить изображение или текстовое значение)

Несколько приятных плюшек и подведение итогов

Теперь все декораторы встроены непосредственно в их классы (это то же самое для @Injectable, @Pipe, @Directive) – это означает, что когда компилятор переводит ваш шаблон в JavaScript, разрешено использовать информацию, непосредственно доступную для компонента и его декоратора (он не может смотреть на dependencies). Файл metadata.json больше не требуется, что упрощает тулзам обработку вашего кода, и больше инструментов становятся совместимыми с пакетами npm. Это упрощает как создание, так и потребление пакетов! И, конечно же теперь у вашего приложения будет более быстрое время перекомпиляции – изменение одного компонента с меньшей вероятностью вызовет перекомпиляцию для всего приложения.

Теперь с легкостью можно обновить автоматически Ваши @angular зависимости в файле package.json

ng update

Если Вы создаете свои библиотеки на благо человечеству, вы можете создать схему, которая будет использовать ng update для того, что бы ваша библиотека обновлялась у пользователей. ng add – это способ легко и просто добавлять новые возможности в ваше приложение.

ng add @angular/pwa

ng add @angular/elements

ng add @angular/material

ng add @nativescript/schematics

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

Поделиться

Отправить ответ

avatar
  Subscribe  
Notify of