[筆記][Blazor][CRUD]Blazor透過WebAPI進行CRUD範例

這裡筆記一下 Blazor 透過 WebAPI,針對北風資料庫的 Shippers 進行CRUD(新增修改刪除查詢)的範例

緣起

這裡記錄一下 Blazor,透過 WebAPI 存取資料庫(非同步)的範例程式。資料庫採用北風資料庫的 Shippers,提供網友與自己參考。

解決方案、專案準備與說明

建立一個空白解決方案,在裡面新增兩個專案,一個是 WebAPI ,另一個是 Blazor 。資料庫的部分,以北風資料庫的 Shippers,資料的存取透過 Dapper 進行存取。

文章中不會把專案中所有的檔案程式內容都放上,如果有缺漏看不懂的部分,請提出我再補充。

WebAPI : Model

首先是 DTO 用的 ShipperAM (ApiModel):

namespace PNorthWindAPI.Models.ApiModel
{
    public class ShipperAM
    {
        public int ShipperID { get; set; } = 0;
        public string CompanyName { get; set; } = "";
        public string Phone { get; set; } = "";
    }
}

再來是用來傳回結果的 JsonRltInfo

namespace PCATAPI.Models.Infos
{
    public class JsonRltInfo
    {
        public int rltCode { get; set; } = 0;
        public string rltMsg { get; set; } = "";
    }
}

用以傳回錯誤的 ErrMsgInfo

using System;
using System.Net;
using System.Net.Http;

namespace PCATAPI.Models.Infos
{
    public class ErrMsgInfo
    {
        private string m_ErrCode = "";
        private string m_ErrMsg = "";
        private string m_ErrTime = "";
        private string m_ErrJSON = "";

        private Exception m_Ex = null;
        private HttpResponseMessage m_RepMsg = null;

        public Exception ex
        {
            set
            {
                m_Ex = value;
                if (m_Ex != null)
                {
                    m_ErrCode = m_Ex.HResult.ToString();
                    m_ErrMsg = m_Ex.Message;
                    GenErrJSON();
                }
            }
        }

        public HttpResponseMessage RepMsg
        {
            get
            {
                return m_RepMsg;
            }
        }


        private void GenErrJSON()
        {
            //throw new NotImplementedException(); 
            if (m_ErrCode != "" && m_ErrMsg != "")
            {
                m_ErrJSON = "{\"ErrCode\":" + m_ErrCode + ",\"ErrMsg\":" + m_ErrMsg + "}";
                m_RepMsg = new HttpResponseMessage(HttpStatusCode.ExpectationFailed);
                m_RepMsg.Content = new StringContent(m_ErrJSON);
                m_ErrTime = DateTime.Now.ToString("yyyyMMddHHmmss");
                m_RepMsg.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json");
            }
        }
    }
}

連接字串暫時用一個類別存放 ConnStrInfo

namespace PNorthWindAPI.Models.Info
{
    public class ConnStrInfo
    {
        public string ConnStr { get; set; } = "Data Source=.\\SQLExpress;Initial Catalog=NorthwindChinese;Integrated Security=True";
    }
}

接下來是比較重頭戲的 CRUD 的 DAO 啦

using Dapper;
using PNorthWindAPI.Models.ApiModel;
using PNorthWindAPI.Models.Info;
using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Linq;
using System.Transactions;

namespace PNorthWindAPI.Models.DAOs
{
    public class ShipperDao
    {
        public List<ShipperAM> getShipprs() 
        {
            List<ShipperAM> oShippers = new List<ShipperAM>();
            try
            {
                string ConnStr = GetConnStr();
                using(SqlConnection Conn = new SqlConnection(ConnStr))
                {
                    Conn.Open();
                    string SqlTxt = "";
                    SqlTxt += " SELECT * ";
                    SqlTxt += " FROM [dbo].[Shippers] (NOLOCK) ";
                    SqlTxt += "  ";

                    oShippers = Conn.Query<ShipperAM>(SqlTxt).ToList();
                }
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message);
            }
            return oShippers;
        }

        public ShipperAM getShipper(int ShipperID)
        {
            ShipperAM oShipper = new ShipperAM();
            try
            {
                string ConnStr = GetConnStr();
                using(SqlConnection Conn = new SqlConnection(ConnStr))
                {
                    Conn.Open();
                    string SqlTxt = "";
                    SqlTxt += " SELECT * ";
                    SqlTxt += " FROM [dbo].[Shippers] (NOLOCK) ";
                    SqlTxt += " WHERE ShipperID = @ShipperID ";
                    SqlTxt += "  ";

                    oShipper = Conn.Query<ShipperAM>(SqlTxt, new { ShipperID = ShipperID }).FirstOrDefault();
                }
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message);
            }
            return oShipper;
        }

        public string InsertShipper(ShipperAM oShipper)
        {
            string rc = "";
            using (var scope = new TransactionScope())
            {
                try
                {
                    string ConnStr = GetConnStr();
                    using (SqlConnection Conn = new SqlConnection(ConnStr))
                    {
                        Conn.Open();
                        string SqlTxt = "";
                        SqlTxt += " INSERT INTO [dbo].[Shippers] ";
                        SqlTxt += " 	(CompanyName, Phone) ";
                        SqlTxt += " VALUES (@CompanyName, @Phone) ";
                        SqlTxt += "  ";
                        Conn.Execute(SqlTxt, oShipper);
                        rc = "Success";
                    }
                    scope.Complete();
                }
                catch (Exception ex)
                {
                    throw new Exception(ex.Message);
                }
            }
            return rc;
        }

        public string ChgShipper(ShipperAM oShipper)
        {
            string rc = "";
            using (var scope = new TransactionScope())
            {
                try
                {
                    string ConnStr = GetConnStr();
                    using (SqlConnection Conn = new SqlConnection(ConnStr))
                    {
                        Conn.Open();
                        string SqlTxt = "";
                        SqlTxt += " UPDATE [dbo].[Shippers] ";
                        SqlTxt += " SET CompanyName = @CompanyName ";
                        SqlTxt += " 	, Phone = @Phone ";
                        SqlTxt += " WHERE ShipperID = @ShipperID ";
                        SqlTxt += "  ";
                        Conn.Execute(SqlTxt, oShipper);
                        rc = "Success";
                    }
                    scope.Complete();
                }
                catch (Exception ex)
                {
                    throw new Exception(ex.Message);
                }
            }
            return rc;
        }

        public string DelShipper(int ShipperID)
        {
            string rc = "";
            using (var scope = new TransactionScope())
            {
                try
                {
                    string ConnStr = GetConnStr();
                    using (SqlConnection Conn = new SqlConnection(ConnStr))
                    {
                        Conn.Open();
                        string SqlTxt = "";
                        SqlTxt += " DELETE Shippers ";
                        SqlTxt += " WHERE ShipperID = @ShipperID ";
                        SqlTxt += "  ";
                        Conn.Execute(SqlTxt, new { ShipperID = ShipperID });
                        rc = "Success";
                    }
                    scope.Complete();
                }
                catch (Exception ex)
                {
                    throw new Exception(ex.Message);
                }
            }
            return rc;
        }

        private string GetConnStr()
        {
            string ConnStr = "";
            ConnStrInfo oConnStr = new ConnStrInfo();
            ConnStr = oConnStr.ConnStr;
            return ConnStr;
        }
    }
}

 

WebAPI : Controller

直接來看程式碼:

using PCATAPI.Models.Infos;
using PNorthWindAPI.Models.ApiModel;
using PNorthWindAPI.Models.DAOs;
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Web.Http;

namespace PNorthWindAPI.Controllers.WebAPI
{
    public class ShipperController : ApiController
    {
        [HttpGet]
        [Route("api/Shipper")]
        public List<ShipperAM> getShippers()
        {
            List<ShipperAM> oShippers = new List<ShipperAM>();
            ShipperDao dao = new ShipperDao();
            try
            {
                oShippers = dao.getShipprs();
            }
            catch (Exception ex)
            {
                ErrMsgInfo oErr = new ErrMsgInfo();
                oErr.ex = ex;
                HttpResponseMessage RepMsg = oErr.RepMsg;
                throw new HttpResponseException(RepMsg);
            }
            return oShippers;
        }

        [HttpGet]
        [Route("api/Shipper/{ShipperID}")]
        public ShipperAM getShipper(int ShipperID = 0)
        {
            ShipperAM oShipper = new ShipperAM();
            ShipperDao dao = new ShipperDao();
            try
            {
                oShipper = dao.getShipper(ShipperID);
            }
            catch (Exception ex)
            {
                ErrMsgInfo oErr = new ErrMsgInfo();
                oErr.ex = ex;
                HttpResponseMessage RepMsg = oErr.RepMsg;
                throw new HttpResponseException(RepMsg);
            }
            return oShipper;
        }

        [HttpPost]
        [Route("api/Shipper/Insert")]
        public JsonRltInfo InsertItem([FromBody]ShipperAM oShipper)
        {
            JsonRltInfo oRlt = new JsonRltInfo();
            ShipperDao dao = new ShipperDao();
            try
            {
                string rc = dao.InsertShipper(oShipper);
                if (rc == "Success")
                {
                    oRlt.rltCode = 0;
                    oRlt.rltMsg = "新增成功";
                }
            }
            catch (Exception ex)
            {
                oRlt.rltCode = 417;
                oRlt.rltMsg = ex.Message;
            }
            return oRlt;
        }

        [HttpPost]
        [Route("api/Shipper/Update")]
        public JsonRltInfo UpdateItem([FromBody]ShipperAM oShipper)
        {
            JsonRltInfo oRlt = new JsonRltInfo();
            ShipperDao dao = new ShipperDao();
            try
            {
                string rc = dao.ChgShipper(oShipper);
                if (rc == "Success")
                {
                    oRlt.rltCode = 0;
                    oRlt.rltMsg = "更新成功";
                }
            }
            catch (Exception ex)
            {
                oRlt.rltCode = 417;
                oRlt.rltMsg = ex.Message;
            }
            return oRlt;
        }

        [HttpGet]
        [Route("api/Shipper/Del/{ShipperID}")]
        public JsonRltInfo DelItem(int ShipperID)
        {
            JsonRltInfo oRlt = new JsonRltInfo();
            ShipperDao dao = new ShipperDao();
            try
            {
                string rc = dao.DelShipper(ShipperID);
                if (rc == "Success")
                {
                    oRlt.rltCode = 0;
                    oRlt.rltMsg = "刪除成功";
                }
            }
            catch (Exception ex)
            {
                oRlt.rltCode = 417;
                oRlt.rltMsg = ex.Message;
            }
            return oRlt;
        }

    }
}

以上就是 WebAPI 相關程式碼的部分。

 

Blazor

接下來就要看看Blazor的部分。一樣的,我這邊不會把所有的Blazor的每一隻程式碼都放上來,只放我覺得必要的。一樣,如果網友們覺得看不懂,需要我補充,請留言通知一下。

Blazor : Data

首先是 ShipperVM 的類別。

namespace BlazorCRUDLifeCycle.Data
{
    public class ShipperVM
    {
        public int ShipperID { get; set; } = 0;
        public string CompanyName { get; set; } = "";
        public string Phone { get; set; } = "";

    }
}

WebAPI Domain 的名稱,就存在一個類別 UriWebAPI 中

namespace PCAT_Blazor.Data
{
    public class UriWebAPI
    {
        private readonly string _uriServer = "http://localhost:55056";

        public string UriServer => _uriServer;
    }
}

用來接收 WebAPI 回傳回來的內容

namespace PCAT_Blazor.Data
{
    public class JsonRltInfo
    {
        public int rltCode { get; set; } = 0;
        public string rltMsg { get; set; } = "";
    }
}

用來處理 Bootstrap Modal 顯示訊息的類別

namespace BlazorCRUDLifeCycle.Data
{
    public class MsgInfo
    {
        public string Msg { get; set; } = "";

        public string Title { get; set; } = "";

        public bool showMsg { get; set; } = false;
    }
}

 

Blazor : DAOs

這裡把透過 HttpClient 呼叫 WebAPI 進行 CRUD 的部分集中在這個類別中

using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using BlazorCRUDLifeCycle.Data;
using PCAT_Blazor.Data;

namespace BlazorCRUDLifeCycle.DAOs
{

    public class ShipperDao
    {
        private string urlServer = "";

        public ShipperDao()
        {
            UriWebAPI oUriWebAPI = new UriWebAPI();
            urlServer = oUriWebAPI.UriServer;
        }

        #region 查詢

        public async Task<List<ShipperVM>> GetShippersAsync()
        {
            List<ShipperVM> oShippers = new List<ShipperVM>();
            try
            {
                HttpClient client = new HttpClient();
                string sUrl = urlServer + "/api/Shipper";

                HttpResponseMessage response = await client.GetAsync(sUrl);
                string jData = await response.Content.ReadAsStringAsync();
                oShippers = JsonSerializer.Deserialize<List<ShipperVM>>(jData);
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message);
            }
            return oShippers;
        }

        public async Task<ShipperVM> GetShipperAsync(int ShipperID)
        {
            ShipperVM oShipper = new ShipperVM();
            try
            {
                HttpClient client = new HttpClient();
                string sUrl = urlServer + "/api/Shipper/" + ShipperID.ToString(); ;

                HttpResponseMessage response = await client.GetAsync(sUrl);
                string jData = await response.Content.ReadAsStringAsync();
                oShipper = JsonSerializer.Deserialize<ShipperVM>(jData);

            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message);
            }
            return oShipper;
        }

        #endregion


        #region 維護

        public async Task<string> InsertItemAsync(ShipperVM oShipper)
        {
            string rc = "";
            try
            {
                string sUrl = urlServer + "/api/Shipper/Insert";
                string jsonData = JsonSerializer.Serialize<ShipperVM>(oShipper);

                HttpClient http = new HttpClient();
                HttpContent content = new StringContent(jsonData, Encoding.UTF8, "application/json");
                HttpResponseMessage response = await http.PostAsync(sUrl, content);
                if (response.IsSuccessStatusCode)
                {
                    string jRlt = await response.Content.ReadAsStringAsync();
                    JsonRltInfo oRlt = JsonSerializer.Deserialize<JsonRltInfo>(jRlt);
                    if (oRlt.rltCode == 0)
                    {
                        rc = oRlt.rltMsg;
                    }
                    else
                    {
                        throw new Exception(oRlt.rltMsg);
                    }
                }
            }
            catch (Exception ex)
            {

                throw new Exception(ex.Message);
            }
            return rc;
        }

        public async Task<string> UpdateItemAsync(ShipperVM oShipper)
        {
            string rc = "";
            try
            {
                string sUrl = urlServer + "/api/Shipper/Update";
                string jsonData = JsonSerializer.Serialize<ShipperVM>(oShipper);

                HttpClient http = new HttpClient();
                HttpContent content = new StringContent(jsonData, Encoding.UTF8, "application/json");
                HttpResponseMessage response = await http.PostAsync(sUrl, content);
                if (response.IsSuccessStatusCode)
                {
                    string jRlt = await response.Content.ReadAsStringAsync();
                    JsonRltInfo oRlt = JsonSerializer.Deserialize<JsonRltInfo>(jRlt);
                    if (oRlt.rltCode == 0)
                    {
                        rc = oRlt.rltMsg;
                    }
                    else
                    {
                        throw new Exception(oRlt.rltMsg);
                    }
                }

            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message);
            }
            return rc;  
        }

        public async Task<string> DelItemAsync(int ShipperID)
        {
            string rc = "";
            try
            {
                HttpClient client = new HttpClient();
                string sUrl = urlServer + "/api/Shipper/Del/" + ShipperID.ToString();

                HttpResponseMessage response = await client.GetAsync(sUrl);
                string jData = await response.Content.ReadAsStringAsync();
                JsonRltInfo oRlt = JsonSerializer.Deserialize<JsonRltInfo>(jData);
                if (oRlt.rltCode == 0)
                {
                    rc = oRlt.rltMsg;
                }
                else
                {
                    throw new Exception(oRlt.rltMsg);
                }
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message);
            }
            return rc;
        }
        #endregion

    }
}

在 Startup.cs 將DAO宣告成可相依注入的服務

接著開啟 Startup.cs ,在 ConfigureServices 裡面將類別家入為服務。加入【services.AddSingleton<ShipperDao>();】如下:

public void ConfigureServices(IServiceCollection services)
{
	services.AddRazorPages();
	services.AddServerSideBlazor();
	services.AddSingleton<WeatherForecastService>();
	services.AddSingleton<ShipperDao>();
}

 

Blazor : Component

先新增一個用 Bootstrap Modal 來顯示訊息的 Component 。

【ShowMsg.razor】:

@if (showMsg)
{
    <!-- Modal -->
    <div class="modal fade show" id="exampleModal" tabindex="-1" style="display:block" role="dialog" aria-modal="true" aria-labelledby="exampleModalLabel">
        <div class="modal-dialog" role="document">
            <div class="modal-content">
                <div class="modal-header">
                    <h5 class="modal-title" id="exampleModalLabel">@Title</h5>
                    <button type="button" class="close" aria-label="Close" @onclick="CloseMsg">
                        <span aria-hidden="true">&times;</span>
                    </button>
                </div>
                <div class="modal-body">
                    @((MarkupString)Msg)
                </div>
                <div class="modal-footer">
                    <button type="button" class="btn btn-outline-primary" @onclick="CloseMsg">關閉</button>

                </div>
            </div>
        </div>
    </div>
    <!--End Modal-->
}


@code {
    [Parameter]
    public bool showMsg { get; set; } = false;
    [Parameter]
    public string Title { get; set; } = "";
    [Parameter]
    public string Msg { get; set; } = "";

    public override async Task SetParametersAsync(ParameterView parameters)
    {
        await base.SetParametersAsync(parameters);
    }


    private void CloseMsg()
    {
        showMsg = false;
    }
}

接著是重頭戲了,Shipper.razor 如下:

@page "/Shipper"
@using BlazorCRUDLifeCycle.Data
@using BlazorCRUDLifeCycle.DAOs
@inject ShipperDao ShipperService
@inject IJSRuntime ijJS
@inject NavigationManager ijNavigationManager

<p>@NowTime</p>
@if (showList)
{
    <div class="row" id="divList">
        <div class="col-12">
            <button class="btn btn-outline-primary" @onclick="btnAddNew">新增</button>
            <table class="table table-bordered table-hover table-striped">
                <thead>
                    <tr class="bg-primary text-white">
                        <th>功能</th>
                        <th>ShipperID</th>
                        <th>ComponyName</th>
                        <th>Phone</th>
                    </tr>
                </thead>
                <tbody>
                    @foreach (var tShipper in oShippers)
                    {
                        <tr>
                            <td>
                                <button class="btn btn-outline-primary" @onclick="(e=>editItm(tShipper))">編</button>
                            </td>
                            <td>@tShipper.ShipperID</td>
                            <td>@tShipper.CompanyName</td>
                            <td>@tShipper.Phone</td>
                        </tr>
                    }
                </tbody>
            </table>
        </div>
    </div>
}


@if (showEdit)
{
    <div class="row" id="divEdit">
        <div class="col-sm-1"></div>
        <div class="col-sm-10">
            <div class="card border-primary mb-3">
                <div class="card-header">
                    <h3>Shipper維護</h3>
                </div>
                <div class="card-body">
                    <fieldset>
                        <div class="form-group row">
                            <label for="staticEmail" class="col-sm-2 col-form-label">ShipperID</label>
                            <div class="col-sm-10">
                                <input type="text" readonly class="form-control" id="txtShipperID" @bind="oShipper.ShipperID">
                            </div>
                        </div>
                        <div class="form-group row">
                            <label for="txtCompanyName" class="col-sm-2 col-form-label">CompanyName</label>
                            <div class="col-sm-10">
                                <input type="text" class="form-control" id="txtCompanyName" @bind="oShipper.CompanyName">
                            </div>
                        </div>
                        <div class="form-group row">
                            <label for="txtPhone" class="col-sm-2 col-form-label">Phone</label>
                            <div class="col-sm-10">
                                <input type="text" class="form-control" id="txtPhone" @bind="oShipper.Phone">
                            </div>
                        </div>
                    </fieldset>
                </div>
                <div class="card-footer">
                    @if (showBtnInsert)
                    {
                        <button class="btn btn-outline-primary" @onclick="InsertItem">新增</button>
                    }
                    @if (showBtnUpdate)
                    {
                        <button class="btn btn-outline-primary" @onclick="UpdateItem">修改</button>
                    }
                    @if (showBtnDel)
                    {
                        <button class="btn btn-outline-secondary" @onclick="DelItem">刪除</button>
                    }
                    <button class="btn btn-outline-secondary" @onclick="btnCancelClick">取消</button>
                </div>
            </div>
        </div>
        <div class="col-sm-1"></div>
    </div>
}

<ShowMsg Title="@oMsg.Title" Msg="@oMsg.Msg" showMsg="@oMsg.showMsg"></ShowMsg>

@code {

    #region 變數設定
    private List<ShipperVM> oShippers = new List<ShipperVM>();
    private ShipperVM oShipper = new ShipperVM();
    private MsgInfo oMsg = new MsgInfo();
    private string NowTime = "";
    private bool showList = true;
    private bool showEdit = false;
    private bool showBtnInsert = false;
    private bool showBtnUpdate = false;
    private bool showBtnDel = false;
    private string ErrMsg = "";

    #endregion

    #region 初始化
    protected async override Task OnInitializedAsync()
    {
        oShippers = await ShipperService.GetShippersAsync();

        await base.OnInitializedAsync();
    }
    private void EditInit()
    {
        oShipper = new ShipperVM();
        showBtnInsert = false;
        showBtnUpdate = false;
        showBtnDel = false;
    }

    #endregion

    #region 檢查輸入
    private bool ChkInput()
    {
        bool rc = true;
        ErrMsg = "";
        if(oShipper.CompanyName == "")
        {
            ErrMsg += "請輸入CompanyName<br>";
            rc = false;
        }
        if(oShipper.Phone == "")
        {
            ErrMsg += "請輸入Phone<br>";
            rc = false;
        }
        return rc;
    }

    #endregion

    #region 開啟編輯區
    private void btnAddNew()
    {
        showList = false;
        showEdit = true;
        EditInit();
        showBtnInsert = true;
    }

    private void editItm(ShipperVM tShipper)
    {
        showList = false;
        EditInit();
        oShipper = tShipper;
        showEdit = true;
        showBtnUpdate = true;
        showBtnDel = true;
    }

    #endregion

    #region 編輯區按鈕

    /// <summary>
    /// 新增
    /// </summary>
    public async void InsertItem()
    {
        if (ChkInput())
        {
            string rc = await ShipperService.InsertItemAsync(oShipper);
            if (rc != "")
            {
                EditInit();
                showEdit = false;
                showList = true;

                oShippers = await ShipperService.GetShippersAsync();

                oMsg.Title = "訊息:";
                oMsg.Msg = rc;
                oMsg.showMsg = true;
                StateHasChanged();
            }
            else
            {
                oMsg.Title = "訊息:";
                oMsg.Msg = "新增失敗";
                oMsg.showMsg = true;
                StateHasChanged();
            }
        }
        else
        {
            oMsg.Title = "訊息:";
            oMsg.Msg = ErrMsg;
            oMsg.showMsg = true;
            StateHasChanged();
        }
    }

    /// <summary>
    /// 修改
    /// </summary>
    public async void UpdateItem()
    {
        if (ChkInput())
        {
            string rc = await ShipperService.UpdateItemAsync(oShipper);
            if (rc != "")
            {
                EditInit();
                showEdit = false;
                showList = true;

                oShippers = await ShipperService.GetShippersAsync();

                oMsg.Title = "訊息:";
                oMsg.Msg = rc;
                oMsg.showMsg = true;
                StateHasChanged();
            }
            else
            {
                oMsg.Title = "訊息:";
                oMsg.Msg = "修改失敗";
                oMsg.showMsg = true;
                StateHasChanged();
            }
        }
        else
        {
            oMsg.Title = "訊息:";
            oMsg.Msg = ErrMsg;
            oMsg.showMsg = true;
            StateHasChanged();
        }
    }

    /// <summary>
    /// 刪除
    /// </summary>
    public async void DelItem()
    {
        bool confirmed = await ijJS.InvokeAsync<bool>("confirm", "您確定要刪除此資料嗎?");
        if (confirmed)
        {

            string rc = await ShipperService.DelItemAsync(oShipper.ShipperID);
            if (rc != "")
            {
                EditInit();
                showEdit = false;
                showList = true;

                oShippers = await ShipperService.GetShippersAsync();

                oMsg.Title = "訊息:";
                oMsg.Msg = rc;
                oMsg.showMsg = true;
                StateHasChanged();
            }
        }
        else
        {
            oMsg.Title = "訊息:";
            oMsg.Msg = ErrMsg;
            oMsg.showMsg = true;
            StateHasChanged();
        }
    }

    /// <summary>
    /// 取消
    /// </summary>
    private async void btnCancelClick()
    {
        bool confirmed = await ijJS.InvokeAsync<bool>("confirm", "您確定要放棄維護嗎?");
        if (confirmed)
        {
            EditInit();
            showEdit = false;
            showList = true;
            oShippers = await ShipperService.GetShippersAsync();
            StateHasChanged();
        }
    }

    #endregion

}

這裡提醒一下,由於 WebAPI 透過非同步的方式處理,從 Blazor的生命週期中 ,畫面的更新會有一般的更新與非同步兩個部分。小喵在測試的時候,非同步後的畫面異動相關,都會慢一步,後來查文件得知,資料非同步取得後,要通知Blazor進行狀態修改後的處理,記得要呼叫【StateHasChanged();】來啟動 Server 端通知畫面的處理與更新。

問題

在完成這一篇後,小喵在測試的過程中,發現了一個詭異的問題。
當小喵按下新增,資料都不輸入的情況下,按下新增資料,那麼程式在 ChkInput 會檢查輸入,傳回錯誤資料沒有輸入,然後啟動對話視窗,顯示錯誤訊息。一切看似美好~

但~

一樣都不輸入,再次按下新增,理論上會再次顯示對話視窗,再次提示錯誤訊息。然而卻一點動靜都沒有!!即使是逐步的執行,該給的資料都有,也都oMsg.showMsg=true;
一樣接下來沒有動靜。

這個問題與解決方式,就留下一篇來說明。
[筆記][Blazor]使用對話視窗Component無法動作與解決方式

末記

其實整個過程與 Vue 的開發類似,唯一不一樣的是非同步存取會影響畫面雙向綁定的即時性,記得要透過【StateHasChanged();】來通知 Blazor 通知 Server 端狀態已經改變,該重新處理畫面了。

相關的程式碼,小喵放到 GitHub 中,相關網址如下:
topcattw/Blazor-CRUD-with-WebAPI-Sample: Blazor CRUD with WebAPI Sample (github.com)


以下是簽名:


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