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

 
_Layout佈局(版面配置)頁

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




主要原因在於
.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



_Layout.cshtml佈局實質前端檔案

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
<!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">
            &copy; 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即可




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



預設就會幫我們安插好@RenderBody()

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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
<!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來做全域性更改設置


再去運行即可套用自己設置的版面配置



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

 ./Views/Shared/_SiteLayout.cshtml

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
<!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)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
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
單指定檔名
1
2
3
4
5
6
7
@{ 
    Layout = "_SiteLayout";
}

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

.\Views\Product\Show.cshtml
指定完整路徑
1
2
3
4
5
6
7
@{
    Layout = "/Views/Shared/_SiteLayout.cshtml";
}

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

運行效果


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


分布視圖、部分檢視/PartialView

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


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




return partial view語法

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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");
        }
    }
}





運行效果
沒有被套用全域默認版面配置


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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
@{
    Layout = "/Views/Shared/_SiteLayout.cshtml";
}

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

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




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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
@{
    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也沒有捨麼不同




在VIew內傳遞值也是可以的


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





但是當到了特定單一View 
比方Home的Index或是Privacy
就又被客製指定為別的標題文字內容



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

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

一般Model 物件傳遞方式

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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
<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>


運行結果即可看到


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

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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
<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>



但可能仍有一些美中不足
我們不想每次在IEnumerable裡面都要把整個命名空間寫進來有點太長
在View裡面當然也可省略命名空間



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



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

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

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



強型別傳入模型的BookController.cs

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
<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是一樣的
就不再多贅述





Ref:
The Partial Tag Helper

5 ways to render a partial view in asp.net core


留言

這個網誌中的熱門文章

何謂淨重(Net Weight)、皮重(Tare Weight)與毛重(Gross Weight)

Architecture(架構) 和 Framework(框架) 有何不同?_軟體設計前的事前規劃的藍圖概念

經得起原始碼資安弱點掃描的程式設計習慣培養(五)_Missing HSTS Header