Angular: routing
Opublikowano ndz 07 lutego 2021 w angular • 3 min read
Client-side rendering - bez zapytania do serwera (wymaga konfiguracji serwera tak aby zwracał index.html skąd angular przejmuje tworzenie ścieżki)
app.component.html - wykorzystanie <router-outlet></router-outlet>
dyrektywy w celu wskazania, w którym miejscu powinien pojawić sie komponent związany z routingiem
<div class="container">
[...]
<div class="row">
<div class="col-xs-12">
<!-- load router here -->
<router-outlet></router-outlet>
</div>
</div>
</div>
app-routing.module.ts - zdefiniowanie ścieżek routingu
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { CreateCharacterComponent } from './create-character/create-character.component';
import { TabsComponent } from './tabs/tabs.component';
const routes: Routes = [
{path: '', component: TabsComponent},
{path: 'new-character', component: CreateCharacterComponent}
];
@NgModule({
// registers routes in the angular router module
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
app.module.ts - zaimportowanie do głównego modułu
import { BrowserModule } from '@angular/platform-browser';
[...]
@NgModule({
declarations: [
...
],
imports: [BrowserModule, AppRoutingModule, FormsModule],
[...]
})
export class AppModule {}
header.component.html - gdzie routing jest stosowany po str html -> routerLinkActive="active"
oraz [routerLinkActiveOptions]="{exact: true}"
<ul class="nav nav-pills">
<!-- add active class when link active -->
<!-- active only when exactly and not only part of the path -->
<li class="nav-item" routerLinkActive="active"
[routerLinkActiveOptions]="{exact: true}">
<a class="nav-link" routerLink="/">Star Wars Characters</a>
</li>
<li class="nav-item" routerLinkActive="active">
<a class="nav-link" routerLink="/new-character"> New Character</a>
</li>
</ul>
Zastosowanie routingu w małej aplikacji
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
import { CrisisListComponent } from './crisis-list/crisis-list.component';
import { HeroesListComponent } from './heroes-list/heroes-list.component';
import { RouterModule } from '@angular/router';
import { PageNotFoundComponent } from './page-not-found/page-not-found.component';
@NgModule({
declarations: [
AppComponent,
CrisisListComponent,
HeroesListComponent,
PageNotFoundComponent
],
imports: [
BrowserModule,
RouterModule.forRoot([
{path: 'crisis-list', component: CrisisListComponent},
{path: 'heroes-list', component: HeroesListComponent},
{path: '', redirectTo: '/heroes-list', pathMatch: 'full'},
{path: '**', component: PageNotFoundComponent}
]),
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
Router-outlet w app.component.html odpowiada za wyświetlenie elementów przypisanych do konkretnej ścieżki (alternatywnie można stosować ngIf)
app.component.html
<h1>Angular Router Sample</h1>
<nav>
<a class="button" routerLink="/crisis-list" routerLinkActive="activebutton">Crisis Center</a> |
<a class="button" routerLink="/heroes-list" routerLinkActive="activebutton">Heroes</a>
</nav>
<router-outlet></router-outlet>
Większa aplikacja może wymagać odosobnienia logiki routingu
Założenie nowego pliku przechowującego/obsługującego Routing w folderze app app-routing.modules.ts
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
// import komponentów, które mają funkcjonować pod konkretną ścieżką
import { MoviesComponent } from './pages/movies/movies.component';
import { PageNotFoundComponent } from './pages/page-not-found/page-not-found.component';
import { CategoriesComponent } from './pages/categories/categories.component';
import { MovieDetailsComponent } from './pages/movies/movie-details/movie-details.component';
// tablica przechowująca routing - ścieżki
const routes: Routes = [
// przekierowanie do movies od razu po załadowaniu ścieżki początkowej/pustego adresu
// pathMatch: 'full' - ścieżka musi zgadzać się w 100%
{ path: '', redirectTo: '/movies', pathMatch: 'full' },
{ path: 'movies', component: MoviesComponent },
// jako id (token) powinna być przekazana przy pomocy routingu wartość
{ path: 'movie/:id', component: MovieDetailsComponent},
{ path: 'categories', component: CategoriesComponent},
// dopasuje się do każdej ścieżki, dlatego musi być na końcu ścieżek
{ path: '**', component: PageNotFoundComponent },
];
// importowanie i eksportowanie
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule],
})
export class AppRoutingModule {}
Zaimportowanie oraz implementacja w app.module.ts Routingu
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
import { HttpClientModule } from '@angular/common/http';
import { PageNotFoundComponent } from './pages/page-not-found/page-not-found.component';
import { MovieCoverComponent } from './shared/movie-cover/movie-cover.component';
import { MovieDetailsComponent } from './pages/movies/movie-details/movie-details.component';
import { MoviesComponent } from './pages/movies/movies.component';
import { CategoriesComponent } from './pages/categories/categories.component';
import { MoviesInCategoryComponent } from './pages/categories/movies-in-category/movies-in-category.component';
// >> Import Routingu <<
import { AppRoutingModule } from './app-routing.module';
@NgModule({
declarations: [
AppComponent,
MovieDetailsComponent,
MoviesComponent,
CategoriesComponent,
PageNotFoundComponent,
MoviesInCategoryComponent,
MovieCoverComponent,
],
// >> Implementacja Routingu <<
imports: [BrowserModule, HttpClientModule, AppRoutingModule],
providers: [],
bootstrap: [AppComponent],
})
export class AppModule {}
Router-outlet w app.component.html odpowiada za wyświetlenie elementów przypisanych do konkretnej ścieżki (alternatywnie można stosować ngIf)
<nav>
<div class="nav-wrapper black">
<a class="brand-logo" routerLink="/">Best Movies</a>
<ul class="right hide-on-med-and-down">
// active - klasa (nazwa przykładowa), która przypisana jest do aktywnego linku
<li><a routerLink="/movies" routerLinkActive="active">Movies</a></li>
<li><a routerLink="/categories" routerLinkActive="active">Categories</a></li>
<li><a>Years</a></li>
</ul>
</div>
</nav>
<router-outlet></router-outlet>
Przekazywanie id/tokena wartości przy pomocy Routingu
movie-cover.component.html
<div class="grid__item">
<div class="card">
<div class="card-image">
<img [src]="movie.poster" />
<span class="card-title black">{{ movie.title }}</span>
</div>
<div class="card-content">
<p>{{ movie.plot.slice(0, 100) }}...</p>
</div>
<div class="card-action">
// >> przekazanie id z obiektu movie <<
<a class="black-text" [routerLink]="['/movie', movie.id]">More >></a>
</div>
</div>
</div>
Wykorzystanie przekazanego id
movie-details.component.ts
import { Component, OnInit } from '@angular/core';
import { Observable } from 'rxjs';
import { HttpService } from '../../../services/http.service';
import { Movie } from '../../../models/movie';
import { ActivatedRoute, ParamMap } from '@angular/router';
import { switchMap } from 'rxjs/operators';
@Component({
selector: 'app-movie-details',
templateUrl: './movie-details.component.html',
styleUrls: ['./movie-details.component.css'],
})
export class MovieDetailsComponent implements OnInit {
movieDetails: Observable<Movie>;
// wykorzystanie serwisu dzięki niemu zastosowanie id
constructor(private http: HttpService, private route: ActivatedRoute) {}
ngOnInit() {
this.movieDetails = this.route.paramMap.pipe(
// odwołanie się do serwisu // subskrypcja zachodzi po str frontendu przy pomocy async pipe
switchMap((params: ParamMap) => this.http.getMovie(params.get('id')))
)
}
goToMovies() {}
}
movie-details.component.html
<div class="row" *ngIf="movieDetails | async as movie">
<div class="col s4"><img [src]="movie.poster" /></div>
<table class="col s8">
<tbody>
[...]
</table>
<button class="btn black" (click)="goToMovies()">Back</button>
</div>
ParamMap jako Observable pozwala na zmianę zawartości i re-renderowanie komponentu, również przy zmianie ścieżki
Źródła: