Статьи

Как обновить угловые проекты до последней версии

В этой статье мы рассмотрим, как обновить Angular проекты до последней версии.

Эта статья является частью 6 Учебника SitePoint Angular 2+ о том, как создать приложение CRUD с помощью Angular CLI .

  1. Часть 0 — Ultimate Angular CLI Справочное руководство
  2. Часть 1. Подготовка и запуск нашей первой версии приложения Todo
  3. Часть 2. Создание отдельных компонентов для отображения списка задач и одной задачи
  4. Часть 3. Обновление сервиса Todo для связи с REST API
  5. Часть 4. Использование углового маршрутизатора для разрешения данных
  6. Часть 5. Добавление аутентификации для защиты частного контента.
  7. Часть 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. Каждый шаг происходит численно с шагом 1.

  2. Когда ошибка исправлена ​​и код остается обратно совместимым, компонент исправления увеличивается:

     v0.0.3 // Before bugfix v0.0.4 // After bugfix 
  3. Когда функциональность добавлена ​​и код остается обратно совместимым, дополнительный компонент увеличивается, а компонент исправления сбрасывается на ноль:

     v0.2.4 // Before addition of new functionality v0.3.0 // After addition of new functionality 
  4. Когда реализуется изменение, которое приводит к тому, что код становится обратно несовместимым, также известным как критическое изменение , основной компонент увеличивается, а вспомогательный и исправляющий компоненты сбрасываются в ноль:

     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 с помощью Руководства по обновлению 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() 

Основными преимуществами конвейерных операторов являются:

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

Метод .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 .

Хорошего вам!