-2

Routing in Angular (P2)

Tiếp nối chuỗi bài về Angular, hôm nay mình xin trình bày về Routing strategies, Route Parameters,...

Routing Strategies

Cách ứng dụng Angular phân tích và tạo đường dẫn từ và để định tuyến các định nghĩa được gọi là location strategy. (Nó được biết đến với Angular 1là routing modes)

Giá trị default strategy là PathLocationStrategy.

Trong khi sử dụng location strategy, routes được biểu diễn bằng các đường dẫn thông thường, như /home hoặc /contact. Chúng ta có thể thay đổi location strategy được sử dụng cho ứng dụng bằng cách ràng buộc lớp LocationStrategy cho một lớp chiến lược mới, cụ thể. Thay vì sử dụng PathLocationStrategy mặc định chúng ta cũng có thể sử dụng HashLocationStrategy. Lý do chúng ta đang sử dụng chiến lược băm là mặc định là bởi vì nếu chúng ta sử dụng định tuyến HTML5, các URL của chúng ta sẽ kết thúc là các đường dẫn định kỳ (có nghĩa là, không sử dụng các thẻ hash/anchor).

Bằng cách này, các routes sẽ hoạt động khi bạn nhấp vào liên kết và điều hướng ở phía khách hàng, giả sử từ /about to /contact. Nếu chúng ta làm mới trang, thay vì yêu cầu máy chủ cho URL gốc, đó là những gì đang được phục vụ, thay vào đó chúng ta sẽ yêu cầu /about hoặc /contact. Bởi vì không có trang nào được biết đến tại /about máy chủ sẽ trả lại 404. Chiến lược mặc định này hoạt động với các đường dẫn dựa trên băm, như /#/home hoặc /#/contact mà máy chủ hiểu là là /path. (Đây cũng là chế độ mặc định ở Angular 1.)

Cuối cùng, để làm cho ứng dụng ví dụ của chúng ta hoạt động với chiến lược mới này, trước tiên chúng ta phải nhập LocationStrategyHashLocationStrategy: code/routes/basic/app/ts/app.ts

import {LocationStrategy, HashLocationStrategy} from '@angular/common';

và sau đó chỉ cần thêm rằng chiến lược vị trí để các nhà cung cấp của chúng ta NgModule code/routes/basic/app/ts/app.ts

    providers: [
        { provide: LocationStrategy, useClass: HashLocationStrategy }
    ]

Route Parameters

Trong ứng dụng của chúng ta, chúng ta thường muốn điều hướng đến một nguồn cụ thể. Ví dụ: giả sử chúng ta có trang web tin tức và chúng ta đã có nhiều bài báo. Mỗi bài viết có thể có một ID, và nếu chúng ta có một bài báo với ID 3 thì chúng ta có thể điều hướng đến bài viết đó bằng cách truy cập URL: /articles/3 Và nếu chúng ta có một bài viết có ID là 4, chúng ta sẽ truy cập vào nó tại /articles/4 và cứ như vậy. Rõ ràng chúng ta sẽ không muốn viết một tuyến đường cho mỗi bài báo, nhưng thay vào đó chúng ta muốn sử dụng một biến, hoặc tham số tuyến đường. Chúng ta có thể chỉ định rằng một tuyến đường mất một tham số bằng cách đặt một dấu hai chấm : in phía trước của phân đoạn đường dẫn như sau:

/route/:param

Vì vậy, trong trang web tin tức ví dụ của chúng ta, chúng ta có thể chỉ định tuyến đường của chúng ta là:

/articles/:id

Để thêm một tham số cho cấu hình router của chúng ta, chúng ta chỉ định đường dẫn tuyến đường như thế này

const routes: Routes = [
    { path: '', redirectTo: 'search', pathMatch: 'full' },
    { path: 'search', component: SearchComponent },
    { path: 'artists/:id', component: ArtistComponent },
    { path: 'tracks/:id', component: TrackComponent },
    { path: 'albums/:id', component: AlbumComponent },
];

Khi chúng ta truy cập vào tuyến đường /articles/123, phần 123 sẽ được truyền như tham số đường dẫn id tới tuyến đường. Nhưng làm thế nào chúng ta có thể lấy các thông số cho một tuyến đường nhất định? Đó là nơi chúng ta sử dụng các tham số tuyến đường. ActivatedRoute Để sử dụng các tham số tuyến đường, trước hết chúng ta cần phải nhập ActivatedRoute:

import { ActivatedRoute } from '@angular/router';

Tiếp theo, chúng ta inject ActivatedRoute vào constructor của thành phần của chúng ta. Ví dụ: giả sử chúng ta có một Routes xác định những điều dưới đây:

    const routes: Routes = [
        { path: 'articles/:id', component: ArticlesComponent }
    ];

Sau đó, khi chúng ta viết ArticleComponent, chúng ta thêm ActivatedRoute như là một trong những đối số trong khàm khởi tạo:

    export class ArticleComponent {
         id: string;

        constructor(private route: ActivatedRoute) {
            route.params.subscribe(params => { this.id = params['id']; });
        }
    }

Lưu ý rằng route.params là một observable. Chúng ta có thể giải nén giá trị của param thành một giá trị cứng bằng cách sử dụng .subscribe. Trong trường hợp này, chúng ta gán giá trị params ['id'] cho instance variable trên component. Bây giờ khi chúng ta truy cập / articles / 230, thuộc tính id của thành phần của chúng ta sẽ nhận được 230.

Router Hooks

Có những lúc chúng ta có thể muốn làm một số hành động khi thay đổi tuyến đường. Một ví dụ điển hình của đó là chứng thực. Giả sử chúng ta có một đường đăng nhập và một tuyến đường được bảo vệ. chúng ta muốn chỉ cho phép ứng dụng đi đến tuyến đường được bảo vệ nếu tên người dùng chính xác và mật khẩu đã được cung cấp trên trang đăng nhập. Để làm được điều này, chúng ta cần nối vào vòng đời của router và yêu cầu được thông báo khi tuyến đường được bảo vệ đang được kích hoạt. Sau đó chúng ta có thể gọi một dịch vụ chứng thực và hỏi liệu không phải là người sử dụng cung cấp các thông tin phù hợp. Để kiểm tra nếu một thành phần có thể được kích hoạt, chúng ta thêm một lớp bảo vệ vào khóa canActivate in cấu hình router của chúng ta. Hãy xem lại ứng dụng ban đầu của chúng ta, thêm các trường nhập và mật khẩu nhập vào và một tuyến đường được bảo vệ mới mà chỉ hoạt động nếu chúng ta cung cấp một tên người dùng và mật khẩu kết hợp nhất định

AuthService

Chúng ta hãy tạo ra một dịch vụ thực hiện đơn giản và tối thiểu, có trách nhiệm xác thực và ủy quyền các resources code/routes/auth/app/ts/services/AuthService.ts

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

@Injectable()
export class AuthService {
	login(user: string, password: string): boolean {
		if (user === 'user' && password === 'password') {
			localStorage.setItem('username', user);
			return true;
		}
		
		return false;
	}
}

Phương pháp đăng nhập sẽ trở lại đúng nếu cặp user/password được cung cấp bằng 'user' và 'password', tương ứng. Ngoài ra, khi kết hợp, nó sẽ sử dụng bộ lưu trữ cục bộ để lưu tên người dùng. Điều này cũng sẽ đóng vai trò là một lá cờ để cho biết có một người dùng đăng nhập đang hoạt động hay không. Phương thức logout chỉ cần clear giá giá trị username: The logout method just clears the username value:

logout(): any {
    localStorage.removeItem('username');
}

Cuối cùng chúng ta sẽ được 2 method:

  • getUser returns the username or null
  • isLoggedIn uses getUser() to return true if we have a user

code/routes/auth/app/ts/services/AuthService.ts

    getUser(): any {
        return localStorage.getItem('username');
    }

    isLoggedIn(): boolean {
        return this.getUser() !== null;
    }

Ngay sau khi import AUTH_PROVIDERS, chúng ta có thể inject vào app của chúng ta code/routes/auth/app/ts/services/AuthService.ts

export var AUTH_PROVIDERS: Array<any> = [
    { provide: AuthService, useClass: AuthService }
];

Bây giờ chúng ta có AuthService chúng ta có thể tiêm nó vào các thành phần của chúng ta để đăng nhập vào người dùng, kiểm tra người dùng hiện đang đăng nhập, đăng nhập người dùng, vv Trong một chút, chúng ta cũng sẽ sử dụng nó trong router của chúng ta để bảo vệ ProtectedComponent. Nhưng trước tiên, chúng ta hãy tạo ra các thành phần mà chúng ta sử dụng để đăng nhập.

LoginComponent

Thành phần này sẽ hiển thị biểu mẫu đăng nhập, trong trường hợp không có người dùng đã đăng nhập hoặc hiển thị một biểu ngữ nhỏ với thông tin người dùng cùng với liên kết đăng xuất. Mã có liên quan ở đây là các phương pháp đăng nhập và đăng xuất: code/routes/auth/app/ts/components/LoginComponent.ts

export class LoginComponent {
     message: string;

     constructor(private authService: AuthService) {
         this.message = '';
     }

     login(username: string, password: string): boolean {
         this.message = '';
         if (!this.authService.login(username, password)) {
             this.message = 'Incorrect credentials.';
             setTimeout(function() {
                 this.message = '';
             }.bind(this), 2500);
         }
         return false;
     }

     logout(): boolean {
         this.authService.logout();
         return false;
     }

Khi dịch vụ của chúng ta xác nhận thông tin đăng nhập, chúng ta sẽ đăng nhập người dùng. Mẫu thành phần có hai đoạn được hiển thị dựa trên việc liệu người dùng có đăng nhập hay không. Đầu tiên là một hình thức đăng nhập, được bảo vệ bởi * ngIf = "!AuthService.getUser()":

code/routes/auth/app/ts/components/LoginComponent.ts

<form class="form-inline" *ngIf="!authService.getUser()">
	<div class="form-group">
		<label for="username">User:</label>
		<input class="form-control" name="username" #username>
	</div>
	<div class="form-group">
		<label for="password">Password:</label>
		<input class="form-control" type="password" name="password" #password>
	</div>
	<a class="btn btn-default" (click)="login(username.value, password.value)">
		Submit
	</a>
</form>

code/routes/auth/app/ts/components/LoginComponent.ts

 <div class="well" *ngIf="authService.getUser()">
     Logged in as <b>{{ authService.getUser() }}</b>
    <a href (click)="logout()">Log out</a>
</div>

Bây giờ chúng ta có thể xử lý đăng nhập người dùng, chúng ta hãy tạo ra một tài nguyên mà chúng ta sẽ bảo vệ đằng sau một đăng nhập người dùng.

ProtectedComponent

Giả sử chúng ta có 1 component cần được bảo vệ lại chỉ khi đăng nhập mới được truy cập như thế này

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

    @Component({
        selector: 'protected',
        template: `<h1>Protected content</h1>`
    })
    export class ProtectedComponent {
    }

Làm thế nào để bảo vệ nó? và câu trả lời là chúng ta sẽ sử dụng router hook canActivate kết hợp với 1 guard class để implements CanActivate

The LoggedInGuard

Chúng ta sẽ tạo 1 folder được gọi là guards và tạo file loggedIn.guard.ts: code/routes/auth/app/ts/guards/loggedIn.guard.ts

    import { Injectable } from '@angular/core';
    import { CanActivate } from '@angular/router';
    import { AuthService } from 'services/AuthService';
    
    @Injectable()
        export class LoggedInGuard implements CanActivate {
        constructor(private authService: AuthService) {}
        
        canActivate(): boolean {
        return this.authService.isLoggedIn();
        }
    }

Our guard states, nó sẽ implements CanActivate. Điều này được thỏa mãn bằng cách thực hiện một phương pháp canActive. Tiến hành inject AuthService vào lớp này trong constructor và lưu nó dưới dạng một biến private authService. Trong chức năng canActivate của chúng ta, chúng ta kiểm tra this.authService để xem người dùng có isLoggedIn.

Configuring the Router

Các bước cần thiết để configure:

  • import LoggedInGuard
  • use LoggedInGuard trong route configuration
  • include LoggedInGuard vào danh sách providers

Chúng ta làm các bước đó tại file app.ts.

  1. import the LoggedInGuard:
    import {AUTH_PROVIDERS} from 'services/AuthService';
    import {LoggedInGuard} from 'guards/loggedIn.guard';
  1. add canActivate:
const routes: Routes = [
   { path: '', redirectTo: 'home', pathMatch: 'full' },
   { path: 'home', component: HomeComponent },
   { path: 'about', component: AboutComponent },
   { path: 'contact', component: ContactComponent },
   { path: 'protected', component: ProtectedComponent,
   canActivate: [LoggedInGuard]}
];
  1. add LoggedInGuard to our list of providers:
providers: [
    AUTH_PROVIDERS,
    LoggedInGuard,
    { provide: LocationStrategy, useClass: HashLocationStrategy },
]

Well =)) chúng ta đã đi 1 vòng về Router trong Angular. Hi vọng bài viết đã giúp bạn phần nào hiểu rõ hơn về Router in Angular 2, Cảm ơn các bạn đã quan tâm tới bài viết

Tài liệu tham khảo ng-book 2, Felipe Coury, Ari Lerner, Nate Murray, & Carlos Taborda


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí