02. Компонент редактирования. Роутинг

Роутинг

Перед тем, как заняться роутингом, т.е. имитировать URL-адреса и переход по ссылкам внутри нашего одностраничного приложения, нужно выделить вывод списка друзей в отдельный компонент. Для этого создадим его:

ng generate component friends-list

Теперь у нас появилась папка ./src/app/friends-list. Аккуратно переносим код из ./src/app/app.component.ts в ./src/app/friends-list/friends-list.component.ts. Внимательно сравните листинги двух файлов, обратите внимание, изменилась адресация до сервиса и файла с классом Friend:

Отсюда: ./src/app/app.component.ts

Переносим сюда: ./src/app/friends-list/friends-list.component.ts

			import { Component, OnInit } from '@angular/core';
import { Friend } from './friend';
import { FriendsService } from './friends.service';


@Component({
	selector: 'app-root',
	templateUrl: './app.component.html',
	styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
	title = 'app';
	friends: Friend[];
	selectedFriend: Friend;
	constructor (private friendsService: FriendsService) {
		
	}
	ngOnInit() {
		this.getFriends();
	}
	getFriends():void {
		this.friendsService.getFriends().subscribe(result => {
			this.friends = result;
		});
	}
	selectFriend(friend: Friend):void {
		this.selectedFriend = friend;
	}
}
			
			
			import { Component, OnInit } from '@angular/core';
import { Friend } from '../friend';
import { FriendsService } from '../friends.service';


@Component({
	selector: 'app-friends-list',
	templateUrl: './friends-list.component.html',
	styleUrls: ['./friends-list.component.css']
})
export class FriendsListComponent implements OnInit {
	title = 'app';
	friends: Friend[];
	selectedFriend: Friend;
	constructor (private friendsService: FriendsService) {
		
	}
	ngOnInit() {
		this.getFriends();
	}
	getFriends():void {
		this.friendsService.getFriends().subscribe(result => {
			this.friends = result;
		});
	}
	selectFriend(friend: Friend):void {
		this.selectedFriend = friend;
	}
}
			
			

Теперь частично пересём содержимое из ./src/app/app.component.html в ./src/app/friends-list/friends-list.component.html:

Отсюда: ./src/app/app.component.html

Переносим сюда: ./src/app/friends-list/friends-list.component.html

			<div style="text-align:center">
  <h1>
    Welcome to {{ title }}!
  </h1>
  <img width="150" alt="Angular Logo" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTAgMjUwIj4KICAgIDxwYXRoIGZpbGw9IiNERDAwMzEiIGQ9Ik0xMjUgMzBMMzEuOSA2My4ybDE0LjIgMTIzLjFMMTI1IDIzMGw3OC45LTQzLjcgMTQuMi0xMjMuMXoiIC8+CiAgICA8cGF0aCBmaWxsPSIjQzMwMDJGIiBkPSJNMTI1IDMwdjIyLjItLjFWMjMwbDc4LjktNDMuNyAxNC4yLTEyMy4xTDEyNSAzMHoiIC8+CiAgICA8cGF0aCAgZmlsbD0iI0ZGRkZGRiIgZD0iTTEyNSA1Mi4xTDY2LjggMTgyLjZoMjEuN2wxMS43LTI5LjJoNDkuNGwxMS43IDI5LjJIMTgzTDEyNSA1Mi4xem0xNyA4My4zaC0zNGwxNy00MC45IDE3IDQwLjl6IiAvPgogIDwvc3ZnPg==">
</div>
<ul class="friends-list">
	<li *ngFor="let friend of friends">
		<span (click)="selectFriend(friend)">{{friend.name}}</span>
	</li>
</ul>
<app-friend-detail [friend]="selectedFriend"></app-friend-detail>
			
			
			<ul class="friends-list">
	<li *ngFor="let friend of friends">
		<span (click)="selectFriend(friend)">{{friend.name}}</span>
	</li>
</ul>
<app-friend-detail [friend]="selectedFriend"></app-friend-detail>
			
			

Что останется в ./src/app/app.component.html и ./src/app/app.component.ts?

./src/app/app.component.html:

<div class="outer">
<div style="text-align:center">
  <h1>
    Welcome to {{ title }}!
  </h1>
  <img width="150" alt="Angular Logo" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTAgMjUwIj4KICAgIDxwYXRoIGZpbGw9IiNERDAwMzEiIGQ9Ik0xMjUgMzBMMzEuOSA2My4ybDE0LjIgMTIzLjFMMTI1IDIzMGw3OC45LTQzLjcgMTQuMi0xMjMuMXoiIC8+CiAgICA8cGF0aCBmaWxsPSIjQzMwMDJGIiBkPSJNMTI1IDMwdjIyLjItLjFWMjMwbDc4LjktNDMuNyAxNC4yLTEyMy4xTDEyNSAzMHoiIC8+CiAgICA8cGF0aCAgZmlsbD0iI0ZGRkZGRiIgZD0iTTEyNSA1Mi4xTDY2LjggMTgyLjZoMjEuN2wxMS43LTI5LjJoNDkuNGwxMS43IDI5LjJIMTgzTDEyNSA1Mi4xem0xNyA4My4zaC0zNGwxNy00MC45IDE3IDQwLjl6IiAvPgogIDwvc3ZnPg==">
</div>
<app-friends-list></app-friends-list>
</div>

./src/app/app.component.ts:

import { Component } from '@angular/core';


@Component({
	selector: 'app-root',
	templateUrl: './app.component.html',
	styleUrls: ['./app.component.css']
})
export class AppComponent {
	title = 'app';
}

Зарегистрируем новый компонент в корневом модуле (./src/app/app.module.ts):

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FriendsService } from './friends.service';
import { HttpModule } from '@angular/http';
import { FormsModule } from '@angular/forms';

import { AppComponent } from './app.component';
import { FriendDetailComponent } from './friend-detail/friend-detail.component';
import { FriendsListComponent } from './friends-list/friends-list.component';


@NgModule({
  declarations: [
    AppComponent,
	FriendDetailComponent,
	FriendsListComponent
  ],
  imports: [
    BrowserModule,
	HttpModule,
	FormsModule
  ],
  providers: [FriendsService],
  bootstrap: [AppComponent]
})
export class AppModule { }


Роутинг. Создание адресации

Создаём модуль роутинга:

ng generate module app-routing --flat --module=app

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

--module=app регистрирует наш модуль в массиве imports модуля AppModule. Туда будет добавлено значение AppRoutingModule (в массив imports декоратора @NgModule). Листинг корневого модуля смотрите в самом низу страницы.

Заменяем содержимое модуля роутинга ./src/app/app-routing.module.ts на:

import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
// Импортируем свои компоненты
import { FriendsListComponent } from './friends-list/friends-list.component';
import { FriendDetailComponent } from './friend-detail/friend-detail.component';

const routes: Routes = [
	{ path: '', redirectTo: '/friends-list', pathMatch: 'full' }, // теперь у нас в корневом компоненте ничего нет, поэтому сразу редиректим на компонент списка
	{ path: 'friends-list', component: FriendsListComponent }, // задаём адрес компоненту списка
	{ path: 'detail/:id', component: FriendDetailComponent } // задаём адрес компоненту редактирования с динамически заменяемым параметром
];

@NgModule({
	imports: [ RouterModule.forRoot(routes) ],
	exports: [ RouterModule ]
})

export class AppRoutingModule { }

В шаблоне корневого компонента (./src/app/app.component.html) заменяем <app-friends-list></app-friends-list> на вызов модуля роутинга:

<div class="outer">
<div style="text-align:center">
  <h1>
    Welcome to {{ title }}!
  </h1>
  <img width="150" alt="Angular Logo" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTAgMjUwIj4KICAgIDxwYXRoIGZpbGw9IiNERDAwMzEiIGQ9Ik0xMjUgMzBMMzEuOSA2My4ybDE0LjIgMTIzLjFMMTI1IDIzMGw3OC45LTQzLjcgMTQuMi0xMjMuMXoiIC8+CiAgICA8cGF0aCBmaWxsPSIjQzMwMDJGIiBkPSJNMTI1IDMwdjIyLjItLjFWMjMwbDc4LjktNDMuNyAxNC4yLTEyMy4xTDEyNSAzMHoiIC8+CiAgICA8cGF0aCAgZmlsbD0iI0ZGRkZGRiIgZD0iTTEyNSA1Mi4xTDY2LjggMTgyLjZoMjEuN2wxMS43LTI5LjJoNDkuNGwxMS43IDI5LjJIMTgzTDEyNSA1Mi4xem0xNyA4My4zaC0zNGwxNy00MC45IDE3IDQwLjl6IiAvPgogIDwvc3ZnPg==">
</div>
<router-outlet></router-outlet>
</div>

Из шаблона компонента вывода списка (./src/app/friends-list/friends-list.component.html) убираем назначение события по клику и вызов модуля редактирования, т.к. он теперь лежит по отдельному адресу:

<ul class="friends-list">
	<li *ngFor="let friend of friends">
		<span><a routerLink="/detail/{{friend._id}}">{{friend.name}}</a></span>
	</li>
</ul>

В аттрибуте routerLink прописываем URL, динамическим параметром будет выступать свойство _id объекта Friend.

В логике компонента списка убираем метод selectFriend() и свойство класса selectedFriend.

import { Component, OnInit } from '@angular/core';
import { Friend } from '../friend';
import { FriendsService } from '../friends.service';


@Component({
	selector: 'app-friends-list',
	templateUrl: './friends-list.component.html',
	styleUrls: ['./friends-list.component.css']
})
export class FriendsListComponent implements OnInit {
	title = 'app';
	friends: Friend[];

	constructor (private friendsService: FriendsService) {
		
	}
	ngOnInit() {
		this.getFriends();
	}
	getFriends():void {
		this.friendsService.getFriends().subscribe(result => {
			this.friends = result;
		});
	}

}

Адресация работает, вы можете кликнуть на любом имени и перейдёте на компонент редактирования (он будет пуст), адрес при это будет типа такого: http://localhost:4200/detail/58a53fbed70607f9bbc3cfb4. Если в адресной строке убрать лишнее и перейти на http://localhost:4200/, то сработает наш редирект и вы попадёте на компонент списка по адресу http://localhost:4200/friends-list. Однако, сейчас, как вы только что видели, компонент редактирования пуст, т.к. туда не передаётся экземпляр объекта Friend. Об этом далее.

Результирующие листинги изменённых файлов
import { Component } from '@angular/core';


@Component({
	selector: 'app-root',
	templateUrl: './app.component.html',
	styleUrls: ['./app.component.css']
})
export class AppComponent {
	title = 'app';
}

<div class="outer">
<div style="text-align:center">
  <h1>
    Welcome to {{ title }}!
  </h1>
  <img width="150" alt="Angular Logo" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTAgMjUwIj4KICAgIDxwYXRoIGZpbGw9IiNERDAwMzEiIGQ9Ik0xMjUgMzBMMzEuOSA2My4ybDE0LjIgMTIzLjFMMTI1IDIzMGw3OC45LTQzLjcgMTQuMi0xMjMuMXoiIC8+CiAgICA8cGF0aCBmaWxsPSIjQzMwMDJGIiBkPSJNMTI1IDMwdjIyLjItLjFWMjMwbDc4LjktNDMuNyAxNC4yLTEyMy4xTDEyNSAzMHoiIC8+CiAgICA8cGF0aCAgZmlsbD0iI0ZGRkZGRiIgZD0iTTEyNSA1Mi4xTDY2LjggMTgyLjZoMjEuN2wxMS43LTI5LjJoNDkuNGwxMS43IDI5LjJIMTgzTDEyNSA1Mi4xem0xNyA4My4zaC0zNGwxNy00MC45IDE3IDQwLjl6IiAvPgogIDwvc3ZnPg==">
</div>
<router-outlet></router-outlet>
</div>

import { Component, OnInit } from '@angular/core';
import { Friend } from '../friend';
import { FriendsService } from '../friends.service';


@Component({
	selector: 'app-friends-list',
	templateUrl: './friends-list.component.html',
	styleUrls: ['./friends-list.component.css']
})
export class FriendsListComponent implements OnInit {
	title = 'app';
	friends: Friend[];

	constructor (private friendsService: FriendsService) {
		
	}
	ngOnInit() {
		this.getFriends();
	}
	getFriends():void {
		this.friendsService.getFriends().subscribe(result => {
			this.friends = result;
		});
	}

}

<ul class="friends-list">
	<li *ngFor="let friend of friends">
		<span><a routerLink="/detail/{{friend._id}}">{{friend.name}}</a></span>
	</li>
</ul>

import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';

import { FriendsListComponent } from './friends-list/friends-list.component';
import { FriendDetailComponent } from './friend-detail/friend-detail.component';

const routes: Routes = [
	{ path: '', redirectTo: '/friends-list', pathMatch: 'full' },
	{ path: 'friends-list', component: FriendsListComponent },
	{ path: 'detail/:id', component: FriendDetailComponent }
];

@NgModule({
	imports: [ RouterModule.forRoot(routes) ],
	exports: [ RouterModule ]
})

export class AppRoutingModule { }

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FriendsService } from './friends.service';
import { HttpModule } from '@angular/http';
import { FormsModule } from '@angular/forms';

import { AppComponent } from './app.component';
import { FriendDetailComponent } from './friend-detail/friend-detail.component';
import { FriendsListComponent } from './friends-list/friends-list.component';
import { AppRoutingModule } from './app-routing.module';


@NgModule({
  declarations: [
	AppComponent,
	FriendDetailComponent,
	FriendsListComponent
  ],
  imports: [
	BrowserModule,
	HttpModule,
	FormsModule,
	AppRoutingModule
  ],
  providers: [FriendsService],
  bootstrap: [AppComponent]
})
export class AppModule { }


Дополнительные ссылки