[Blazor][筆記][CheckBox]撰寫多Checkbox,勾選即時維護功能

小喵試著撰寫多個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++)
        {
            @:&nbsp;&nbsp;
        }
        @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++)
            {
                @:&nbsp;&nbsp;
            }
            @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;】然後才去維護資料。
如此就能如小喵預期的,修改並且即時的維護資料。


以下是簽名:


Microsoft MVP
Visual Studio and Development Technologies
(2005~2019/6) 
topcat
Blog:http://www.dotblogs.com.tw/topcat