Blingers
Blingers

Reputation: 883

Remote validation model binding not working on a razor page in dotnet core 2.0

I have a form in a razor page with a field that has remote validation..

. . .
    <div class="form-group">
        <label class="control-label" asp-for="ReportViewModel.ExecSql"></label>
        <textarea class="form-control" asp-for="ReportViewModel.ExecSql" ></textarea>
        <span asp-validation-for="ReportViewModel.ExecSql" class="text-danger"></span>
    </div>
. . .

The field in the ReportViewModel is defined as:

. . . 
[Remote(action: "VerifySql", controller: "ReportService", HttpMethod = "POST")]
public string ExecSql { get; set; }

To call the action below:

[AcceptVerbs("Post")]
public IActionResult VerifySql(string ExecSql)
{
   if (!Repository.VerifySql(ExecSql))
      return Json("Sql is not valid (Select statements only are allowed)");

    return Json(data: true);
}

The razor page cs file:

public class AddReportModel : PageModel
    {
        private readonly IIntegrityReportRepository _repository;
        private readonly IMapper _mapper;

        public AddReportModel(IIntegrityReportRepository repository, IMapper mapper)
        {
            _repository = repository;
            _mapper = mapper;
        }

        [TempData]
        public string ConfirmationMessage { get; set; }

        [BindProperty]
        public IntegrityReportViewModel ReportViewModel { get; set; }
. . .

This calls the action but ExecSql is always null. This is because when I look at the request, the form is posting.. ReportViewModel.ExecSql. Which I cannot pick up from my VerifySql action method.

I have tried adding a name to the cshtml field:

<textarea class="form-control" asp-for="ReportViewModel.ExecSql" name="ExecSql" ></textarea>

This then does bind the field and pass the value, however the client side validation does not work when it is passed back.

I can get it working by defining ANOTHER field in the cs razor page file e.g.

[Remote(action: "VerifySql", controller: "ReportService", HttpMethod = "POST")]
[BindProperty]
public string ExecSqlField { get; set; }

And then changing the cshtml to:

<div class="form-group">
    <label class="control-label" asp-for="ExecSqlField"></label>
    <textarea class="form-control" asp-for="ExecSqlField"></textarea>
    <span asp-validation-for="ExecSqlField" class="text-danger"></span>
</div>

However this feels wrong and I am then duplicating the viewmodel which I need to share across another pages. Is there anyway to pick up the field in the original viewmodel in my action method by somehow getting the Verify action method to access ReportViewModel.ExecSql?

Many thanks for any help

Upvotes: 3

Views: 1430

Answers (2)

CSharpMinor
CSharpMinor

Reputation: 232

Just wanted to add that @Baseless and @Breno did have the correct answer.

My property was as such:

        [Display(Name = "Access Code")]
        [Remote("CheckForGroup", "Users", "Admin", HttpMethod = "Post")]
        public string AccessCode { get; set; }

and it only worked when I added this to my controller:

    public IActionResult CheckForGroup([Bind(Prefix = "Input.AccessCode")] string accessCode)

Thank you guys!

Upvotes: 4

Breno Miyazaki
Breno Miyazaki

Reputation: 148

Afte banging heads a lot, I found a workaround. Not the most elegant but it is doing the job right now.

Just add [Bind(Prefix = "ReportViewModel.ExecSql")] to your validation method parameter.

You can revert the changes and resume using the "Model.Field" form pattern.

Upvotes: 3

Related Questions