Маршрутизация

Управление маршрутами

Последнее обновление: 18.02.2020

Маршрутизация позволяет сопоставлять запросы к приложению с определенными ресурсами внутри приложения. Рассмотрим, как использовать маршрутизацию Angular в связке с ASP.NET Core. Возьмем, к примеру приложение из прошлой темы:

ASP.NET Core 3 и Angular 9

В частности, в проекте определен контроллер ProductController:

using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNetCore.Mvc;
using HelloAngularApp.Models;

namespace HelloAngularApp.Controllers
{
    [ApiController]
    [Route("api/products")]
    public class ProductController : Controller
    {
        ApplicationContext db;
        public ProductController(ApplicationContext context)
        {
            db = context;
            if (!db.Products.Any())
            {
                db.Products.Add(new Product { Name = "iPhone X", Company = "Apple", Price = 79900 });
                db.Products.Add(new Product { Name = "Galaxy S8", Company = "Samsung", Price = 49900 });
                db.Products.Add(new Product { Name = "Pixel 2", Company = "Google", Price = 52900 });
                db.SaveChanges();
            }
        }
        [HttpGet]
        public IEnumerable>Product< Get()
        {
            return db.Products.ToList();
        }

        [HttpGet("{id}")]
        public Product Get(int id)
        {
            Product product = db.Products.FirstOrDefault(x =< x.Id == id);
            return product;
        }

        [HttpPost]
        public IActionResult Post(Product product)
        {
            if (ModelState.IsValid)
            {
                db.Products.Add(product);
                db.SaveChanges();
                return Ok(product);
            }
            return BadRequest(ModelState);
        }

        [HttpPut]
        public IActionResult Put(Product product)
        {
            if (ModelState.IsValid)
            {
                db.Update(product);
                db.SaveChanges();
                return Ok(product);
            }
            return BadRequest(ModelState);
        }

        [HttpDelete("{id}")]
        public IActionResult Delete(int id)
        {
            Product product = db.Products.FirstOrDefault(x =< x.Id == id);
            if (product != null)
            {
                db.Products.Remove(product);
                db.SaveChanges();
            }
            return Ok(product);
        }
    }
}

И в папке ClientApp/src/app в файле data.service.ts определен сервис, который отправляет запрос к этому контроллеру:

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Product } from './product';

@Injectable()
export class DataService {

    private url = "/api/products";

    constructor(private http: HttpClient) {
    }

    getProducts() {
        return this.http.get(this.url);
    }

    createProduct(product: Product) {
        return this.http.post(this.url, product);
    }
    updateProduct(product: Product) {

        return this.http.put(this.url, product);
    }
    deleteProduct(id: number) {
        return this.http.delete(this.url + '/' + id);
    }
}

Вначале добавим в папку ClientApp/src/app новый файл product-detail.component.ts, который будет выводить данные об одном товаре:

import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { DataService } from './data.service';
import { Product } from './product';

@Component({
    templateUrl: './product-detail.component.html',
    providers: [DataService]
})
export class ProductDetailComponent implements OnInit {

    id: number;
    product: Product;
    loaded: boolean = false;

    constructor(private dataService: DataService, activeRoute: ActivatedRoute) {
        this.id = Number.parseInt(activeRoute.snapshot.params["id"]);
    }

    ngOnInit() {
        if (this.id)
            this.dataService.getProduct(this.id)
                .subscribe((data: Product) => { this.product = data; this.loaded = true; });
    }
}

Здесь мы предполагаем, что этому компоненту через параметр маршрута будет передаваться идентификатор товара, информацию о котором надо отобразить. В конструкторе через объект типа ActivatedRoute (activeRoute.snapshot.params["id"]) мы можем получить значение id товара и сохранить его в переменную. В данном случае параметр маршрута будет также называться "id".

Затем в методе ngOnInit() отправляем серверу запрос, в который передаем ранее полученный id, и в качетве ответа от сервера получаем нужный нам товар. А с помощью дополнительной переменной loaded мы можем указать, что данные загружены.

Для вывода информации о товаре компонент будет использовать файл product-detail.component.html. Поэтому добавим этот файл в папку ClientApp/src/app и определим в нем следующее содержимое:

<div *ngIf="loaded">
    <h2>Модель {{product.id}}</h2>
    <div>
        <p><b>Модель:</b> {{product.name}}</p>
        <p><b>Производитель:</b> {{product.company}}</p>
        <p><b>Цена:</b> {{product.price}}</p>
        <p><a routerLink="/" class="nav-link">К списку моделей</a></p>
    </div>
</div>

Если данные загружены с сервера (то есть переменная loaded равна true), то будут выводиться данные о товаре.

В конце файла определена ссылка на корень приложения. Чтобы привязать ссылку к определенному адресу внутри приложения Angular, у элемента ссылки устанавливается атрибут routerLink, который указывает на нужный адрес. В данном случае в качестве адреса применяется "/", то есть корневой адрес приложения, по которому будет выводиться список товаров.

Теперь добавим в папку ClientApp/src/app новый файл product-list.component.ts, который будет собственно и будет представлять список товаров:

import { Component, OnInit } from '@angular/core';
import { DataService } from './data.service';
import { Product } from './product';

@Component({
    templateUrl: './product-list.component.html',
    providers: [DataService]
})
export class ProductListComponent implements OnInit {

    products: Product[]; 
    constructor(private dataService: DataService) { }

    ngOnInit() {
        this.dataService.getProducts().subscribe((data: Product[]) => this.products = data);
    }
}

При загрузке компонента будут загружаться данные с сервера с помощью метода this.dataService.getProducts(). Для вывода данных определим в той же папке файл product-list.component.html:

<h2>Список моделей</h2>
<table class="table table-striped">
    <tbody>
        <tr *ngFor="let p of products">
            <td><a [routerLink]="['product', p.id]" class="nav-link">{{p?.name}}</a></td>
        </tr>
    </tbody>
</table>

Здесь выводится список объектов. Для каждого объекта выводится только значение поля name, которое заключено в ссылку. Данная ссылка указывает на определенный путь внутри приложения Angular, который определяется с помощью атрибута [routerLink]. При этом теперь этот атрибут фактически принимает массив компонентов, из которых будет создаваться путь. Для создания пути передается строка "product" и id элемента. То есть в итоге получатся пути типа "product/3" или "product/5". Предполагается, что при переходе по таким путям мы будет попадать на компонент ProductDetailComponent, который будет получать id и выводить информацию о товаре.

В итоге весь проект будет выглядеть следующим образом:

Routing in ASP.NET Core Web API and Angular

Теперь изменим файл модуля app.module.ts, в котором сопоставим компоненты с определенными маршрутами:

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
import { HttpClientModule } from '@angular/common/http';
import { Routes, RouterModule } from '@angular/router';

import { AppComponent } from './app.component';
import { ProductListComponent } from './product-list.component';
import { ProductDetailComponent } from './product-detail.component';

// определение маршрутов
const appRoutes: Routes = [
    { path: '', component: ProductListComponent },
    { path: 'product/:id', component: ProductDetailComponent },
    { path: '**', redirectTo: '/' }
];

@NgModule({
    imports: [BrowserModule, FormsModule, HttpClientModule, RouterModule.forRoot(appRoutes)],
    declarations: [AppComponent, ProductListComponent, ProductDetailComponent],
    bootstrap: [AppComponent]
})
export class AppModule { }

Прежде всего здесь определены маршруты в виде объекта Route. Первый маршурт сопоставляется с корнем сайта и будет обрабатываться компонентом ProductListComponent:

{ path: '', component: ProductListComponent }

Второй маршрут будет сопоставляться с путем "product/id", где вместо id будет указан идентификатор товара. И такой маршрут будет обрабатываться компонентом ProductDetailComponent:

{ path: 'product/:id', component: ProductDetailComponent }

Для всех остальных путей ("**") будет идти переадресация на корень приложения.

И другой важный момент состоит в импорте всех нужных модулей:

imports: [BrowserModule, FormsModule, HttpClientModule, RouterModule.forRoot(appRoutes)],

и в добавлении всех ранее используемых компонентов:

declarations: [AppComponent, ProductListComponent, ProductDetailComponent],

Теперь изменим определение главного компонента AppComponent в файле app.component.ts:

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

@Component({
    selector: 'app',
    templateUrl: './app.component.html'
})
export class AppComponent { }

И определим в файле app.component.html следующий код:

<div class="container body-content">
    <router-outlet></router-outlet>
</div>

В элемент <router-outlet> как раз и будет загружаться выбранный для обработки маршрута компонент.

И еще один важный момент, который надо учитывать при работе с маршрутизацией: на веб-странице должен быть указан элемент base:

<base href="/" />

В частности, веб-страница index.html, которая расположена в проекте в папке ClientApp/src и которая загружается по умолчанию, должна содержать этот элемент:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <base href="/" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Angular in ASP.NET Core</title>
    <link rel="stylesheet" href="https://ajax.aspnetcdn.com/ajax/bootstrap/3.3.7/css/bootstrap.main.css" />
</head>
<body>
    <app>Загрузка...</app>
</body>
</html>

Запустим приложение, и по умолчанию будет идти запрос к корню приложения, а значит такой запрос будет обрабатываться компонентом ProductListComponent, который выведет список товаров:

Маршрутизация в Angular и ASP.NET Core

Если мы перейдем по ссылке, которую представляет товар, то такой запрос будет обрабатываться компонентом ProductDetailComponent, и соответственно мы в браузере увидим информацию о товаре:

Routing in Angular и ASP.NET Core
Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850