我正在使用带有Razor Pages的ASP.NET Core 2,我试图在一个页面上两个具有单独属性(BindProperty)的表单。
@page
@model mfa.Web.Pages.TwoFormsModel
@{
Layout = null;
}
<form method="post">
<input asp-for="ProductName" />
<span asp-validation-for="ProductName" class="text-danger"></span>
<button type="submit" asp-page-handler="Product">Save product</button>
</form>
<form method="post">
<input asp-for="MakerName" />
<span asp-validation-for="MakerName" class="text-danger"></span>
<button type="submit" asp-page-handler="Maker">Save maker</button>
</form>
和相应的PageModel:
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using System.ComponentModel.DataAnnotations;
using System.Threading.Tasks;
namespace mfa.Web.Pages
{
public class TwoFormsModel : PageModel
{
[BindProperty]
[Required]
public string ProductName { get; set; }
[BindProperty]
[Required]
public string MakerName { get; set; }
public async Task<IActionResult> OnPostProductAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
return Page();
}
public async Task<IActionResult> OnPostMakerAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
return Page();
}
}
}
点击两个提交按钮中的任何一个都会带我进入相应的帖子处理程序。两者都是&#34; ProdutName&#34;和&#34; MakerName&#34;使用我在相应输入字段中输入的内容进行核心填充。到目前为止,非常好。
但是:ModelState.IsValid()始终返回true - 无论相应属性的值是否具有值。即使两个属性都为null,ModelState.IsValid()也为true。
另外:OnPostProductAsync()应该只验证&#34; ProductName&#34;因此OnPostMakerAsync()应该只验证&#34; MakerName&#34;。
这可以完成吗?还是我从Razor Pages那里问了太多?有很多博客和教程向您展示如何在一个页面上有两个表单......但它们都使用相同的模型。我需要不同的型号!
答案 0 :(得分:5)
为了使验证正常工作,您必须创建一个包含两个属性的视图模型,并为要检查的每个属性定义[Required],但因为您有两个不同的不同形式验证它不会起作用,因为如果两个值都被定义为必需,那么当您尝试验证产品时,它将验证Maker,它也没有值。
您可以做的是自己进行检查。例如,OnPostProduct可以包含以下代码:
@RunWith(MockitoJUnitRunner::class)
class RegistrationFragmentViewModelTest {
var mViewModel: RegistrationFragmentViewModel? = null
@Mock var mContext: Context? = null
@Mock var mView: RegistrationView? = null
@Before
fun setUp() {
mViewModel = RegistrationFragmentViewModel(mContext!!, mView!!)
}
@Test
fun AfterInput_InvalidFirstName_ShowFirstNameError() {
// Given
val firstName = "fa"
val email = "fanjavaid@gmail.com"
val password = "demo"
// When
mViewModel?.doRegister()
// Then
verify(mViewModel)?.validateInput()
verify(mView)?.showFirstNameError(mContext?.resources?.getString(R.string.registration_error_email_format)!!)
}
}
答案 1 :(得分:2)
我的解决方案不是很好,但是它不需要您手动进行验证。您可以保留[Required]
批注。
您的PageModel
看起来像这样-
private void ClearFieldErrors(Func<string, bool> predicate)
{
foreach (var field in ModelState)
{
if (field.Value.ValidationState == Microsoft.AspNetCore.Mvc.ModelBinding.ModelValidationState.Invalid)
{
if (predicate(field.Key))
{
field.Value.ValidationState = Microsoft.AspNetCore.Mvc.ModelBinding.ModelValidationState.Valid;
}
}
}
}
public async Task<IActionResult> OnPostProductAsync()
{
ClearFieldErrors(key => key.Contains("MakerName"));
if (!ModelState.IsValid)
{
return Page();
}
return Page();
}
public async Task<IActionResult> OnPostMakerAsync()
{
ClearFieldErrors(key => key.Contains("ProductName"));
if (!ModelState.IsValid)
{
return Page();
}
return Page();
}
不是最好的主意,因为您需要将绑定的字段名称与字符串进行比较。我使用Contains
是因为字段键不一致,并且有时包含前缀。对我来说已经足够好了,因为我的表单很小,具有不同的字段名称。
答案 2 :(得分:1)
另一种解决方案非常接近...
public static class ModelStateExtensions
{
public static ModelStateDictionary MarkAllFieldsAsSkipped(this ModelStateDictionary modelState)
{
foreach(var state in modelState.Select(x => x.Value))
{
state.Errors.Clear();
state.ValidationState = ModelValidationState.Skipped;
}
return modelState;
}
}
public class IndexModel : PageModel
{
public class Form1Model {
// ...
}
public class Form2Model {
// ...
}
[BindProperty]
public Form1Model Form1 { get; set; }
[BindProperty]
public Form2Model Form2 { get; set; }
public async Task<IActionResult> OnPostAsync()
{
ModelState.MarkAllFieldsAsSkipped();
if (!TryValidateModel(Form1, nameof(Form1)))
{
return Page();
}
// ...
}
}
答案 3 :(得分:0)
我面临着同样的问题,那是我的解决方案。
public class IndexModel : PageModel
{
private readonly ILogger<IndexModel> _logger;
[BindProperty]
public IndexSubscribeInputModel SubscribeInput { get; set; }
[BindProperty]
public IndexContactInputModel ContactInput { get; set; }
public IndexModel(ILogger<IndexModel> logger)
{
_logger = logger;
}
public void OnGet()
{
SubscribeInput = new IndexSubscribeInputModel();
ContactInput = new IndexContactInputModel();
}
public void OnPostSubscribe()
{
if (IsValid(SubscribeInput))
{
return;
}
}
public void OnPostContact()
{
if (IsValid(ContactInput))
{
return;
}
}
public class IndexSubscribeInputModel
{
[Required(AllowEmptyStrings =false, ErrorMessage ="{0} é obrigatório!")]
public string Email { get; set; }
}
public class IndexContactInputModel
{
[Required(AllowEmptyStrings = false, ErrorMessage = "{0} é obrigatório!")]
public string Email { get; set; }
[Required(AllowEmptyStrings = false, ErrorMessage = "{0} é obrigatório!")]
public string Message { get; set; }
}
private bool IsValid<T>(T inputModel)
{
var property = this.GetType().GetProperties().Where(x => x.PropertyType == inputModel.GetType()).FirstOrDefault();
var hasErros = ModelState.Values
.Where(value => value.GetType().GetProperty("Key").GetValue(value).ToString().Contains(property.Name))
.Any(value =>value.Errors.Any());
return !hasErros;
}
我可能会将“ IsValid”方法放在PageModelExtensions类中,这样会更加有趣。
希望这可以帮助某人...