iT邦幫忙

2021 iThome 鐵人賽

DAY 11
0
自我挑戰組

.NET Core MVC網頁應用開發系列 第 13

.NET Core第13天_View常見操作_Layout佈局頁_PartialView部分檢視_強類型視圖(大量資料或物件的傳遞)

_Layout佈局(版面配置)頁

預設當我們新建好.net5 mvc專案後
比方今天新增一個空的Razor檢視
當執行在瀏覽器呈現時候會發現被套用到一個預設佈局頁

https://ithelp.ithome.com.tw/upload/images/20210914/20107452Gvpm7vwC48.png

主要原因在於
.net core mvc預設會產生和之前.net webform MasterPage
有點類似的佈局套版頁機制
_ViewStart.cshtml

本質也是一個View但主要是跟MasterPage一樣的定義佈局頁(佈局視圖)
_ViewStart.cshtml會比其他所有視圖都還要優先被運行

Layout這裡有指定一個名稱_Layout代表指向_Layout.cshtml,
而_Layout.cshtml才是真正的佈局內容。

專案目錄擺放層級
./Views/_ViewStart.cshtml
./Views/Shared/_Layout.cshtml

https://ithelp.ithome.com.tw/upload/images/20210914/20107452nzsrq5z93L.png

_Layout.cshtml佈局實質前端檔案

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>@ViewData["Title"] - Net5MvcApp1</title>
    <link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.min.css" />
    <link rel="stylesheet" href="~/css/site.css" />
</head>
<body>
    <header>
        <nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3">
            <div class="container">
                <a class="navbar-brand" asp-area="" asp-controller="Home" asp-action="Index">Net5MvcApp1</a>
                <button class="navbar-toggler" type="button" data-toggle="collapse" data-target=".navbar-collapse" aria-controls="navbarSupportedContent"
                        aria-expanded="false" aria-label="Toggle navigation">
                    <span class="navbar-toggler-icon"></span>
                </button>
                <div class="navbar-collapse collapse d-sm-inline-flex justify-content-between">
                    <ul class="navbar-nav flex-grow-1">
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Index">Home</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Privacy">Privacy</a>
                        </li>
                    </ul>
                </div>
            </div>
        </nav>
    </header>
    <div class="container">
        <main role="main" class="pb-3">
            @RenderBody()
        </main>
    </div>

    <footer class="border-top footer text-muted">
        <div class="container">
            © 2021 - Net5MvcApp1 - <a asp-area="" asp-controller="Home" asp-action="Privacy">Privacy</a>
        </div>
    </footer>
    <script src="~/lib/jquery/dist/jquery.min.js"></script>
    <script src="~/lib/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
    <script src="~/js/site.js" asp-append-version="true"></script>
    @await RenderSectionAsync("Scripts", required: false)
</body>
</html>

當中主要負責變化引入Content頁採用@RenderBody()語法
來做套版

假設我們Add.cshtml不想套用佈局頁
則可以在檔案中用Razor語法來指定Layout為null即可

https://ithelp.ithome.com.tw/upload/images/20210914/20107452Rmrvub10Tf.png

那想自行創建一個佈局頁
也是可以就在Shared目錄下
新建Razor版面配置頁(佈局),命名通常習慣以下滑線為開頭。

https://ithelp.ithome.com.tw/upload/images/20210914/20107452ONYVNfRuT1.png

預設就會幫我們安插好@RenderBody()
https://ithelp.ithome.com.tw/upload/images/20210914/20107452Cl3YLC3uRw.png

假設這裡設置一個簡單樣式
_Site.cshtml

<!DOCTYPE html>

<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>@ViewBag.Title</title>
</head>
<body>
    <div style="color:red;">
        @RenderBody()
    </div>
</body>
</html>

在去_ViewStart.cshtml來做全域性更改設置

https://ithelp.ithome.com.tw/upload/images/20210914/201074525up07p35NB.png

再去運行即可套用自己設置的版面配置
https://ithelp.ithome.com.tw/upload/images/20210914/20107452WJsPNkwcn4.png

版面配置頁在設置上可以單指定檔名,也可指定完整路徑。
這裡創建額外的版面配置頁

./Views/Shared/_SiteLayout.cshtml

<!DOCTYPE html>

<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>@ViewBag.Title</title>
</head>
<body>
    <div style="height:50px; width:100%; background-color:blue;color:white;">
        <span>SiteLayout測試版面配置</span>
    </div>
    <div style="height:500px;">
        @RenderBody()
    </div>
    <div style="height: 30px; width: 100%; background-color: red; color: white;">
        底部
    </div>
</body>
</html>

新增額外一個ProductController.cs,兩個action method (Index , Show)

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

namespace Net5MvcApp1.Controllers
{
    public class ProductController : Controller
    {
        public IActionResult Index()
        {
            return View();
        }

        public IActionResult Show()
        {
            return View();
        }
    }
}

各自都採用默認檢視
.\Views\Product\Index.cshtml
單指定檔名

@{ 
    Layout = "_SiteLayout";
}

<div>
    Product-視圖頁Index
</div>

.\Views\Product\Show.cshtml
指定完整路徑

@{
    Layout = "/Views/Shared/_SiteLayout.cshtml";
}

<div>
    Product-視圖頁Show
</div>

運行效果
https://ithelp.ithome.com.tw/upload/images/20210914/20107452Qzui8fqjqo.png

上面是一個View就指定一次的方式
若今天視圖有100個就要重複100次
若不想這麼累可以直接從_ViewStart.cshtml
更改默認全域版面配置(布局)頁,之後每頁就不需要去設置Layout了。

分布視圖、部分檢視/PartialView

主要用於某個主視圖中的部分內容,常用在部分內容更新。
於Controller當中會使用 PartialView()語法來回傳
可返回指定的View或Model Entity,跟View()使用一樣。
跟一般的不需要引用佈局頁。

分布視圖的新建
在./Views/Product 目錄新建檢視(跟一般檢視新增方式一樣)

https://ithelp.ithome.com.tw/upload/images/20210914/201074525y8xvRCRCg.png

https://ithelp.ithome.com.tw/upload/images/20210914/20107452Adf3SmP7AO.png

return partial view語法

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

namespace Net5MvcApp1.Controllers
{
    public class ProductController : Controller
    {
        public IActionResult Index()
        {
            return View();
        }

        public IActionResult Show()
        {
            return View();
        }

        public IActionResult Add()
        {
            return PartialView("_Partial01");
        }
    }
}

https://ithelp.ithome.com.tw/upload/images/20210914/201074523wpFxyPOfG.png

運行效果
沒有被套用全域默認版面配置
https://ithelp.ithome.com.tw/upload/images/20210914/20107452aJ7E3sO3N5.png

若在某一個主頁面比方Product的Show檢視
去加載分布視圖
此時可以在檢視中使用partial tag來實踐

@{
    Layout = "/Views/Shared/_SiteLayout.cshtml";
}

<div>
    Product-視圖頁Show
</div>

<div>
    <partial name="/Views/Product/_Partial01.cshtml" />
</div>

https://ithelp.ithome.com.tw/upload/images/20210914/20107452vxyGzoT0kw.png

PartialView的tag寫法主要是取代
Html.Partial 、 Html.RenderPartial這兩個同步的寫法方式
但有時你的分布頁可能會需要等待loading這時
會建議改採用非同步處理
也就是不會有上半部沒Load完下半部也會Delay
寫法也可以換成@await Html.PartialAsync("分布視圖路徑")
或者@{await Html.RenderPartialAsync("/Views/Product/_Partial01.cshtml");}
以下是程式範本

@{
    Layout = "/Views/Shared/_SiteLayout.cshtml";
}
<div>
    Product-視圖頁Show
</div>
<div>
    @*第1種.partial tag Tag helper*@
    <partial name="/Views/Product/_Partial01.cshtml" />

    @* 同步的方式-------*@
    
    @*Html Helper第1種.Html.Partial*@
    @Html.Partial("/Views/Product/_Partial01.cshtml")
    @*Html Helper第2種.Html.RenderPartial*@
    @{
        Html.RenderPartial("/Views/Product/_Partial01.cshtml");
    }

    @* 同步的方式-------*@

    @* 非同步的方式-------*@
    
    @*Html Helper第1種.Html.PartialAsync*@
    @await Html.PartialAsync("/Views/Product/_Partial01.cshtml")

    @*Html Helper第2種.Html.RenderPartialAsync*@
    @{
        await Html.RenderPartialAsync("/Views/Product/_Partial01.cshtml");
    }

    @* 非同步的方式-------*@

</div>

<div>
    後面的網頁內容
</div>

View資料傳遞

從Controller要傳遞資料到View基本上跟以前的.net mvc也沒有捨麼不同
ASP.NET MVC(六)_ViewData,ViewBag,TempData用法與差異比較

https://ithelp.ithome.com.tw/upload/images/20210914/20107452HYbvI4yUgy.png

在View內傳遞值也是可以的
https://ithelp.ithome.com.tw/upload/images/20210914/20107452lnKQTkvmUp.png

版面配置頁跟View之間傳遞資料方式
比方預設的_Layout中有一個Title的ViewData
在版面配置頁有默認值

https://ithelp.ithome.com.tw/upload/images/20210914/20107452gLLSGOfZCs.png

但是當到了特定單一View
比方Home的Index或是Privacy
就又被客製指定為別的標題文字內容
https://ithelp.ithome.com.tw/upload/images/20210914/20107452Xes7SmpICm.png

強類型(型別)視圖(大量資料或物件的傳遞)
在從控制器要傳送資料到View過程若只是單純的string或者數值要傳送可以依賴
ViewData , ViewBag

但若需要向VIew傳送比較大量的資料或者要有跨server互動作用的資料
則會建議採用強類型(型別)視圖機制

一般Model 物件傳遞方式

在Models目錄新增一個Class (模型類別)
命名為BookViewModel
(BookViewModel.cs)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Net5MvcApp1.Models
{
    public class BookViewModel
    {
        public string Name { get; set; }
        public double Price { get; set; }
    }
}

建立好Book控制器並撰寫程式
BookController.cs

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

namespace Net5MvcApp1.Controllers
{
    public class BookController : Controller
    {
        public IActionResult Index()
        {
            List<BookViewModel> bookViewModels = new List<BookViewModel>()
            {
                new BookViewModel(){Name="外宿族必備寶典:1次搞懂租屋細節",Price=300},
                new BookViewModel(){Name="年年18%,一生理財這樣做就對了(全新修訂版)",Price=380},
                new BookViewModel(){Name="無腦理財術,小資大翻身!:無論起薪多少都受用的超簡單投資法",Price=320}
            };
            ViewBag.BookList = bookViewModels;
            return View();
        }
    }
}

與新增預設View後
./Views/Book/Index.cshtml

<table class="table table-bordered">
    <tr class="bg-primary">
        <td style="color:white">圖書名稱</td>
        <td style="color:white">圖書單價</td>
    </tr>
    @foreach (var item in ViewBag.BookList)
    {
        <tr>
            <td>@item.Name</td>
            <td>@item.Price</td>
        </tr>
    }

</table>

運行結果即可看到
https://ithelp.ithome.com.tw/upload/images/20210914/20107452M5LLa7v3XF.png

透過ViewBag將dynamic書單列表呈現在View中
但當欄位一多的時候這種方式可能比較不方便沒有智能提示容易打錯

因此也可以透過IEnumerable的泛型來做傳遞,在遍歷Book Model的時候
就能有智能提示對應屬性

<table class="table table-bordered">
    <tr class="bg-primary">
        <td style="color:white">圖書名稱</td>
        <td style="color:white">圖書單價</td>
    </tr>
    @foreach (var item in ViewBag.BookList as IEnumerable<Net5MvcApp1.Models.BookViewModel>)
    {
        <tr>
            <td>@item.Name</td>
            <td>@item.Price</td>
        </tr>
    }

</table>

https://ithelp.ithome.com.tw/upload/images/20210914/20107452Dmn4VG9tS5.png

但可能仍有一些美中不足
我們不想每次在IEnumerable裡面都要把整個命名空間寫進來有點太長
在View裡面當然也可省略命名空間
https://ithelp.ithome.com.tw/upload/images/20210914/20107452N9N9y4xSpi.png

可以省略主因在於.net core MVC中有提供一個導入文件
./Views/_ViewImports.cshtml
裡面自動引入該name space

https://ithelp.ithome.com.tw/upload/images/20210914/20107452H1sHOKDia4.png

強類型(型別)視圖傳遞方式

不透過ViewData或ViewBag這些不確定的型別(泛型列表)
而是採用具體的Class型態(強型別)在控制器和視圖之間做資料傳遞互動,將Model跟View結合起來組成的View就稱為強類型(型別)視圖。

於Controller直接透過 return View(某型別物件(集合) )回傳,也就是給ViewData中的Model賦值。
於View當中可以用List<某型別物件> 或IEnumerable<某型別物件>方式遍歷

https://ithelp.ithome.com.tw/upload/images/20210914/20107452Ie1PnVvFIx.png

強型別傳入模型的BookController.cs

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

namespace Net5MvcApp1.Controllers
{
    public class BookController : Controller
    {
        public IActionResult Index()
        {
            List<BookViewModel> bookViewModels = new List<BookViewModel>()
            {
                new BookViewModel(){Name="外宿族必備寶典:1次搞懂租屋細節",Price=300},
                new BookViewModel(){Name="年年18%,一生理財這樣做就對了(全新修訂版)",Price=380},
                new BookViewModel(){Name="無腦理財術,小資大翻身!:無論起薪多少都受用的超簡單投資法",Price=320}
            };
            //ViewBag.BookList = bookViewModels;
            return View(bookViewModels);
        }
    }
}

強型別視圖訪問的View(./Views/Book/Index.cshtml)

<table class="table table-bordered">
    <tr class="bg-primary">
        <td style="color:white">圖書名稱</td>
        <td style="color:white">圖書單價</td>
    </tr>

    @model List<BookViewModel>
    @foreach (var item in Model)
    {
        <tr>
            <td>@item.Name</td>
            <td>@item.Price</td>
        </tr>
    }
</table>

在.net core mvc當中的razor語法則跟之前.net mvc是一樣的
ASP.NET MVC(五)_Razor語法筆記
就不再多贅述

本篇同步發表至個人部落格
https://coolmandiary.blogspot.com/2021/07/net-core13viewlayout.html

Ref:
The Partial Tag Helper
https://www.learnrazorpages.com/razor-pages/tag-helpers/partial-tag-helper

5 ways to render a partial view in asp.net core
https://nitishkaushik.com/how-to-render-a-partial-view-in-asp-net-core/


上一篇
.NET Core第12天_服務依賴注入_IoC容器生命週期_ConfigureServices
下一篇
.NET Core第14天_檢視模型ViewModel_Controller跟View雙向資料傳遞方式
系列文
.NET Core MVC網頁應用開發30

尚未有邦友留言

立即登入留言