Что нового в Angular 5 (по мотивам митапа AngularNYC September)
Посмотрела на днях запись очень интересного митапа AngularNYC September в Нью Йорке.
Первый спикер Alex Zhang рассказывала, что нового в Angular 5. Да, да, окончательная версия уже совсем на подходе (23.10.17 релиз), так что информация довольно полезная.
Правда, насколько я поняла, это то, что входит в бету 5.0.0‑rc.0. Так как релиза-то еще нет.
И, конечно, не все, а самое интересное по мнению Alex.
preserveWhitespaces
preserveWhitespaces — это свойство компонента, с помощью которого можно предотвратить в простейшем случае, например, пробел между кнопками.
Назначаем false:
@Component({ selector: 'app-root', templateUrl:'app/app.component.html', styleUrls: ['app/app.component.css'], preserveWhitespaces: false })
В таком случае в шаблоне компонента будут удалены все пробелы.
Но мы можем пойти дальше и задать его глобально:
platformBrowserDynamic().bootstrapModule(AppModule, { preserveWhitespaces: false; });
Это может уменьшить размер сгенерированного через AOT кода, так как будут удалены все пробелы.
По умолчанию данное свойство сейчас true, но это может измениться в будущем.
Его можно обойти несколькими способами:
- Написать вместо
специальный символ &ngsp; , который компилятором Angular будет трансформироваться в пробел.
<button>one</button>&ngsp;<button>two</button>
-
Можно завернуть кнопки в директиву ngPreserveWhitespaces>
<div ngPreserveWhitespaces> <button>one</button> <button>two</button> </div>
В таком случае, даже при наличии глобального значения preserveWhitespaces: false, между кнопками останется пробел.
Однако, нужно учитывать, что можно получить только один пробел, даже если проставить кучу &ngsp;, они трансформируются в один пробел:
<button>one</button>&ngsp;&ngsp;&ngsp;&ngsp;&ngsp;<button>two</button>
Множественные exportAs имена (multiple exportAs names)
ExportAs — свойство директивы, которое позволяет предоставить доступ к ее экземпляру в шаблоне, а следовательно по сути доступ к API директивы. Подробнее можно прочитать в статье Angular 2 — Take Advantage Of The exportAs Property
Так вот, теперь можно задавать два значения exportAs — одно старое, другое новое, что может помочь с обратной совместимость.
@Directive({ selector: '[some-directive]', exportAs: 'one, two' }) export class SomeDirective { }
<div some-directive #someone="one">ice cream</div> <div some-directive #someone="two">cake</div>
Обновление i18n
i18n — инструмент для локализации, облегчающий перевод приложения, использующий стандартные форматы: xlf, xlf2, xmb.
Сама я вместо него начала работать с ngx-translate. Так как переписываю с AngularJS, где использовался angular-translate, чей синтаксис в шаблоне похож на ngx-translate, так что это мне несколько облегчает жизнь.
Плюс мы используем удаленный сервис переводов, так что просто нужно заменять в шаблонах обозначение слова на само слово. Так что могла перевести не совсем удачно, исправляйте, если что 🙂
Одна из фич — можно помечать i18n текст, не создавая DOM элемент. Это полезно, если для каких-то css целей элемент совсем не нужен.
Раньше было два способа:
- С помощью элемента ng-container:
<ng-container i18n>I don't output any element</ng-container>
-
Обернув текст в html комментарии:
<!--i18n: optional meaning|optional description --> I don't output any element either <!--/i18n-->
Так вот, теперь второй исключен.
Следующий момент — теперь можно добавлять source file information в xmb/xliff переводы.
<msg id="8884523860497558714" meaning="header" desk="desk" source="src/basic.ts;5,6">Translate me</msg>
Это полезно, так как id уникальны, но базируются на тексте. И если изменить текст, то измениться и id.
Поэтому здорово разделять значение и исходный файл.
Подробнее про существовавшую проблему можно прочитать тут.
Далее — больше не используется Intl Api
Почему?
- Многочисленные баги
- Несогласованность с браузерами, ограниченная поддержка
- Вместо этого можно экспортировать данные из Unicode Common Locale Data Repository (CLDR)
- ….но это нарушает некоторые вещи, связанные с i18n и date pipe
Есть изменения и в i18n pipes.
По умолчанию в Angular локальные данные только для en-US.
Если вам нужен LOCALE_ID для другого языка, то необходимо его импортировать.
Старые пайпы i18n можно использовать, но учтите, что они теперь не в CommonModule, а в DeprecatedI18NPipesModule.
import { NgModule } from '@angular/core'; import { CommonModule, DeprecatedI18NPipesModule } from '@angular/common'; @NgModule({ imports: [ CommonModule, // import deprecated module after DeprecatedI18NPipesModule ] }) export class AppModule { }
Со всеми изменениями можно ознакомиться в changeLog и в табличке.
Обновление zone.js
zone.js оборачивает любые асинхронные вещи в браузере и помогает Angular находить где запустить change detection. Что намного круче, чем запускать CD полностью.
События могут быть интенсивными (например, скрол), что делает приложение медленным.
Но теперь есть возможность добавить его в черный список, тем самым событие будет запускаться вне ngZone.
// before load polyfill.js <script> // black list scroll event handler for addEventListener Zone[Zone.__symbol__('BLACK_LISTED_EVENTS')] = ['scroll']; // black list scroll event handler for onscroll const targets = [window, Document.prototype, HTMLBodyElement.prototype, HTMLElement.prototype]; __Zone_ignore_on_properties = []; targets.forEach(function(target) { __Zone_ignore_on_properties.push({ target: target, ignoreProperties: ['scroll'] }); }); </script>
HttpClientModule вместо HttpModule
Улучшения:
1. Больше не нужно вручную извлекать json
Раньше приходилось делать так:
list(): Observable<UserModel> { return this.http.get('api/users') .map(response => response.json()) }
Теперь:
list(): Observable<UserModel> { return this.http.get('api/users') }
2. Можно использовать HttpClientTestingModule, который делает тестирование проще (на первый взгляд — как минимум меньше кода :)) )
Раньше тест мог выглядеть примерно так:
describe('UserService', () => { beforeEach(() => TestBed.configureTestingModule({ imports: [HttpModule], providers: [ MockBackend, BaseRequestOptions, { provide: Http, useFactory: (backend, defaultOptions) => new Http(backend, defaultOptions), deps: [MockBackend, BaseRequestOptions] }, UserService ] })); it('should list the users', async(() => { const userService = TestBed.get(UserService); const mockBackend = TestBed.get(MockBackend); // fake response const expectedUsers = [{ name: 'Cédric' }]; const response = new Response(new ResponseOptions({ body: expectedUsers })); // return the response if we have a connection to the MockBackend mockBackend.connections.subscribe((connection: MockConnection) => { expect(connection.request.url).toBe('/api/users'); expect(connection.request.method).toBe(RequestMethod.Get); connection.mockRespond(response); }); userService.list().subscribe((users: Array<UserModel>) => { expect(users).toEqual(expectedUsers); }); })); });
Теперь же:
describe('UserService', () => { beforeEach(() => TestBed.configureTestingModule({ imports: [HttpClientTestingModule], providers: [UserService] })); it('should list the users', () => { const userService = TestBed.get(UserService); const http = TestBed.get(HttpTestingController); // fake response const expectedUsers = [{ name: 'Cédric' }]; let actualUsers = []; userService.list().subscribe((users: Array<UserModel>) => { actualUsers = users; }); http.expectOne('/api/users').flush(expectedUsers); expect(actualUsers).toEqual(expectedUsers); }); });
3. Можно использовать перехватчики (interceptors)
Эти перехватчики вызываются для каждого запроса и ответа. Они позволяют легко управлять такими задачами как, например, добавление заголовка ко всем запросам.
Пример:
@Injectable() export class GithubAPIInterceptor implements HttpInterceptor { intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { // if it is a Github API request if (req.url.includes('api.github.com')) { // we need to add an OAUTH token as a header to access the Github API const clone = req.clone({ setHeaders: { 'Authorization': `token ${OAUTH_TOKEN}` } }); return next.handle(clone); } // if it's not a Github API request, we just handle it to the next handler return next.handle(req); } }
Больше используемых материалов
Angular — How to use HttpClientModule?
Презентация Alex
P.S. Второй доклад был тоже очень интересным — «Масштабирование приложения, как профи!», но пока не решила стоит ли и его перевести.