В этой статье мы рассмотрим, как обновить Angular проекты до последней версии.
Эта статья является частью 6 Учебника SitePoint Angular 2+ о том, как создать приложение CRUD с помощью Angular CLI .
- Часть 0 — Ultimate Angular CLI Справочное руководство
- Часть 1. Подготовка и запуск нашей первой версии приложения Todo
- Часть 2. Создание отдельных компонентов для отображения списка задач и одной задачи
- Часть 3. Обновление сервиса Todo для связи с REST API
- Часть 4. Использование углового маршрутизатора для разрешения данных
- Часть 5. Добавление аутентификации для защиты частного контента.
- Часть 6 — Как обновить Angular Projects до последней версии.
В первой части мы узнали, как запустить наше приложение Todo и развернуть его на страницах GitHub. Это работало просто отлично, но, к сожалению, все приложение было собрано в один компонент.
Во второй части мы рассмотрели более модульную архитектуру компонентов и узнали, как разбить этот отдельный компонент на структурированное дерево более мелких компонентов, которые легче понять, использовать повторно и поддерживать.
В третьей части мы обновили наше приложение для связи с бэкэндом REST API, используя RxJS и HTTP-сервис Angular.
В части 4 мы представили Angular Router и узнали, как маршрутизатор обновляет наше приложение при изменении URL браузера и как мы можем использовать маршрутизатор для разрешения данных из нашего внутреннего API.
В части 5 мы добавили аутентификацию в наше приложение и узнали, как мы можем защитить разделы от нашего приложения от несанкционированного доступа.
Не волнуйся! Вам не нужно следовать части 1, 2, 3, 4 или 5 этого урока в течение 6, чтобы иметь смысл. Вы можете просто взять копию нашего репо , проверить код из части 5 и использовать ее в качестве отправной точки. Это объясняется более подробно ниже.
Вверх и работает
Чтобы начать нашу цель по обновлению Angular, убедитесь, что у вас установлена последняя версия Angular CLI. Если вы этого не сделаете, вы можете установить его с помощью следующей команды:
npm install -g @angular/cli@latest
Если вам нужно удалить предыдущую версию Angular CLI, вы можете:
npm uninstall -g @angular/cli angular-cli npm cache clean npm install -g @angular/cli@latest
После этого вам понадобится копия кода из части 5. Это доступно на GitHub . Каждая статья в этой серии имеет соответствующий тег в репозитории, чтобы вы могли переключаться между различными состояниями приложения.
Код, который мы закончили в части 5 и с которого мы начнем в этой статье, помечен как часть-5 . Код, которым мы заканчиваем эту статью, помечен как часть-6 .
Вы можете думать о тегах как псевдоним для определенного идентификатора фиксации. Вы можете переключаться между ними, используя git checkout
. Вы можете прочитать больше об этом здесь .
Итак, чтобы приступить к работе (с установленной последней версией Angular CLI), мы должны сделать это:
git clone [email protected]:sitepoint-editors/angular-todo-app.git cd angular-todo-app git checkout part-5 npm install ng serve
Затем посетите http: // localhost: 4200 / . Если все хорошо, вы должны увидеть работающее приложение Todo.
Обновление Angular: наш план атаки
В этой статье мы обновим Angular и узнаем следующее:
- как работают угловые версии
- где найти инструкцию как обновить Angular
- как обновить наш код с Angular 4 до Angular 5 (Angular 5 является последней версией на момент написания).
К концу этой статьи вы поймете:
- основное значение конкретных версий Angular
- где найти точные инструкции по обновлению приложений Angular
- как выяснить, какие изменения кода требуются (если таковые имеются) для Angular 5.
Давайте начнем!
Значение угловых версий
Чтобы поддерживать процветающую экосистему, Angular должен быть стабильным и эволюционным.
С одной стороны, Angular стремится предоставить разработчикам максимальную стабильность для критически важных приложений. С другой стороны, он постоянно нуждается в адаптации и продвижении для поддержки последних изменений в веб-технологиях.
Поэтому команда Angular решила использовать цикл выпуска на основе времени с семантическим версионированием .
Временной цикл выпуска означает, что мы можем ожидать появления новых версий Angular (Angular 5, Angular 6, Angular 7 и т. Д.) Каждые пару недель или месяцев.
Семантическое управление версиями означает, что номер версии Angular позволяет нам предсказать, не нарушит ли оно наше приложение, если мы перейдем на него.
По сути, семантическая версия выглядит так: Major.Minor.Patch
.
Таким образом, версия v1.3.8
имеет основной компонент со значением 1, второстепенный компонент со значением 3 и компонент исправления со значением 1.
Когда новая версия выпущена, новая версия неявно указывает тип изменения, которое было сделано в коде.
При увеличении семантической версии применяются следующие правила:
-
Каждый шаг происходит численно с шагом 1.
-
Когда ошибка исправлена и код остается обратно совместимым, компонент исправления увеличивается:
v0.0.3 // Before bugfix v0.0.4 // After bugfix
-
Когда функциональность добавлена и код остается обратно совместимым, дополнительный компонент увеличивается, а компонент исправления сбрасывается на ноль:
v0.2.4 // Before addition of new functionality v0.3.0 // After addition of new functionality
-
Когда реализуется изменение, которое приводит к тому, что код становится обратно несовместимым, также известным как критическое изменение , основной компонент увеличивается, а вспомогательный и исправляющий компоненты сбрасываются в ноль:
v7.3.5 // Before implementing backwards incompatible changes v8.0.0 // After implementing backwards incompatible changes
Если вы не знакомы с семантическим версионированием, обязательно ознакомьтесь с этим простым руководством по семантическому версионированию .
Команда Angular сочетает семантическое управление версиями с основанным на времени циклом выпуска для достижения следующих целей:
- новый патч выпускается каждую неделю
- новый минорный выпуск каждый месяц
- новый основной выпуск каждые 6 месяцев
График выпуска не установлен точно, так как могут быть праздники или специальные мероприятия, но это хороший показатель того, что мы можем ожидать в отношении будущих версий.
Вы можете следить за официальным блогом Angular и официальным журналом изменений, чтобы быть в курсе последних событий.
Огромным преимуществом семантических версий является то, что мы можем безопасно обновлять приложения Angular с помощью исправлений или небольших выпусков, не беспокоясь о том, чтобы сломать наши приложения.
Но что, если есть новый основной выпуск?
Угловое руководство по обновлению
Мы уже узнали, что основной релиз может прийти с серьезными изменениями. Итак, как мы узнаем, сломается ли наше существующее приложение или нет, если мы обновим его?
Одним из способов было бы прочитать официальный журнал изменений и просмотреть список изменений.
Гораздо проще использовать Angular Update Guide для обновления Angular. Вы выбираете текущую версию Angular и версию, которую хотите обновить, и приложение сообщает вам точные шаги, которые необходимо предпринять:
Для нашего приложения Angular Todo мы хотим перейти с Angular 4.0 на Angular 5.0.
Давайте выберем уровень сложности приложения Advanced, чтобы увидеть все возможные меры, которые нам нужно предпринять:
Мы получаем полный обзор всех шагов, которые необходимо предпринять для обновления нашего приложения.
Как это мило!
Перед обновлением
Список « До обновления» содержит 12 элементов. Ни один из пунктов не относится к нашему приложению Angular Todo, поэтому мы можем смело переходить к следующему шагу.
Во время обновления
Из списка « Во время обновления» только последний элемент относится к нашему приложению. Нам нужно обновить наши зависимости, поэтому давайте запустим предложенные команды в корне нашего проекта:
$ npm install @angular/{animations,common,compiler,compiler-cli,core,forms,http,platform-browser,platform-browser-dynamic,platform-server,router}@'^5.0.0' [email protected] rxjs@'^5.5.2' $ npm install [email protected] --save-exact
Поскольку мы обновили наш Angular CLI до последней версии в разделе « Вверх и работало », мы также обновили нашу локальную версию:
$ npm install @angular/cli@latest --save-dev
Чтобы убедиться, что наше приложение работает правильно, мы запускаем:
$ ng serve
Если ng serve
не запускается, попробуйте удалить каталог node_modules
и package-lock.json
и запустить npm install
чтобы воссоздать чистый каталог node_modules
и package-lock.json
.
После обновления
Список После обновления содержит четыре элемента, из которых первый и последний относятся к нашему приложению:
- переключиться с
HttpModule
наHttpClientModule
- импортировать операторы RxJS из операторов
rxjs/operators
и использоватьrxjs/operators
канала RxJS
Давайте решать их один за другим.
Переключение с HttpModule на HttpClientModule
В Руководстве по обновлению Angular говорится, что мы должны перейти с HttpModule
на HttpClientModule
.
Если мы проверим примечания к выпуску Angular версии 5.0.0 , мы узнаем, что Angular 4.3 и более поздние версии поставляются с новым HttpClient
который автоматически обрабатывает ответы JSON и поддерживает перехватчики HTTP.
В нем говорится, что для обновления нашего кода мы должны заменить HttpModule
на HttpClientModule
, внедрить службу HttpClient
и удалить все вызовы map(res => res.json())
поскольку новый HttpClient
автоматически анализирует ответы JSON.
Давайте откроем src/app/app.module.ts
и заменим HttpModule
:
// ... import { HttpModule } from '@angular/http'; @NgModule({ declarations: [ // ... ], imports: [ // ... HttpModule, ], providers: [ // ... ], bootstrap: [AppComponent] }) export class AppModule { }
с HttpClientModule
:
// ... import { HttpClientModule } from '@angular/common/http'; @NgModule({ declarations: [ // ... ], imports: [ // ... HttpClientModule, ], providers: [ // ... ], bootstrap: [AppComponent] }) export class AppModule { }
Далее, мы должны использовать сервис HttpClient
вместо сервиса Http
и удалить все вызовы map(res => res.json())
в нашем коде, потому что новый HttpClient
автоматически анализирует ответы для нас.
В третьей части мы централизовали весь код, связанный с HTTP, в сервисе под названием ApiService
, и теперь мы ApiService
преимущества этого подхода.
В результате нам нужно обновить только один файл, поэтому давайте откроем src/app/api.service.ts
и заменим:
import { Http, Headers, RequestOptions, Response } from '@angular/http'; // ... @Injectable() export class ApiService { constructor( private http: Http, private session: SessionService ) { } public signIn(username: string, password: string) { return this.http .post(API_URL + '/sign-in', { username, password }) .map(response => response.json()) .catch(this.handleError); } public getAllTodos(): Observable<Todo[]> { const options = this.getRequestOptions(); return this.http .get(API_URL + '/todos', options) .map(response => { const todos = response.json(); return todos.map((todo) => new Todo(todo)); }) .catch(this.handleError); } public createTodo(todo: Todo): Observable<Todo> { const options = this.getRequestOptions(); return this.http .post(API_URL + '/todos', todo, options) .map(response => { return new Todo(response.json()); }) .catch(this.handleError); } public getTodoById(todoId: number): Observable<Todo> { const options = this.getRequestOptions(); return this.http .get(API_URL + '/todos/' + todoId, options) .map(response => { return new Todo(response.json()); }) .catch(this.handleError); } public updateTodo(todo: Todo): Observable<Todo> { const options = this.getRequestOptions(); return this.http .put(API_URL + '/todos/' + todo.id, todo, options) .map(response => { return new Todo(response.json()); }) .catch(this.handleError); } public deleteTodoById(todoId: number): Observable<null> { const options = this.getRequestOptions(); return this.http .delete(API_URL + '/todos/' + todoId, options) .map(response => null) .catch(this.handleError); } private handleError(error: Response | any) { console.error('ApiService::handleError', error); return Observable.throw(error); } private getRequestOptions() { const headers = new Headers({ 'Authorization': 'Bearer ' + this.session.accessToken }); return new RequestOptions({ headers }); } }
с
import { HttpClient, HttpErrorResponse, HttpHeaders } from '@angular/common/http'; // ... @Injectable() export class ApiService { constructor( private http: HttpClient, private session: SessionService ) { } public signIn(username: string, password: string) { return this.http .post(API_URL + '/sign-in', { username, password }) .catch(this.handleError); } public getAllTodos(): Observable<Todo[]> { const options = this.getRequestOptions(); return this.http .get(API_URL + '/todos', options) .map(response => { const todos = <any[]> response; return todos.map((todo) => new Todo(todo)); }) .catch(this.handleError); } public createTodo(todo: Todo): Observable<Todo> { const options = this.getRequestOptions(); return this.http .post(API_URL + '/todos', todo, options) .map(response => { return new Todo(response); }) .catch(this.handleError); } public getTodoById(todoId: number): Observable<Todo> { const options = this.getRequestOptions(); return this.http .get(API_URL + '/todos/' + todoId, options) .map(response => { return new Todo(response); }) .catch(this.handleError); } public updateTodo(todo: Todo): Observable<Todo> { const options = this.getRequestOptions(); return this.http .put(API_URL + '/todos/' + todo.id, todo, options) .map(response => { return new Todo(response); }) .catch(this.handleError); } public deleteTodoById(todoId: number): Observable<null> { const options = this.getRequestOptions(); return this.http .delete(API_URL + '/todos/' + todoId, options) .map(response => null) .catch(this.handleError); } // ... }
Мы заменяем старые классы из HttpModule
на их новые аналоги из HttpClientModule
.
Более конкретно, мы заменим:
-
import { Http, Headers, RequestOptions, Response } from '@angular/http';
сimport { HttpClient, HttpErrorResponse, HttpHeaders } from '@angular/common/http';
- строка 81:
Response
с помощьюHttpErrorResponse
- строка 90:
Headers
сHttpHeaders
- строка 93:
return new RequestOptions({ headers });
сreturn { headers };
Если мы бежим:
$ ng serve
и перейдите в наш браузер по http://localhost:4200
, мы увидим, что наше приложение все еще работает, как и ожидалось, но теперь использует HttpClientModule
за кулисами.
Пора заняться пунктом 2: импортировать операторы RxJS из операторов rxjs/operators
и использовать rxjs/operators
канала RxJS.
Использование RxJS Pipe Operator
Angular 5 был обновлён для использования RxJS 5.5.2 или новее.
Начиная с версии 5.5, RxJS поставляется с конвейерными операторами . Официальная документация гласит:
Трубный оператор — это любая функция, которая возвращает функцию с сигнатурой:
<T, R>(source: Observable<T>) => Observable<R>
…Вы вводите любого оператора, который вам нужен, из одной точки, под
rxjs/operators
(во множественном числе!). Также рекомендуется использовать нужные методы создания Observable, как показано ниже, с диапазоном:import { range } from >'rxjs/observable/range'; import { map, filter, scan } from >'rxjs/operators'; const source$ = range(0, 10); source$.pipe( filter(x => x % 2 === 0), map(x => x + x), scan((acc, x) => acc + x, 0) ) .subscribe(x => console.log(x))
Хотя это звучит сложно, это по сути означает, что там, где мы ранее использовали цепочечные методы:
source$ .operatorOne() .operatorTwo() .subscribe()
Теперь мы должны импортировать операторы из rxjs/operators
и использовать метод .pipe()
для их применения:
source$ .pipe( operatorOne(), operatorTwo() ) .subscribe()
Основными преимуществами конвейерных операторов являются:
- они могут быть потрясены деревом, что позволяет инструментам уменьшать размер нашего пакета приложений путем удаления неиспользуемого кода
- это простые функции, поэтому мы можем легко создавать собственные пользовательские конвейерные операторы .
Метод .pipe()
сводит влияние на наш код к минимуму.
В нашем приложении есть два элемента, требующие рефакторинга: наш ApiService
и TodosComponent
.
Во-первых, давайте откроем src/app/api.service.ts
для обновления нашего ApiService
:
// import operators from rxjs/operators import { map } from 'rxjs/operators'; // ... @Injectable() export class ApiService { constructor( private http: HttpClient, private session: SessionService ) { } // ... // update .map() to .pipe(map()) public getAllTodos(): Observable<Todo[]> { const options = this.getRequestOptions(); return this.http .get(API_URL + '/todos', options) .pipe( map(response => { const todos = <any[]> response; return todos.map((todo) => new Todo(todo)); }) ) .catch(this.handleError); } // update .map() to .pipe(map()) public createTodo(todo: Todo): Observable<Todo> { const options = this.getRequestOptions(); return this.http .post(API_URL + '/todos', todo, options) .pipe( map(response => { return new Todo(response); }) ) .catch(this.handleError); } // update .map() to .pipe(map()) public getTodoById(todoId: number): Observable<Todo> { const options = this.getRequestOptions(); return this.http .get(API_URL + '/todos/' + todoId, options) .pipe( map(response => { return new Todo(response); }) ) .catch(this.handleError); } // update .map() to .pipe(map()) public updateTodo(todo: Todo): Observable<Todo> { const options = this.getRequestOptions(); return this.http .put(API_URL + '/todos/' + todo.id, todo, options) .pipe( map(response => { return new Todo(response); }) ) .catch(this.handleError); } }
Мы импортируем конвейерный оператор map
из rxjs/operators
и обновляем все вхождения из .map(fn)
в .pipe(map(fn))
.
Далее, давайте откроем src/app/todos/todos.component.ts
чтобы применить те же изменения к TodosComponent
:
// import operators from rxjs/operators import { map } from 'rxjs/operators'; // ... @Component({ selector: 'app-todos', templateUrl: './todos.component.html', styleUrls: ['./todos.component.css'] }) export class TodosComponent implements OnInit { // ... // update .map() to .pipe(map()) public ngOnInit() { this.route.data .pipe( map((data) => data['todos']) ) .subscribe( (todos) => { this.todos = todos; } ); } // ... }
Опять же, мы импортируем конвейерный оператор map
из rxjs/operators
и обновляем .map(fn)
до .pipe(map(fn))
.
Это оно! Цепные операторы в нашем приложении были заменены конвейерными операторами, как и было указано в Руководстве по угловому обновлению.
Если мы перейдем в наш браузер по http://localhost:4200
, мы увидим, что наше приложение по-прежнему работает отлично.
Чтобы убедиться, что мы действительно запускаем Angular 5, мы можем открыть инспектор элементов:
Angular добавляет атрибут ng-version
в app-root
со значением версии, в которой он работает. Мы видим ng-version="5.2.9"
, что указывает на то, что мы используем Angular 5.2.9.
Миссия выполнена! Наше приложение успешно обновлено до версии Angular 5.2.9.
Мы довольно много рассказали, поэтому давайте вспомним то, что узнали.
Резюме
В первой статье мы узнали, как:
- инициализировать наше приложение Todo с помощью Angular CLI
- создать класс
Todo
для представления отдельных задач - создать сервис
TodoDataService
для создания, обновления и удаления задач - используйте компонент
AppComponent
для отображения пользовательского интерфейса - разверните наше приложение на страницах GitHub.
Во второй статье мы реорганизовали AppComponent
чтобы делегировать большую часть его работы:
-
TodoListComponent
для отображения списка задач -
TodoListItemComponent
для отображения одного todo -
TodoListHeaderComponent
для создания новой задачи -
TodoListFooterComponent
чтобы показать, сколькоTodoListFooterComponent
.
В третьей статье мы узнали, как:
- создать макет REST API
- сохранить URL API в качестве переменной среды
- создать
ApiService
для связи с REST API - обновите
TodoDataService
чтобы использовать новыйApiService
- обновить
AppComponent
для обработки асинхронных вызовов API - создайте
ApiMockService
чтобы избежать реальных HTTP-вызовов при выполнении модульных тестов.
В четвертой статье мы узнали:
- почему приложение может нуждаться в маршрутизации
- что такое роутер JavaScript
- что такое Angular Router, как он работает и что он может сделать для вас
- как настроить Angular router и настроить маршруты для нашего приложения
- как сказать Angular роутер, где разместить компоненты в DOM
- как изящно обрабатывать неизвестные URL
- как использовать распознаватель, чтобы позволить Angular router разрешать данные.
В пятой статье мы узнали:
- разница между куки и токенами
- Как создать
AuthService
для реализации логики аутентификации - как создать
SessionService
для хранения данных сеанса - как создать форму для входа с помощью угловой формы
- как создать охрану маршрута, чтобы предотвратить несанкционированный доступ к частям вашего приложения
- как отправить токен пользователя в качестве заголовка авторизации в HTTP-запросе к вашему API
- почему вы никогда не должны отправлять токен своего пользователя третьему лицу.
В этой статье о том, как обновить Angular, мы узнали:
- как работают угловые версии
- что означает номер семантической версии
- как семантическое управление версиями может защитить нас от слепого внесения критических изменений в наше приложение
- Как руководство по обновлению Angular может помочь нам найти подробные инструкции по обновлению Angular
- как заменить
HttpModule
наHttpClientModule
- как обновить наш код RxJS с помощью конвейерных операторов
- как атрибут
ng-version
позволяет нам проверить, какую версию Angular мы используем.
В следующих версиях Angular CLI представит команду ng update
чтобы помочь обновить приложения Angular. Как только появится больше подробностей, мы предоставим вам дополнительную статью о том, как эта новая команда может сделать нашу жизнь еще проще.
До этого вы можете использовать эту статью в качестве руководства по обновлению приложений Angular до последней версии.
Весь код из этой статьи доступен на GitHub .
Хорошего вам!