Blazor University (35)表單 —— 編寫自定義驗證

原文鏈接:https://blazor-university.com/forms/writing-custom-validation/

編寫自定義驗證

源代碼[1]

請注意,與有關 EditContext、FieldIdentifiers 和 FieldState[2] 的部分一樣,這是一個高級主題。

如前所述,FieldState 類保存表單數據的元狀態。除了指示值是否已被手動編輯外,Blazor 還存儲驗證錯誤消息的集合。為了了解它的工作原理,本節將說明如何創建我們自己的自定義驗證機制,該機制可與 Blazor 一起使用來驗證用戶輸入。

下面的 UML 圖顯示了 EditForm 和存儲此元狀態的各種類(在圖中分組)之間的關系。請記住,每當 EditForm.Model 發生更改時,EditForm 都會創建一個新的 EditContext 實例。然后可以對以前的 EditContext(不再需要它,因為它包含有關以前模型的信息)進行垃圾收集,并且可以對圖表中分組的所有類實例進行垃圾收集。

f84a655063986b45cb2cc8984537f5b1.png

我們的自定義驗證將基于 FluentValidation[3]。完成本節后(或者如果您只是想要一些可以立即使用的東西),請查看 blazor-validation[4]

創建驗證器組件

我們的驗證器組件不必為了提供驗證而從任何特定類繼承。唯一的要求是它來自 Blazor ComponentBase 類,以便我們可以將其添加到視圖中的 <EditForm> 標記中。嵌入 <EditForm> 標記的目的是為了讓我們可以定義一個級聯參數[5],以便在 EditFormModel 參數更改時獲取當前由 EditForm 創建的 EditContext

首先,創建一個新的 Blazor 應用并添加對 FluentValidation NuGet 包[6]的引用。然后創建一個名為 FluentValidationValidator 的類。

public?class?FluentValidationValidator?:?ComponentBase
{[CascadingParameter]private?EditContext?EditContext?{?get;?set;?}[Parameter]public?Type?ValidatorType?{?get;?set;?}private?IValidator?Validator;private?ValidationMessageStore?ValidationMessageStore;[Inject]private?IServiceProvider?ServiceProvider?{?get;?set;?}
}
  • EditContext

    從其父 <EditForm> 組件傳遞給我們的組件的級聯參數。每次 EditForm.Model 更改時,這都會更改。

  • ValidatorType

    這將指定用于執行實際驗證的類類型。我們將檢查這是一個 IValidator(一個 FluentValidation 接口)。

  • Validator

    這將保存對指定 ValidatorType 實例的引用,以執行實際的對象驗證。

  • ValidationMessageStore

    每次我們的 EditContext 更改(因為 EditForm.Model 已更改)時,我們都會創建一個新的。

  • ServiceProvider

    IServiceProvider 的注入依賴項,我們可以使用它來創建 ValidatorType 的實例。

public?override?async?Task?SetParametersAsync(ParameterView?parameters)
{//?Keep?a?reference?to?the?original?values?so?we?can?check?if?they?have?changedEditContext?previousEditContext?=?EditContext;Type?previousValidatorType?=?ValidatorType;await?base.SetParametersAsync(parameters);if?(EditContext?==?null)throw?new?NullReferenceException($"{nameof(FluentValidationValidator)}?must?be?placed?within?an?{nameof(EditForm)}");if?(ValidatorType?==?null)throw?new?NullReferenceException($"{nameof(ValidatorType)}?must?be?specified.");if?(!typeof(IValidator).IsAssignableFrom(ValidatorType))throw?new?ArgumentException($"{ValidatorType.Name}?must?implement?{typeof(IValidator).FullName}");if?(ValidatorType?!=?previousValidatorType)ValidatorTypeChanged();//?If?the?EditForm.Model?changes?then?we?get?a?new?EditContext//?and?need?to?hook?it?upif?(EditContext?!=?previousEditContext)EditContextChanged();
}
  • 第 4-5 行

    每當我們的參數之一更改(包括我們的 EditContext 級聯參數)時,都會執行 SetParametersAsync。我們需要做的第一件事是保留對一些原始值的引用,以便我們可以查看它們是否已更改并做出相應的反應。

  • 第 7 行

    調用 base.SetParametersAsync 會將我們對象的屬性更新為新值。

  • 第 9-16 行

    確保我們有一個 EditContext 和一個作為 IValidatorValidatorType

  • 第 18-19 行

    如果 ValidatorType 已更改,那么我們需要創建該類型的新實例并將其分配給我們的私有 Validator 字段以驗證我們的 EditContext.Model

  • 第 23-24 行

    如果 EditContext 發生了變化,那么我們需要連接一些事件以便我們可以驗證用戶輸入,并且我們需要一個新的 ValidationMessageStore 來存儲任何驗證錯誤。

創建一個新的 ValidatorType 實例就像指示我們的 ServiceProvider 檢索一個實例一樣簡單。

private?void?ValidatorTypeChanged()
{Validator?=?(IValidator)ServiceProvider.GetService(ValidatorType);
}

為此,我們必須在應用程序的 Startup.ConfigureServices 方法中注冊驗證器——一旦我們有了驗證器和要驗證的東西,我們就會這樣做。

每當 EditContext 發生變化時,我們都需要一個新的 ValidationMessagesStore 來存儲我們的驗證錯誤消息。

void?EditContextChanged()
{ValidationMessageStore?=?new?ValidationMessageStore(EditContext);HookUpEditContextEvents();
}

我們還需要連接一些事件,以便我們可以驗證用戶輸入并將錯誤添加到我們的 ValidationMessageStore

private?void?HookUpEditContextEvents()
{EditContext.OnValidationRequested?+=?ValidationRequested;EditContext.OnFieldChanged?+=?FieldChanged;
}
  • OnValidationRequested

    當需要驗證 EditContext.Model 的所有屬性時會觸發此事件。當用戶嘗試發布 EditForm 以便 Blazor 可以確定輸入是否有效時,會發生這種情況。

  • OnFieldChanged

    每當用戶通過在 Blazor 的 InputBase<T> 派生組件之一中編輯 EditContext.Model 的屬性值來更改它時,都會觸發此事件。

async?void?ValidationRequested(object?sender,?ValidationRequestedEventArgs?args)
{ValidationMessageStore.Clear();var?validationContext?=new?ValidationContext<object>(EditContext.Model);ValidationResult?result?=await?Validator.ValidateAsync(validationContext);AddValidationResult(EditContext.Model,?result);
}
  • 第 3 行

    首先,我們從任何先前的驗證中清除所有錯誤消息。

  • 第 4 行

    接下來,我們指示 FluentValidation.IValidator 驗證在 EditForm(我們通過 EditContext.Model 訪問)中正在編輯的模型。

  • 第 5 行

    最后,我們將任何驗證錯誤添加到我們的 ValidationMessageStore,這是在一個單獨的方法中完成的,因為我們將在驗證整個對象時使用它,并且在通過 EditContext.OnFieldChanged 通知時驗證單個更改的屬性時也會使用它。

將錯誤消息添加到 ValidationMessageStore 只是創建一個 FieldIdentifier 以準確識別哪個對象/屬性有錯誤并使用該標識符添加任何錯誤消息,然后讓 EditContext 知道驗證狀態已更改的情況。

請注意,當驗證涉及長時間運行的異步調用(例如,對 WebApi 以檢查 UserName 可用性)時,我們可以更新驗證錯誤并多次調用 EditContext.NotifyValidationStateChanged 以在用戶界面中提供驗證狀態的增量顯示。

void?AddValidationResult(object?model,?ValidationResult?validationResult)
{foreach?(ValidationFailure?error?in?validationResult.Errors){var?fieldIdentifier?=?new?FieldIdentifier(model,?error.PropertyName);ValidationMessageStore.Add(fieldIdentifier,?error.ErrorMessage);}EditContext.NotifyValidationStateChanged();
}

最后,當用戶在表單輸入控件中編輯值時,我們需要驗證單個對象/屬性。當發生這種情況時,我們會通過 EditContext.OnFieldChanged 事件得到通知。除了前兩行和最后一行,以下代碼是 FluentValidator 特定的。

async?void?FieldChanged(object?sender,?FieldChangedEventArgs?args)
{FieldIdentifier?fieldIdentifier?=?args.FieldIdentifier;ValidationMessageStore.Clear(fieldIdentifier);var?propertiesToValidate?=?new?string[]?{?fieldIdentifier.FieldName?};var?fluentValidationContext?=new?ValidationContext<object>(instanceToValidate:?fieldIdentifier.Model,propertyChain:?new?FluentValidation.Internal.PropertyChain(),validatorSelector:?new?FluentValidation.Internal.MemberNameValidatorSelector(propertiesToValidate));ValidationResult?result?=?await?Validator.ValidateAsync(fluentValidationContext);AddValidationResult(fieldIdentifier.Model,?result);
}
  • 第 3-4 行

    從事件 args 中獲取 FieldIdentifier(ObjectInstance/PropertyName 對),并僅清除該屬性的所有先前錯誤消息。

  • 第 16 行

    使用 ValidationRequested 使用的相同方法將來自 FluentValidation 的錯誤添加到我們的 ValidationMessageStore

使用組件

首先創建一個模型供我們的用戶編輯。

namespace?CustomValidation.Models
{public?class?Person{public?string?Name?{?get;?set;?}public?int?Age?{?get;?set;?}}
}

接下來,使用 FluentValidation 為 Person 創建一個驗證器。

using?CustomValidation.Models;
using?FluentValidation;namespace?CustomValidation.Validators
{public?class?PersonValidator?:?AbstractValidator<Person>{public?PersonValidator(){RuleFor(x?=>?x.Name).NotEmpty();RuleFor(x?=>?x.Age).InclusiveBetween(18,?80);}}
}

因為我們的驗證組件使用 IServiceProvider 來創建驗證器的實例,所以我們需要在 Startup.ConfigureServices 中注冊它。

public?void?ConfigureServices(IServiceCollection?services)
{services.AddScoped<Validators.PersonValidator>();
}

最后,我們需要設置我們的用戶界面來編輯我們的 Person 類的一個實例。

@page?"/"
@using?Models<EditForm?Model=@Person?OnValidSubmit=@ValidFormSubmitted><FluentValidationValidator?ValidatorType=typeof(Validators.PersonValidator)/><p>Validation?summary</p><ValidationSummary?/><p>Edit?object</p><div?class="form-group"><label?for="Name">Name</label><InputText?@bind-Value=Person.Name?class="form-control"?id="Name"?/><ValidationMessage?For="()?=>?Person.Name"?/></div><div?class="form-group"><label?for="Age">Age</label><InputNumber?@bind-Value=Person.Age?class="form-control"?id="Age"?/><ValidationMessage?For=@(()?=>?Person.Age)?/></div><input?type="submit"?class="btn?btn-primary"?value="Save"?/>
</EditForm>@code?{Person?Person?=?new?Person();void?ValidFormSubmitted(){Person?=?new?Person();}
}

執行流程

頁面顯示

  1. 我們的 EditForm 組件是從 <EditForm Model=@Person> 標記創建的。

  2. EditForm.OnParametersSet 被執行,因為 EditForm.Model 已經從 null 變成了我們的 Person,它創建了一個新的 EditContext 實例。

  3. 新的 EditContext 實例通過級聯值[7]級聯到所有子組件。

  4. 對于這種級聯值的變化,InputBase<T> 的每個派生類都會執行其 SetParametersAsync,并通過創建 FieldIdentifier 的新實例來做出反應。

我們的驗證組件已初始化

  1. 我們的驗證組件的 SetParametersAsync 方法是通過引用新的 EditContext 來執行的。

  2. 我們的組件創建了一個新的 ValidationMessageStore

  3. 我們的組件偵聽 EditContext 上的事件以獲取驗證請求和輸入更改通知。

用戶更改數據

  1. 用戶在 InputBase<T> 派生類中編輯數據。

  2. 組件通過 EditContext.NotifyFieldChanged 通知此狀態更改(從未修改到已修改),并傳遞其 FieldIdentifier

  3. EditContext 觸發其 OnFieldChanged,傳遞 FieldIdentifier

  4. 我們組件的事件訂閱告訴我們的 ValidationMessageStore 清除由 FieldIdentifierModelFieldName 屬性標識的狀態的所有錯誤消息。

  5. 我們的組件對單個屬性執行其自定義驗證。

  6. 驗證錯誤被添加到我們組件的 ValidationMessageStore 中,由 FieldIdentifier 作為鍵。

  7. ValidationMessageStore 執行 EditContext.GetFieldState 以檢索當前 FieldIdentifierFieldState

  8. ValidationMessageStore 被添加到 FieldState,以便 FieldState.GetValidationMessages 能夠從所有 ValidationMessageStore 實例中檢索所有錯誤消息。

第 8 步特別重要,因為 Blazor 需要能夠檢索特定輸入的所有驗證錯誤消息,無論它們被添加到哪個 ValidationMessageStore

用戶提交表單

  1. <EditForm> 執行 EditContext.Validate

  2. EditContext 觸發其 OnValidationRequested 事件。

  3. 我們組件的事件訂閱告訴我們的 ·ValidationMessageStore· 清除所有字段的所有先前驗證錯誤消息。

  4. 我們的組件對整個 EditContext.Model 對象執行其自定義驗證。

  5. 與對單個更改的驗證一樣,錯誤被添加到 ·ValidationMessageStore·,它使用 ·EditContext· 中的所有相關 ·FieldState· 實例注冊自身。

  6. <EditForm> 根據是否有錯誤消息觸發相關的有效/無效事件。

EditForm.Model 已更改

如果這是一個用于創建 people 的用戶界面,那么在成功將我們的新 People 提交到服務器后,我們的應用程序可能會創建一個新 People 供我們的表單進行編輯。這將丟棄與前一個 ?People 實例相關的所有狀態(在虛線框中表示),并從新實例重新開始。

  • EditContext

  • FieldState

  • ValidationMessageStore

b31f838550bdf3ea6b20ca82dd2331ee.png

在演示代碼中添加了一些日志記錄,我們會看到以下輸出。

WASM:編輯上下文已更改
WASM:創建了新的 ValidationMessageStore
WASM:連接 EditContext 事件(OnValidationRequested 和 OnFieldChanged)
WASM:觸發 OnFieldChanged:驗證類 Person 上名為 Name 的單個屬性
WASM:觸發 OnFieldChanged:驗證 Person 類上名為 Age 的單個屬性
WASM:OnValidationRequested 觸發:驗證整個對象
WASM:EditContext 已更改
WASM:創建了新的 ValidationMessageStore
WASM:連接 EditContext 事件(OnValidationRequested 和 OnFieldChanged)
  • 第 1-3 行

    創建相關狀態實例以支持編輯 Person 實例。

  • 第 4 行

    輸入了一個名字

  • 第 5 行

    輸入年齡

  • 第 6 行

    用戶提交表單

  • 第 7 行

    Index.razor 中的 Person 實例被更改,導致元狀態實例被丟棄并為新的 EditForm.Model 創建新實例。

參考資料

[1]

源代碼: https://github.com/mrpmorris/blazor-university/tree/master/src/Forms/CustomValidation

[3]

FluentValidation: https://github.com/JeremySkinner/FluentValidation

[4]

blazor-validation: https://github.com/mrpmorris/blazor-validation

[6]

FluentValidation NuGet 包: https://www.nuget.org/packages/FluentValidation/

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/284886.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/284886.shtml
英文地址,請注明出處:http://en.pswp.cn/news/284886.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

HTML 元素內部添加預加載

CSS&#xff1a; /*元素內部加載loading*/.innerLoading {height: 100%;width: 100%;display: flex;justify-content: center;align-items: center;}.innerLoading * {text-align: center;color: #737782cc;fill: #73777A;font-size: 1em !important;font-family: SimSun,SimHe…

Windows下怎樣安裝Tomcat

Tomcat 是開源的WEB應用容器&#xff0c;所以受到各位程序員和公司的親賴。在這里給大家介紹一下如何在Windows環境下安裝Tomcat綠色版本&#xff0c;希望能夠對大家有幫助。 1.首先去Tomcat官網下載Tomcat軟件&#xff0c;在百度中搜索Tomcat,進入英文網址http://tomcat.apach…

智能識別云服務端平臺之神【合合信息TextIn】

一、前言 眾所周知&#xff0c;隨著互聯網和人工智能的發展&#xff0c;我們非常多的場景需要用到智能“識別”功能&#xff0c;比如人臉識別、通用文字識別、表格識別、辦公文檔識別、身份證、名片、營業執照等國內外卡證文字識別等等&#xff0c;同時識別與理解面臨的全球性技…

【ArcGIS微課1000例】0015:ArcGIS如何創建/自定義快捷鍵?

為了提高工作效率,強大的ArcGIS提供了很多快捷鍵,如訪問 ArcMap 菜單命令、窗口操縱、刷新或暫停地圖繪制、通過拖放進行移動或復制等等。本文在ArcGIS已有快捷鍵的基礎之上,為了提高工作效率,講解如何定制個性化的快捷鍵。 參考閱讀:【ArcGIS風暴】ArcGIS快捷鍵大全 文章…

Bresenham 算法

1965 年&#xff0c;Bresenham 為數字繪圖儀開發了一種繪制直線的算法&#xff0c;該算法同樣使用于光柵掃描顯示器&#xff0c;被稱為 Bresenham 算法。 原理 算法的目標是選擇表示直線的最佳光柵位置。Bresenhan 算法在主位移方向上每次遞增一個單位。另一個方向的增量為 0…

Python高級特性——迭代(Iteration)

Python高級特性——迭代&#xff08;Iteration&#xff09; 1、給定一個集合list或者tuple&#xff0c;可以通過for …… in ……的語法來實現循環遍歷&#xff0c;這個循環我們就叫做迭代 迭代list&#xff1a; >>> m [haha,hehe,heihei,gaga] >>> for li …

ML.NET 更新

點擊上方藍字關注我們&#xff08;本文閱讀時間&#xff1a;5分鐘)ML.NET是一款面向.NET開發人員的開源&#xff0c;跨平臺機器學習框架&#xff0c;可以將自定義機器學習集成到.NET應用中。我們很開心地向您介紹我們在過去幾個月中所做的工作。ML.NET:https://dotnet.microsof…

Andriod之提示java.lang.SecurityException: getDataNetworkTypeForSubscriber導致程序奔潰

1、問題 修改targetSdkVersion 33 適配Android13后4G網絡環境被其它app拉起來提示這個異常 2、原因 我們定位到代碼在這行函數 telephonyManager.getNetworkType()Android11 的權限有關,由于缺少該權限導致無法訪問接口而提示安全異常 3、解決辦法 方法1:我們直接申請RE…

js-權威指南學習筆記7

第七章 數組 1、數組直接量的語法允許有可選的結尾的逗號&#xff0c;所以[ , , ]只有兩個元素而非三個。 2、調用構造函數Array&#xff08;&#xff09;創建數組時&#xff0c;傳入一個參數時表示指定數組的長度。 3、所有的索引都是屬性名&#xff0c;但只有在0~2^32-2之間的…

[譯]基于GPU的體渲染高級技術之raycasting算法

[譯]基于GPU的體渲染高級技術之raycasting算法 PS&#xff1a;我決定翻譯一下《Advanced Illumination Techniques for GPU-Based Volume Raycasting》。像我翻譯其他資料一樣&#xff0c;只按我的需要和觀點來翻譯。有的部分詳細翻譯&#xff0c;附加注解&#xff0c;有的部分…

【GIS風暴】什么是地理空間智能(Geospatial AI)?

人工智能(Artificial Intelligence,AI)已經成為新技術革命下一階段的熱詞,也成為未來產業的驅動力量。使用智能算法,數據分類和智能預測、分析,AI在很多領域將有一系列的工具來幫助解決問題。 將AI用于GIS這一具體的領域的分析、方法和解決方案,就叫地理空間智能(Geos…

JavaScript 清除圖片背景顏色 使之透明

主要JS /**清除圖片背景顏色 **/ function removeImgBg(img) {//背景顏色 白色const rgba [255, 255, 255, 255];// 容差大小const tolerance 60;var imgData null;const [r0, g0, b0, a0] rgba;var r, g, b, a;const canvas document.createElement(canvas);const cont…

day01基礎部分

一、python是什么樣的語言 1、編譯型語言和解釋型語言&#xff0c;python是解釋型語言 1.1、編譯型語言就是把源程序代碼一次性翻譯成機器碼&#xff08;計算機可識別的代碼&#xff09;&#xff0c;然后交給計算機去運行&#xff0c;一般需經過編譯&#xff08;compile&#x…

WPF 制作 Windows 屏保

分享如何使用WPF 制作 Windows 屏保WPF 制作 Windows 屏保作者&#xff1a;驚鏵原文鏈接&#xff1a;https://github.com/yanjinhuagood/ScreenSaver框架使用.NET452&#xff1b;Visual Studio 2019;項目使用 MIT 開源許可協議&#xff1b;更多效果可以通過GitHub[1]|碼云[2]下…

Java 定時線程

功能需求&#xff1a;項目啟動時&#xff0c;后天起一個定時線程&#xff0c;每個小時跑一次&#xff0c;查出數據發郵件出來。 主要使用 public void schedule(TimerTask task, long delay)task被安排在delay&#xff08;毫秒&#xff09;指定的時間后執行。 public void sche…

Windows 7 下右鍵發送到菜單項沒了

為什么80%的碼農都做不了架構師&#xff1f;>>> 問題描述: 突然有一天,Windows 7 下右鍵發送到菜單項沒了,如圖所示: 問題原因 黑人問號臉? 轉載于:https://my.oschina.net/taadis/blog/1591398

【ArcGIS微課1000例】0016:ArcGIS書簽操作(添加書簽、管理書簽)知多少?

書簽可以將地圖數據的某一視圖狀態保存起來,以便在使用時打開書簽,直接回到這一視圖狀態。可創建多個書簽以便快速回到不同的視圖狀態,也可以對書簽進行管理。 文章目錄 1 創建書簽2 管理書簽注意:書簽只針對空間數據,在【布局視圖】中是不能創建書簽的。 1 創建書簽 可…

Android之webView打開http鏈接頁面無法加載顯示net:ERR_CLEARTEXT_NOT_PERMITTED

1、問題 適配Android13后&#xff0c;webView打開http鏈接提示錯誤如下 net:ERR_CLEARTEXT_NOT_PERMITTED2、原因 Android 9.0 默認使用加密連接&#xff0c;這意味著老舊項目在android 9.0 設備上運行&#xff0c;會遇到異常的情況。 3、解決辦法 android:usesCleartextTr…

分享一個WPF 實現 Windows 軟件快捷小工具

分享一個WPF 實現 Windows 軟件快捷小工具Windows 軟件快捷小工具作者&#xff1a;WPFDevelopersOrg原文鏈接&#xff1a;https://github.com/WPFDevelopersOrg/SoftwareHelper框架使用.NET40&#xff1b;Visual Studio 2019;項目使用 MIT 開源許可協議&#xff1b;項目使用 MV…

學習環境配置:Manjaro、MSYS2以及常見軟件

0.前言 在說Manjaro之前&#xff0c;要先說一下Linux發行版。對于各大發行版而言&#xff0c;內核只有版本的差異&#xff0c;最重要的區別就是包管理系統。常見的包管理系統包括&#xff1a;Pacman&#xff0c;Apt , Yum和Portage。在學習Linux的過程中&#xff0c;和大數人一…