Blazor 是新一代的開發方式,可以透過雙向綁定,可以用純C#來開發Web端的SPA應用。這一篇是起手式的範例,參考官方的內容,自己加上官方沒有的完成disabled,並且加上刪除的按鈕功能。(基本上與Vue的範例類似的功能。筆記下來提供自己未來參考,也提供網友參考
緣起
Blazor 是新一代的開發方式,可以透過雙向綁定,可以用純C#來開發Web端的SPA應用。這一篇是起手式的範例,參考官方的內容,自己加上官方沒有的完成disabled,並且加上刪除的按鈕功能。(基本上與Vue的範例類似的功能。筆記下來提供自己未來參考,也提供網友參考。
新增專案
小喵開發的的工具是 Visual Studio 2019 ,所以操作過程與官方的說明流程會有一點點不太一樣。
我們開啟 Visual Studio 2019,新增專案的時候,選擇Blazor如下圖
接著按【下一步】
接著,設定專案名稱與路徑,如下圖
按【建立】
接著,有兩種類型的Blazor可以供選擇,我們選擇上面的那個,進階那邊暫時取消https,按下建立,如下圖
就醬子,初始的專案就建立完成。
觀察專案
我們首先來觀察一下專案,主要對比MVC的專案,所以如果對MVC有一些基本的認知,會比較容易理解這樣的專案結構是怎麼回事
首先,看看一開始建立好的專案樣子
執行起來的樣子
我們先把專案執行起來,然後再來細說這些內容在程式裡面是哪個部分
這篇文章,不會針對初始的所有項目詳細說明只會針對TodoList會動用到的做一些簡單的說明。
- 首先是左邊的Menu,我們會新增一個項目,可以點選到TodoList的頁面。
- 接著,會新增一個TodoItem的類別,用來記錄Todo的內容與是否完成。
- 再接著,我們要新增一個 TodoList 元件(頁面),並且大部分的程式都會寫在這個元件(頁面)。
Pages資料夾
顧名思義,這個資料夾就是放我們的頁面。不過如果您去觀察裡面相關檔案的副檔名,您會發現很多的副檔名是razor而不是cshtml。
Blazor 的副檔名 razor 是【元件(Component)】,您可以想像他是頁面中的某一部分區塊,可以單獨的一個元件運作成類似一個頁面,也可以在元件中放入多個別的元件。
我們來新增一個Todos的元件在Pages裡面:
- 在Pages資料夾上按右鍵,【加入】→【Razor元件】,名稱就叫做【Todos.razor】
- 在元件的程式碼中,最上面,新增【@page "/Todos"】來指定他的【Routing】。您沒看錯,指定Routing就這麼簡單
接著我們把專案執行起來,然後在網址的後面加上【/Todos】,就可以從首頁,切換到這一元件(頁)來。
相關內容如下:
@page "/Todos"
<h3>Todos</h3>
@code {
}
Shared 資料夾
這與MVC的View很像,會把一些共用的部分,放在這,例如MVC裡面會放 Layout 在 Shared一樣。Blazor在Shared中有個【MainLayout.razor】他的副檔名是【razor】,在Blazor裡面是屬於【Component】。
我們觀察裡面有個【NavMenu.razor】,這就是畫面中的左邊Menu,我們新增以下的程式碼,讓他多一個項目,並指向Todos
<li class="nav-item px-3">
<NavLink class="nav-link" href="Todos">
<span class="oi oi-list-rich" aria-hidden="true"></span> Todos
</NavLink>
</li>
再次執行起來,Menu就可以直接切換到Todos元件(頁)。
Data資料夾
有點類似MVC裡面的Model,我們會把用到的資料類別,比較共用的部分,放在這裡。但是也可以建立在獨立的Component裡面,這部分要依據實際專案的需求來決定怎麼放。
我們把TodoItem這樣的類別加入到 Data 資料夾中。在Data資料夾上按右鍵,【加入】→【類別】,名稱就設定為【TodoItemInfo.cs】,相關程式碼如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace BlazorTodos.Data
{
public class TodoItemInfo
{
public string Title { get; set; } = "";
public bool Finish { get; set; } = false;
}
}
雙向綁定測試
之前在開發 Web SPA 應用的時候,無論是 Angular 或者是 Vue ,他們的一大特色就是雙向綁定,可以讓 Script 的 JSON 物件與畫面中的html進行雙向綁定的設定,這簡化的在開發上的許多撰寫。
上訴的這些都是純 Client 端透過 Javascript 的一些 Framework 來做到。但是 Blazor 可是使用 C# 在 Server 端開發,他透過 SignalR 的機制,讓我們可以用純 Server 端的寫法,就可以做到 SPA 的效果,而且是雙向的綁定。這微軟的黑科技真是令人驚豔。以下就來先簡單的體驗一下:
首先,我們剛剛新增了Todos的元件,我們先在下面 Code 的地方,定義好一個private的欄位變數 newTodoTitle,是一個字串。
並且在畫面中,我們安排了一個input,設定bind到【newTodoTitle】這個欄位上。然後下方,再用Razor顯示newTodoTitle。就像以下這樣的程式碼:
<input type="text" @bind="newTodoTitle" />
<hr />
@newTodoTitle
@code {
private string newTodoTitle = "abc";
}
就醬子,我們把畫面執行起來,並且試著運作看看。
您會發現到,當input裡面會直接出現預設值 abc
當你修改他的內容,然後游標跳離這個input的時候,下方的顯示資料,會直接地跟著變化。
您根本不需要去寫送回、取得資料、在把資料放回畫面中的任何相關程式碼。您只需要知道一點,他的綁定是雙向的,有異動,會自動傳回並且渲染到對應使用該欄位的相關內容。
在撰寫這類的雙向綁定,重點要放在【資料(類別物件)的異動】。去處理好資料的異動,程式中不會對html的dom去做直接的操作與修改。
有了這樣的概念後,就可以來開始撰寫Todo的相關程式碼了。
Todo相關程式碼
首先,我們在畫面上方,加上一個using的宣告,用以告知我們要引用到Data中的類別。
@using BlazorTodos.Data;
接著在Code的地方,我們Code宣告一下用來存放TodoList的物件集合
private List<TodoItemInfo> oTodos = new List<TodoItemInfo>();
然後是畫面上的安排與榜定,我們新增一個ul>li的項目,在li外撰寫Razor的foreach,把oTodos的每個項目的Title顯示出來。就像醬子
<ul>
@foreach (var tTodo in oTodos)
{
<li>@tTodo.Title</li>
}
</ul>
在input後面新增一個button的按鈕,透過【@onclick】來宣告他要運作的void函數【AddTodo】,然後在 Code 的地方撰寫 AddTodo 的內容
相關完整的程式碼如下:
@page "/Todos"
@using BlazorTodos.Data;
<h3>Todos</h3>
<input type="text" @bind="newTodoTitle" />
<button @onclick="AddTodo">AddTodo</button>
<hr />
<ul>
@foreach (var tTodo in oTodos)
{
<li>@tTodo.Title</li>
}
</ul>
@code {
private string newTodoTitle = "abc";
private List<TodoItemInfo> oTodos = new List<TodoItemInfo>();
private void AddTodo()
{
//宣告一個todoItem
TodoItemInfo tTodo = new TodoItemInfo();
//將Input的內容給todoItem的Title,並設定預設的Finish是False
tTodo.Title = newTodoTitle;
tTodo.Finish = false;
//把tTodo加到oTodos裡面去
oTodos.Add(tTodo);
//把input用到的newTodoTitle清為空字串,等待下一個Todo輸入
newTodoTitle = "";
}
}
就醬子,我們執行後,就可以把input輸入的內容,加到oTodos裡面,並且顯示出來。
官方的範例,大概就介紹到這邊。
但我們希望可以做更多....
- 我們希望可以用input text在li中顯示Todo的內容,並且可以直接修改
- 我們用一個Checkbox來綁上Todo的Finish,當工作完成我們就打勾
- 當打勾設定完成,我們希望input text設定為disabled,完成了就不能再修改
- 在每一項的後面,新增一個按鈕,完成的時候才能看到
- 可以刪除該條Todo的項目
好,願望清單開出來了,我們就來一一的實踐他。
這裡面的1~4並不會特別的難,所以這部份就如以下的程式碼即可。
@page "/Todos"
@using BlazorTodos.Data;
<h3>Todos</h3>
<input type="text" @bind="newTodoTitle" />
<button @onclick="AddTodo">AddTodo</button>
<hr />
<ul>
@foreach (var tTodo in oTodos)
{
<li>
<input type="checkbox" @bind="tTodo.Finish" />
<input type="text" @bind="tTodo.Title" disabled="@tTodo.Finish" />
@if (tTodo.Finish)
{
<button>X</button>
}
</li>
}
</ul>
<hr />
@code {
private string newTodoTitle = "abc";
private List<TodoItemInfo> oTodos = new List<TodoItemInfo>();
private void AddTodo()
{
//宣告一個todoItem
TodoItemInfo tTodo = new TodoItemInfo();
//將Input的內容給todoItem的Title,並設定預設的Finish是False
tTodo.Title = newTodoTitle;
tTodo.Finish = false;
//把tTodo加到oTodos裡面去
oTodos.Add(tTodo);
//把input用到的newTodoTitle清為空字串,等待下一個Todo輸入
newTodoTitle = "";
}
private void Del(int currentIdx)
{
oTodos.RemoveAt(currentIdx);
}
}
但是第五項(刪除),是比較令人頭疼的~
首先刪除必須知道要刪除第幾個,因此我需要有一個變數來記錄目前是第幾個。
並且,由於這是雙向的綁定,所以每次跑完迴圈的結果,都會持續地記著在變數上,因此,要記得加上歸零讓他從新計算。
另外,直接拿idx來用,由於他有非同步的狀況,所以還需要另一個變數來取得當下的index
最後,要呼叫傳遞參數的函數,必須有特別的呼叫方式
最終程式碼
最後,相關的程式碼如下:
@page "/Todos"
@using BlazorTodos.Data;
<h3>Todos</h3>
<input type="text" @bind="newTodoTitle" />
<button @onclick="AddTodo">AddTodo</button>
<hr />
<ul>
@{
//每次呼叫,都歸零重新計算
idx = 0;
}
@foreach (var tTodo in oTodos)
{
//用變數紀錄當次的index
int currentIdx = idx;
<li>
<input type="checkbox" @bind="tTodo.Finish" />
<input type="text" @bind="tTodo.Title" disabled="@tTodo.Finish" />
@if (tTodo.Finish)
{
//onclick如果直接用Del(currentIdx)會有錯誤產生
<button @onclick="@(e=>Del(currentIdx))">X</button>
}
</li>
idx += 1;
}
</ul>
<hr />
@code {
private string newTodoTitle = "abc";
private List<TodoItemInfo> oTodos = new List<TodoItemInfo>();
private int idx = 0;
private void AddTodo()
{
//宣告一個todoItem
TodoItemInfo tTodo = new TodoItemInfo();
//將Input的內容給todoItem的Title,並設定預設的Finish是False
tTodo.Title = newTodoTitle;
tTodo.Finish = false;
//把tTodo加到oTodos裡面去
oTodos.Add(tTodo);
//把input用到的newTodoTitle清為空字串,等待下一個Todo輸入
newTodoTitle = "";
}
private void Del(int currentIdx)
{
oTodos.RemoveAt(currentIdx);
}
}
以上小喵自己記錄筆記,並有需要的網友參考
^_^
補充
上面的這個是沒有套用視覺的範例,以下這邊小喵套用了Bootstrap的相關設定的範例,也筆記一下,同時提供網友們參考
@page "/todos"
<div class="container">
<div class="row">
<div class="col-sm-1"></div>
<div class="col-sm-10">
<div class="card border-primary mb-3">
<div class="card-header">
<h3>Todos</h3>
</div>
<div class="card-body">
<div class="row">
<div class="col-10">
<input type="text" placeholder="請輸入Todo的標題" class="form-control" @bind="newTodoTitle" />
</div>
<div class="col-2">
<button class="btn btn-primary" @onclick="AddTodo">Add Todo</button>
</div>
</div>
<div class="row">
<div class="col-12">
<hr />
</div>
</div>
@{
idx = 0;
}
@foreach (var tTodo in oTodos)
{
int currentIdx = idx;
<div class="row">
<div class="col-1">
<input type="checkbox" class="form-control" @bind="tTodo.Finish" value="" />
</div>
<div class="col-10">
<input type="text" class="form-control @(tTodo.Finish? "border-success":"border-danger")" @bind="tTodo.Title" disabled="@tTodo.Finish" />
</div>
<div class="col-1">
<button class="btn" @onclick="@(e=>Del(currentIdx))" hidden="@(!tTodo.Finish)">X</button>
</div>
</div>
idx += 1;
}
</div>
</div>
</div>
<div class="col-sm-1"></div>
</div>
</div>
@code {
public class TodoItemInfo
{
public string Title { get; set; } = "";
public bool Finish { get; set; } = false;
}
private string newTodoTitle = "";
private List<TodoItemInfo> oTodos = new List<TodoItemInfo>();
private int idx = 0;
private void AddTodo()
{
TodoItemInfo tTodo = new TodoItemInfo();
tTodo.Title = newTodoTitle;
tTodo.Finish = false;
oTodos.Add(tTodo);
newTodoTitle = "";
}
private void Del(int currentIdx)
{
oTodos.RemoveAt(currentIdx);
}
}
另外,小喵在練習的過程中,當遇到一些問題,不知道怎麼撰寫時,可以透過關鍵詞搜尋找一些網路的範例,大部分的文章是英文的,例如想知道click時要傳參數,於是就搜尋【blazor onclick pass parameter】,就找到相關的做法。
目前Blazor在中文的文章偏少一些,建議網友們,如果有需要找一些資訊時,建議用英文找,會比較容易找到解答。
以下是簽名:
- 歡迎轉貼本站的文章,不過請在貼文主旨上加上【轉貼】,並在文章中附上本篇的超連結與站名【topcat姍舞之間的極度凝聚】,感恩大家的配合。
- 小喵大部分的文章會以小喵熟悉的語言VB.NET撰寫,如果您需要C#的Code,也許您可以試著用線上的工具進行轉換,這裡提供幾個參考
Microsoft MVP Visual Studio and Development Technologies (2005~2019/6) | topcat Blog:http://www.dotblogs.com.tw/topcat |