小喵試著撰寫多個CheckBox,勾選立即儲存的功能,原本以為透過@bind、@onclick就可以搞定,但是實際撰寫下來,似乎又不是小喵原本所想的那樣,至於如何能夠實現,我們繼續看下去~
緣起
小喵撰寫的畫面授權,他的畫面大約是長得像以下這樣
以往寫MVC, WebForm,是透過遞迴的方式來產生這棵樹,並且整批設定完後,整批的維護資料庫。不過現在改用 Component 的方式撰寫每一個Node,要整批維護反而是一個麻煩,不如就試著改成勾選立即維護的方式。
bind與click的問題
一開始,小喵把每個 Checkbox 透過【@bind】的方式綁定,透過【@onclick】的事件進行撰寫,但沒想到竟然與我的期望不同。
以往在撰寫WebForm的時候,一個事件通常會有ing與ed的事件,例如資料綁定,就會有OnDataBinding與OnDataBound事件,分別代表著綁定前與綁定後。
小喵撰寫@bind的部分大約如預期,但是在撰寫@onclick的時候,卻出現不如預期的情況,透過Step By Step的方式來觀察,他大約是類似以前的ing【事件執行前(或當下)】,而非事件執行【後】。可是@onclick卻又沒有類似WebForm時代的ing與ed的事件區分。這讓小喵覺得苦惱
透過checked屬性,搭配【@onchange】解決問題
小喵找了一下關鍵字,參考了以下這一篇
In Blazor, How to @bind and then fire @onchange in a dynamic model - Stack Overflow
透過onchange來處理類似ing的時候做法,並透過input的checked屬性,來處理UI點擊後,勾選或取消的動作。相關的作法,小喵提供相關參考程式碼如下:
Html與開頭宣告的部分如下:
@using PCAT_Blazor.DAOs
@using PCAT_Blazor.Data
@inject MenuDao iMenuService
@inject IJSRuntime iJS
@if (blHasSubMenu)
{
<tr>
<td>
<i class="fas fa-folder-open"></i>
@for (var i = 1; i < iLevel; i++)
{
@:
}
@oMenuP.sText (@oMenuP.NodeId)
<i class="fa fa-@oMenuP.iFontAwesome"></i>
</td>
<td><input type="checkbox" checked="@oMenuP.DenyUse" @onchange="DenyUseChanged" /></td>
<td><input type="checkbox" checked="@oMenuP.Brw" disabled="@DisabledBrw" @onchange="BrwChanged" /></td>
<td><input type="checkbox" checked="@oMenuP.Query" disabled="@DisabledQuery" @onchange="QueryChanged" /></td>
<td><input type="checkbox" checked="@oMenuP.AddNew" disabled="@DisabledAddNew" @onchange="AddNewChanged" /></td>
<td><input type="checkbox" checked="@oMenuP.Edit" disabled="@DisabledEdit" @onchange="EditChanged" /></td>
<td><input type="checkbox" checked="@oMenuP.Del" disabled="@DisabledDel" @onchange="DelChanged" /></td>
<td><input type="checkbox" checked="@oMenuP.Printing" disabled="@DisabledPriting" @onchange="PrintingChanged" /></td>
<td><input type="checkbox" checked="@oMenuP.Download" disabled="@DisabledDownload" @onchange="DownloadChanged" /></td>
</tr>
foreach (var tSMP in oSubMenuPs)
{
<MenuPNode iLevel="@NextLevel" RUCode="@RUCode" RUId="@RUId" iNodeId="@tSMP.NodeId"></MenuPNode>
}
}
else
{
<tr>
<td>
<i class="fas fa-folder-open"></i>
@for (var i = 1; i < iLevel; i++)
{
@:
}
@oMenuP.sText (@oMenuP.NodeId)
<i class="fa fa-@oMenuP.iFontAwesome"></i>
</td>
<td><input type="checkbox" checked="@oMenuP.DenyUse" @onchange="DenyUseChanged" /></td>
<td><input type="checkbox" checked="@oMenuP.Brw" disabled="@DisabledBrw" @onchange="BrwChanged" /></td>
<td><input type="checkbox" checked="@oMenuP.Query" disabled="@DisabledQuery" @onchange="QueryChanged" /></td>
<td><input type="checkbox" checked="@oMenuP.AddNew" disabled="@DisabledAddNew" @onchange="AddNewChanged" /></td>
<td><input type="checkbox" checked="@oMenuP.Edit" disabled="@DisabledEdit" @onchange="EditChanged" /></td>
<td><input type="checkbox" checked="@oMenuP.Del" disabled="@DisabledDel" @onchange="DelChanged" /></td>
<td><input type="checkbox" checked="@oMenuP.Printing" disabled="@DisabledPriting" @onchange="PrintingChanged" /></td>
<td><input type="checkbox" checked="@oMenuP.Download" disabled="@DisabledDownload" @onchange="DownloadChanged" /></td>
</tr>
}
C#程式碼的部分如下:
@code {
#region 參數
//private string _RUId = "";
/// <summary>
/// 節點代號
/// </summary>
[Parameter]
public int iNodeId { get; set; } = 0;
/// <summary>
/// 第幾層
/// </summary>
[Parameter]
public int iLevel { get; set; } = 0;
/// <summary>
/// 角色/使用者區分
/// </summary>
[Parameter]
public string RUCode { get; set; } = "R";
/// <summary>
/// (角色/使用者)代號
/// </summary>
[Parameter]
public string RUId { get; set; } = "";
[Parameter]
public string DataTime { get; set; } = "";
#endregion
#region 變數
/// <summary>
/// 是否有子節點
/// </summary>
private bool blHasSubMenu = false;
/// <summary>
/// 該節點權限物件
/// </summary>
private MenuPVM oMenuP = new MenuPVM();
/// <summary>
/// 子節點權限物件集合
/// </summary>
private List<MenuPVM> oSubMenuPs = new List<MenuPVM>();
private int NextLevel;
private bool DisabledBrw = false;
private bool DisabledQuery = true;
private bool DisabledAddNew = true;
private bool DisabledEdit = true;
private bool DisabledDel = true;
private bool DisabledPriting = true;
private bool DisabledDownload = true;
#endregion
#region 初始化
[CascadingParameter] protected Task<AuthenticationState> AuthStat { get; set; }
protected async override Task OnInitializedAsync()
{
if (RUId != "")
{
//getDataByRUId();
oMenuP = await iMenuService.getMenuP(RUId, iNodeId, RUCode);
blHasSubMenu = await iMenuService.chkHasSubMenuPByNodeIdRUId(iNodeId, RUId);
if (blHasSubMenu)
{
oSubMenuPs = await iMenuService.getSubMenuPsByParentIdRUId(iNodeId, RUId, RUCode);
}
if (oMenuP.Brw == true)
{
DisabledQuery = false;
DisabledAddNew = false;
DisabledEdit = false;
DisabledDel = false;
DisabledPriting = false;
DisabledDownload = false;
}
if (oMenuP.DenyUse == true)
{
oMenuP.Brw = false;
oMenuP.Query = false;
oMenuP.AddNew = false;
oMenuP.Edit = false;
oMenuP.Del = false;
oMenuP.Printing = false;
oMenuP.Download = false;
DisabledBrw = true;
DisabledQuery = true;
DisabledAddNew = true;
DisabledEdit = true;
DisabledDel = true;
DisabledPriting = true;
DisabledDownload = true;
}
}
NextLevel = iLevel + 1;
await base.OnInitializedAsync();
}
#endregion
#region 動作
private async void DenyUseChanged()
{
try
{
oMenuP.DenyUse = !oMenuP.DenyUse;
if (oMenuP.DenyUse == true)
{
oMenuP.Brw = false;
oMenuP.Query = false;
oMenuP.AddNew = false;
oMenuP.Edit = false;
oMenuP.Del = false;
oMenuP.Printing = false;
oMenuP.Download = false;
DisabledBrw = true;
DisabledQuery = true;
DisabledAddNew = true;
DisabledEdit = true;
DisabledDel = true;
DisabledPriting = true;
DisabledDownload = true;
}
else
{
DisabledBrw = false;
}
SaveMenuP();
}
catch (Exception ex)
{
await iJS.InvokeVoidAsync("alert", ex.Message);
}
StateHasChanged();
}
private async void BrwChanged()
{
try
{
oMenuP.Brw = !oMenuP.Brw;
CheckDisabledChg();
SaveMenuP();
}
catch (Exception ex)
{
await iJS.InvokeVoidAsync("alert", ex.Message);
}
StateHasChanged();
}
private async void QueryChanged()
{
try
{
oMenuP.Query = !oMenuP.Query;
SaveMenuP();
}
catch (Exception ex)
{
await iJS.InvokeVoidAsync("alert", ex.Message);
}
}
private async void AddNewChanged()
{
try
{
oMenuP.AddNew = !oMenuP.AddNew;
SaveMenuP();
}
catch (Exception ex)
{
await iJS.InvokeVoidAsync("alert", ex.Message);
}
}
private async void EditChanged()
{
try
{
oMenuP.Edit = !oMenuP.Edit;
SaveMenuP();
}
catch (Exception ex)
{
await iJS.InvokeVoidAsync("alert", ex.Message);
}
}
private async void DelChanged()
{
try
{
oMenuP.Del = !oMenuP.Del;
SaveMenuP();
}
catch (Exception ex)
{
await iJS.InvokeVoidAsync("alert", ex.Message);
}
}
private async void PrintingChanged()
{
try
{
oMenuP.Printing = !oMenuP.Printing;
SaveMenuP();
}
catch (Exception ex)
{
await iJS.InvokeVoidAsync("alert", ex.Message);
}
}
private async void DownloadChanged()
{
try
{
oMenuP.Download = !oMenuP.Download;
SaveMenuP();
}
catch (Exception ex)
{
await iJS.InvokeVoidAsync("alert", ex.Message);
}
}
private void CheckDisabledChg()
{
if (oMenuP.Brw)
{
DisabledQuery = false;
DisabledAddNew = false;
DisabledEdit = false;
DisabledDel = false;
DisabledPriting = false;
DisabledDownload = false;
}
else
{
DisabledQuery = true;
DisabledAddNew = true;
DisabledEdit = true;
DisabledDel = true;
DisabledPriting = true;
DisabledDownload = true;
oMenuP.Query = false;
oMenuP.AddNew = false;
oMenuP.Edit = false;
oMenuP.Del = false;
oMenuP.Printing = false;
oMenuP.Download = false;
}
StateHasChanged();
}
private async void SaveMenuP()
{
try
{
string rc = await iMenuService.chgSingleMenuPByRUIdAsync(oMenuP);
}
catch (Exception ex)
{
await iJS.InvokeVoidAsync("alert", ex.Message);
}
}
#endregion
}
可以把onchange當作是變更【前(ing)】的狀態
此時小喵用程式碼改變物件的值【oMenuP.Brw = !oMenuP.Brw;】然後才去維護資料。
如此就能如小喵預期的,修改並且即時的維護資料。
以下是簽名:
- 歡迎轉貼本站的文章,不過請在貼文主旨上加上【轉貼】,並在文章中附上本篇的超連結與站名【topcat姍舞之間的極度凝聚】,感恩大家的配合。
- 小喵大部分的文章會以小喵熟悉的語言VB.NET撰寫,如果您需要C#的Code,也許您可以試著用線上的工具進行轉換,這裡提供幾個參考
Microsoft MVP Visual Studio and Development Technologies (2005~2019/6) | topcat Blog:http://www.dotblogs.com.tw/topcat |