Razor Page入門 - 8

by vivid 16. 九月 2020 11:44

.NET Magazine國際中文電子雜誌
作 者:許薰尹
審 稿:張智凱
文章編號:N200922202
出刊日期: 2020/9/16

當在ASP.NET Core Razor Page網站應用程式的多個Razor Page之間,如果有HTML標籤、Razor程式碼等需要共用,我們可以使用部分檢視(Partial view)來達成。這篇文章將延續《Razor Page入門 - 7》一文的情境,介紹另一種重複使用HTML標籤、Razor程式碼的方式 – 使用檢視元件(ViewComponent)。在檢視元件(ViewComponent)之中將圖書按照分類來統計圖書的數量,將統計的結果呈現在清單之中。

了解檢視元件(ViewComponent)

檢視元件(ViewComponent)類似部分檢視 (Partial View),但功能更加地強大。檢視元件所需的資料由你在呼叫時提供。檢視元件可以有參數與商業邏輯,為了達到共用,通常從配置(Layout)檔呼叫。檢視元件通常使用來設計動態導覽選單(Dynamic navigation menus)、標籤雲(Tag Cloud)、登入畫面(Login panel)、購物車 (Shopping cart)、最近貼文…等等情境。

檢視元件(ViewComponent)包含兩部份:類別與回傳的結果。

類別在設計時必需遵循以下原則:

  • 必需是一個公開(public)、非巢狀(non-nested)、非抽象(non-abstract)的類別。
  • 可以是一個POCO類別。
  • 類別可繼承其它檢視元件(ViewCompoment),或繼承有套用 [ViewComponent] Attribute的類別。
  • 類別名稱通常後綴「ViewComponent」字串。
  • 完全支援相依性插入 (Dependency Injection)。
  • 在ASP.NET Core MVC的專案中,檢視元件不參與控制器生命周期 (controller lifecycle)。

回傳的結果通常是一個檢視(View),其中包含要顯示在使用者介面的圖檔或HTML。在ASP.NET Razor Page類型的專案中,預設檢視(View)的名稱為「Default.cshtml」,置放於以下資料夾:

Pages/Shared/Components/<view_component_name>

在執行階段呼叫檢視元件(ViewComponent)時將搜尋此路徑來找尋檢視(View)。

定義與使用模型

我們先來定義檢視元件(ViewCompoment)中要使用的「CategoryCount」模型。在「MyModels」專案中加入一個「CategoryCount」類別來描述圖書分類與圖書數量的統計相關資訊,在「Solution Explorer」視窗「MyModels」專案項目上方,按滑鼠右鍵,從快捷選單選擇「Add」-「New Item」項目,在「Add New Item」對話盒中選取「Class」項目,並設定名稱為「Category」,請參考下圖所示:

clip_image002

圖 1:新增「CategoryCount」類別。

在「CategoryCount」類別之中定義「Category」、「Count」兩個屬性,參考以下範例程式碼:

MyModels\CategoryCount.cs

using System;
using System.Collections.Generic;
using System.Text;

namespace MyModels {
  public class CategoryCount {
    public Category Category { get; set; }
    public int Count { get; set; }
  }
}


定義服務

修改「MyServices」服務專案「IBookRepository」介面的程式碼,在「IBookRepository」介面之中,新增一個「GetBookCountByCategory」方法,這個方法將根據圖書分類的值,來計算此分類圖書資料的筆數,同時,這個方法將會回傳「IEnumerable<CategoryCount>」物件,包含所有圖書分類的統計數字,參考以下範例程式碼:

MyServices\IBookRepository.cs

using MyModels;
using System;
using System.Collections.Generic;
using System.Text;

namespace MyServices {
  public interface IBookRepository {
    IEnumerable<Book> GetAllBooks();
    Book GetBook( int id );
    Book Update( Book editBook );
    Book Create( Book newBook );
    Book Delete( int id );
    IEnumerable<CategoryCount> GetBookCountByCategory ( );
  }
}

 

實作服務

接著我們在「MyServices」服務專案「BookRepository」類別中,實作「IBookRepository」介面的「GetBookCountByCategory」方法,查詢出圖書分類統計的資料,請參考以下程式碼:

MyServices\BookRepository.cs

using MyModels;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace MyServices {
  public class BookRepository : IBookRepository {
    private List<Book> _books = null;
    public BookRepository( ) {
      _books = new List<Book>() {
         new Book() {
           Id = 1 ,
           Title = " Essential Programming Language " ,
           Price = 250 ,
           PublishDate = new DateTime( 2019 ,1,2 ) ,
           InStock = true ,
           Description = "Essential Programming Language "  ,
          Category = Category.Computers
         },
         new Book() {
           Id = 2 ,
           Title = "Telling Arts" ,
           Price = 245 ,
           PublishDate = new DateTime( 2019 , 4 , 15 ) ,
           InStock = true ,
           Description = " Telling Arts "  ,
          Category = Category.Arts
         },
           new Book() {
           Id = 3 ,
           Title = "Marvel" ,
           Price = 150  ,
           PublishDate = new DateTime( 2019 , 2, 21 ) ,
           InStock = true ,
           Description = " Marvel "  ,
          Category = Category.Commics
         },
          new Book() {
           Id = 4 ,
           Title = "The Beauty of Cook" ,
           Price = 450 ,
           PublishDate = new DateTime( 2019 ,12, 2 ) ,
           InStock = true ,
           Description = " The Beauty of Cook "  ,
           Category = Category.Cooking
         },
          new Book() {
           Id = 5 ,
           Title = " Learning how to Cook " ,
           Price = 450 ,
           PublishDate = new DateTime( 2020 , 1, 20 ) ,
           InStock = true ,
           Description = " Learning how to Cook  "  ,
           Category = Category.Cooking
         }
      };
    }

    public Book Delete( int id ) {
      Book book = _books.FirstOrDefault( b => b.Id == id );
      if ( book != null ) {
        _books.Remove( book );
      }
      return book;
    }
    public Book Create( Book newBook ) {
      newBook.Id = _books.Max( b => b.Id ) + 1;
      _books.Add( newBook );
      return newBook;
    }

 

    public IEnumerable<Book> GetAllBooks( ) {
      return _books;
    }

    public Book GetBook( int id ) {
      return _books.FirstOrDefault( b => b.Id == id );
    }

    public Book Update( Book editBook ) {
      Book book = _books.FirstOrDefault( b => b.Id == editBook.Id );
      if ( book != null ) {
        book.Title = editBook.Title;
        book.Price = editBook.Price;
        book.PublishDate = editBook.PublishDate;
        book.InStock = editBook.InStock;
        book.Description = editBook.Description;
        book.Category = editBook.Category;
      }
      return book;
    }

    public IEnumerable<CategoryCount> GetBookCountByCategory( ) {
      return _books
        .GroupBy( b => b.Category )
        .Select( g => new CategoryCount() {
          Category = g.Key.Value,
          Count = g.Count()
        } ).ToList();

    }
  }
}

 

設計檢視元件(View Component)

在「MyRazorWeb」專案根目錄下建立「ViewComponents」資料夾。從「Solution Explorer」視窗 - 「MyRazorWeb」專案名稱上方按滑鼠右鍵,從快捷選單選擇「Add」- 「New Folder」選項,請參考下圖所示:

clip_image004

圖 2:新增「ViewComponents」資料夾。

將新建立的資料夾命名為「ViewComponents」。

在「MyRazorWeb」專案定義「CategorySummary」類別。從Visual Studio 2019開發工具 -「Solution Explorer」- 「MyRazorWeb」專案 -「ViewComponents」資料夾上方按滑鼠右鍵,從快捷選單選擇「Add」- 「New Item」項目,開啟「Add New Item」對話盒,從右上方文字方塊輸入「Class」搜尋,選取「Class」,設定名稱為「CategorySummary」,然後按下「Add」按鈕,建立新類別。

clip_image006

圖 3:新增「CategorySummary」類別。

修改「CategorySummary」類別中的程式碼,使其繼承「ViewComponent」類別,並定義一個「Invoke」方法,參考以下程式碼:

MyRazorWeb\ViewComponents\CategorySummary.cs

using Microsoft.AspNetCore.Mvc;
using MyServices;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace MyRazorWeb.ViewComponents {
  public class CategorySummary : ViewComponent {
    private readonly IBookRepository bookRepository;
    public CategorySummary( IBookRepository bookRepository ) {
      this.bookRepository = bookRepository;
    }
    public IViewComponentResult Invoke( ) {
      var result = bookRepository.GetBookCountByCategory();
      return View( result );
    }
  }
}

 

選取Visual Studio 開發工具「Build」-「Build Solution」項目編譯目前的專案,確認程式碼能正確編譯。接著根據下圖的資料夾結構建立「CategorySummary」資料夾:

clip_image008

圖 4:建立「CategorySummary」資料夾。

步驟如下:從Visual Studio 2019開發工具-「Solution Explorer」- 「MyRazorWeb」專案 - 「Pages」-「Shared」資料夾上方按滑鼠右鍵,從快捷選單選擇「Add」- 「New Folder」項目,設定名稱為「Components」。

從「Pages」-「Shared」-「Components」資料夾上方按滑鼠右鍵,從快捷選單選擇「Add」- 「New Folder」項目,設定名稱為「CategorySummary」。這個資料夾的名稱需和檢視元件的名稱一樣。如果檢視元件的名稱以「ViewComponent」字串結尾,則「ViewComponent」字串可以省略,例如檢視元件的名稱為「CategorySummaryViewComponent」,則資料夾名稱要定義為「CategorySummary」。

下一個動作是加入檢視(View)。從「Pages」-「Shared」-「Components」-「CategorySummary」資料夾上方按滑鼠右鍵,從快捷選單選擇「Add」」- 「New Item」項目,開啟「Add New Item」對話盒,選取「Razor View - Empty」項目,將檔名命為「Default.cshtml」檔案,請參考下圖所示:

clip_image010

圖 5:新增「Default.cshtml」檔案。

在新建立的「Default.cshtml」檔案加入以下程式碼,顯示圖書分類圖書筆數統計資料:

MyRazorWeb\Pages\Shared\Components\CategorySummary\Default.cshtml

@model IEnumerable<MyModels.CategoryCount>
  <h3> Book Category Count Summary </h3>
  <ul class = "list-group">
    @foreach ( var item in Model ) {
      <li class = "list-group-item d-flex justify-content-between align-items-center">
        @item.Category
        <span class = "badge badge-primary badge-pill"> @item.Count </span>
      </li>
    }
  </ul>

 

呼叫檢視元件

修改「MyRazorWeb」專案中的「Index」 Razor Page,加入以下程式碼,透過「Component.InvokeAsync」方法叫用檢視元件(ViewComponent),以便在首頁顯示圖書統計資訊:

MyRazorWeb\Pages\Index.cshtml

@page
@model IndexModel
@{
    ViewData["Title"] = "Home page";
}

<div class = "text-center">
    <h1 class = "display-4"> @Model.Greeting </h1>
    <p> Learn about <a href = "https://docs.microsoft.com/aspnet/core"> building Web apps with ASP.NET Core </a>.</p>
</div>

<div class = "text-center">
  @await Component.InvokeAsync( "CategorySummary" )
</div>

@section scripts{
    <div class = "text-center">
        <p> Server Time is @DateTime.Now.ToLongDateString() </p>
    </div>

 

選取Visual Studio開發工具「Build」-「Build Solution」項目編譯目前的專案,確認程式碼能正確編譯。在Visual Studio開發工具,按CTRL+F5執行網站,執行結果參考如下圖所示:

clip_image012

圖 6:使用檢視元件(ViewComponent)。

參數傳遞到檢視元件

若想要在使用檢視元件(ViewComponent)的Razor Page傳遞參數到檢視元件之中,可以在利用「Component.InvokeAsync」方法叫用檢視元件(ViewComponent)時,額外傳入一個匿名型別,要注意的是:匿名型別中的屬性名稱和檢視元件的「Invoke」方法參數名稱一致。

為了說明參數傳遞的設計方式,讓我們來改寫圖書範例程式碼,修改「CategorySummary」類別中的「Invoke」方法,接收一個字串型別的變數「vcTitle」,並將接收到的字串前、後串接「---」符號,並放到ViewBag之中:

MyRazorWeb\ViewComponents\CategorySummary.cs

using Microsoft.AspNetCore.Mvc;
using MyServices;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace MyRazorWeb.ViewComponents {
  public class CategorySummary : ViewComponent {
    private readonly IBookRepository bookRepository;

    public CategorySummary( IBookRepository bookRepository ) {
      this.bookRepository = bookRepository;
    }
    public IViewComponentResult Invoke( string vcTitle) {
      ViewBag.VCTitle=  $"--- {vcTitle} ---";
      var result = bookRepository.GetBookCountByCategory();
      return View( result );
    }
  }
}

 

修改「Default.cshtml」檔案中的程式碼,從「ViewBag」取出標題資訊,顯示在<h3>標籤之中,請參考以下程式碼:

MyRazorWeb\Pages\Shared\Components\CategorySummary\Default.cshtml

@model IEnumerable<MyModels.CategoryCount>
  <h3>
    @ViewBag.VCTitle  
  </h3>
  <ul class = "list-group">
    @foreach ( var item in Model ) {
      <li class = "list-group-item d-flex justify-content-between align-items-center">
        @item.Category
        <span class = "badge badge-primary badge-pill"> @item.Count </span>
      </li>
    }
  </ul>

 

修改「MyRazorWeb」專案中的「Index」 Razor Page,加入以下程式碼,叫用「Component.InvokeAsync」方法時,額外傳入一個匿名物件當參數,請參考以下程式碼:

MyRazorWeb\Pages\Index.cshtml

@page
@model IndexModel
@{
    ViewData["Title"] = "Home page";
}

<div class = "text-center">
    <h1 class = "display-4"> @Model.Greeting </h1>
    <p> Learn about <a href = "https://docs.microsoft.com/aspnet/core"> building Web apps with ASP.NET Core </a>.</p>
</div>

<div class = "text-center">
  @await Component.InvokeAsync( "CategorySummary" , new { vcTitle = "Book Category Count Summary" } )
</div>

@section scripts{
    <div class = "text-center">
        <p> Server Time is @DateTime.Now.ToLongDateString() </p>
    </div>
}

 

選取Visual Studio開發工具「Build」-「Build Solution」項目編譯目前的專案,確認程式碼能正確編譯。在Visual Studio開發工具,按CTRL+F5執行網站,執行結果參考如下圖所示:

clip_image014

圖 7:圖書分類數量統計資料。

標記協助程式(Tag Helper)

標記協助程式(Tag Helper)本身是以C#撰寫的,它的主要用途是讓伺服端程式碼能夠在Razor檔案中,建立並產生HTML元素( Element)來產生UI使用者介面。

除了利用「Component.InvokeAsync」方法叫用檢視元件(ViewComponent)之外,還可以將檢視元件當成標記協助程式(Tag Helper)來使用。若要將檢視元件當作標記協助程式使用,需先在專案中「_ViewImports.cshtml」檔案中註冊。

修改新建立的「_ViewImports.cshtml」檔案,在其中加入以下程式碼,利用「@addTagHelper」指示詞註冊標記協助程式,其中「*」號代表所有可用的標記協助程式;「MyRazorWeb」表示標記協助程式所在的組件,也就是目前的「MyRazorWeb」專案:

MyRazorWeb\Pages\_ViewImports.cshtml

@using MyRazorWeb
@namespace MyRazorWeb.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@addTagHelper *, MyRazorWeb

 

修改「Books\List.cshtml」檔案程式碼,在圖書清單的最後,使用標記協助程式(Tag Helper)顯示圖書分類數量統計資料:

MyRazorWeb\Pages\Books\List.cshtml

@page
@model MyRazorWeb.ListModel
@{
  ViewData[ "Title" ] = "List";
}

<h1>List</h1>

 

<table class = "table">
  <thead>
    <tr>
      <th>
        @Html.DisplayNameFor( model => ( (IList<MyModels.Book>) model.Books )[ 0 ].Id )
      </th>
      <th>
        @Html.DisplayNameFor( model => ( (IList<MyModels.Book>) model.Books )[ 0 ].Title )
      </th>
      <th>
        @Html.DisplayNameFor( model => ( (IList<MyModels.Book>) model.Books )[ 0 ].Price )
      </th>
      <th>
        @Html.DisplayNameFor( model => ( (IList<MyModels.Book>) model.Books )[ 0 ].PublishDate )
      </th>
      <th>
        @Html.DisplayNameFor( model => ( (IList<MyModels.Book>) model.Books )[ 0 ].InStock )
      </th>
      <th>
        @Html.DisplayNameFor( model => ( (IList<MyModels.Book>) model.Books )[ 0 ].Description )
      </th>
      <th>
        @Html.DisplayNameFor( model => ( (IList<MyModels.Book>) model.Books )[ 0 ].Category )
      </th>
      <th></th>
    </tr>
  </thead>
  <tbody>
    @foreach ( var item in Model.Books ) {
      <tr>
        <td>
          @Html.DisplayFor( modelItem => item.Id )
        </td>
        <td>
          @Html.DisplayFor( modelItem => item.Title )
        </td>
        <td>
          @Html.DisplayFor( modelItem => item.Price )
        </td>
        <td>
          @Html.DisplayFor( modelItem => item.PublishDate )
        </td>
        <td>
          @Html.DisplayFor( modelItem => item.InStock )
        </td>
        <td>
          @Html.DisplayFor( modelItem => item.Description )
        </td>
        <td>
          @Html.DisplayFor( modelItem => item.Category )
        </td>
        <td>
          <a asp-page = "./Details" asp-route-Id = "@item.Id"> Details </a> |
          <a asp-page = "./Edit" asp-route-Id = "@item.Id"> Edit </a> |
          <a asp-page = "./Delete" asp-route-Id = "@item.Id"> Delete </a>
        </td>
      </tr>
    }
  </tbody>
</table>
<hr />
<vc:category-summary vc-title = "Book Category Count Summary"> </vc:category-summary>

 

在Razor Page之中使用標記協助程式(Tag Helper)可以以「vc」前置標籤開始,接下來是「:」符號,之後跟隨檢視元件(ViewComponent)的名字。注意,名字要改用kebab case命名法,例如「CategorySummary」檢視元件就要命為「category-summary」,英文字元全部小寫,每個字之間以「-」號區隔開。同樣地,參數的部分也要改成kebab case命名法。

選取Visual Studio開發工具「Build」-「Build Solution」項目編譯目前的專案,確認程式碼能正確編譯。在Visual Studio開發工具,按CTRL+F5執行網站,執行結果參考如下圖所示:

clip_image016

圖 8:使用標記協助程式(Tag Helper)顯示圖書分類數量統計資料。

Tags:

.NET Magazine國際中文電子雜誌 | ASP.NET Razor Pages | 許薰尹Vivid Hsu

新增評論




  Country flag
biuquote
  • 評論
  • 線上預覽
Loading






NET Magazine國際中文電子雜誌

NET Magazine國際中文電子版雜誌,由恆逸資訊創立於2000,自發刊日起迄今已發行超過500篇.NET相關技術文章,擁有超過40000名註冊讀者群。NET Magazine國際中文電子版雜誌希望藉於電子雜誌與NET Developer達到共同學習與技術新知分享,歡迎每一位對.NET 技術有興趣的朋友們多多支持本雜誌,讓作者群們可以有持續性的動力繼續爬文。<請加入免費訂閱>

月分類Month List