0

I'm facing an odd issue here.

I have a page where i have setup a PageRemote attribute to validate a field, and on the same page, i have 2 buttons that each call a distinct handler using the asp-page-handler property (1 to update the content, the other to delete it).

If i remove the PageRemote validation, the handlers for both buttons are triggered as expected, but when i leave the PageRemote validation on the page, the buttons specific handlers are not called, although the PageRemote handler is correctly triggered.

Furthermore, when leaving the PageRemote validation on the page, if i force its validation to return false, then afterwards the buttons trigger their respective handler, but if i do not force it, then the behaviour is as described earlier, where the handlers are not triggered.

Can someone explain me if there is a catch on using these two things on the same page, and/or how to overcome this?

Below is a sample of the page code:

public class EditModel : PageModel
{
    
    private IConfiguration _configuration;

    [Required(ErrorMessageResourceType = typeof(ErrorMessages),
        ErrorMessageResourceName = nameof(ErrorMessages.SlugRequired))]
    [MinLength(3, ErrorMessageResourceType = typeof(ErrorMessages),
        ErrorMessageResourceName = nameof(ErrorMessages.SlugMinLength))]
    [MaxLength(128, ErrorMessageResourceType = typeof(ErrorMessages),
        ErrorMessageResourceName = nameof(ErrorMessages.SlugMaxLength))]
    [RegularExpression(@"^[0-9a-zA-Z_/-]*$", ErrorMessageResourceType = typeof(ErrorMessages),
        ErrorMessageResourceName = nameof(ErrorMessages.SlugAllowedCharacters))]
    [PageRemote(ErrorMessageResourceType = typeof(ErrorMessages),
        ErrorMessageResourceName = nameof(ErrorMessages.SlugDuplicate),
        AdditionalFields = "__RequestVerificationToken,GenericContentDTO.GenericContent.GenericContentId",
        HttpMethod = "post",
        PageHandler = "CheckSlug")]
    [Display(Name = "URL Title")]
    [BindProperty]
    public string Slug { get; set; }

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

    [BindProperty]
    public GenericContentDTO GenericContentDTO { get; set; }

    public EditModel(IConfiguration configuration)
    {
        _configuration = configuration;
    }

    public IActionResult OnGet(Guid genericContentId)
    {
        if (genericContentId.ToString() == "")
        {
            return NotFound();
        }

        ...
        Code to load content
        ...

        return Page();

    }

    public async Task<IActionResult> OnPostUpdate()
    {

        if (!ModelState.IsValid)
        {
            return Page();
        }

        ...
        Code to update content
        ...

        return RedirectToPage(new { GenericContentId = GenericContentDTO.GenericContent.GenericContentId });

    }

    public IActionResult OnPostDelete()
    {

        ...
        Code to delete content
        

    }

    public JsonResult OnPostCheckSlug()
    {

        var token = HttpContext.Session.GetString("APIAuthorizationToken");

        CheckSlugDTO CheckSlug = new CheckSlugDTO
        {
            Slug = Slug,
            ContentId = GenericContentDTO.GenericContent.GenericContentId,
            Exists = true
        };

        CheckSlug = APICommunicationHelper.PostData<CheckSlugDTO>(CheckSlug, _configuration["AppConfigs:APIAddress"] + "api/CheckGenericContentSlugExistance", token);

        return new JsonResult(!CheckSlug.Exists);
    }
}

And of the Razor Code:

@page "{GenericContentId:Guid}"
@model MyProject.Pages.Generic_Contents.EditModel
@{
}

<form method="post" id="editForm" name="editForm" enctype="multipart/form-data">

<h3>@TempData["FormResultMessage"]</h3>

<ul class="nav nav-tabs">
    <li class="nav-item"><a class="nav-link active" data-toggle="tab" href="#main">Main</a></li>
    <li class="nav-item"><a class="nav-link" data-toggle="tab" href="#meta">Meta</a></li>
</ul>

<div class="tab-content">

    <div id="main" class="tab-pane active">
        <br />

        <div asp-validation-summary="ModelOnly" class="text-danger"></div>

        @Html.HiddenFor(model => model.GenericContentDTO.GenericContent.GenericContentId)

        <div class="form-group">
            <label asp-for="GenericContentDTO.GenericContent.Title" class="control-label"></label>
            <input asp-for="GenericContentDTO.GenericContent.Title" class="form-control" />
            <span asp-validation-for="GenericContentDTO.GenericContent.Title" class="text-danger"></span>
        </div>

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

        <div class="form-group row">

            <div class="col-4">
                <label asp-for="GenericContentDTO.MainImage" class="control-label"></label>
                <input asp-for="GenericContentDTO.MainImage" type="file" class="form-control" />
            </div>

        </div>

        <div class="form-group">
            <label asp-for="GenericContentDTO.GenericContent.Summary" class="control-label"></label>
            @Html.TextAreaFor(model => model.GenericContentDTO.GenericContent.Summary, new { @class = "form-control", @rows = 5 })
            <span asp-validation-for="GenericContentDTO.GenericContent.Summary" class="text-danger"></span>
        </div>

        <div class="form-group">
            <label asp-for="GenericContentDTO.GenericContent.ContentText" class="control-label"></label>
            @Html.TextAreaFor(model => model.GenericContentDTO.GenericContent.ContentText, new { @class = "form-control editorHtml" })
        </div>

        <div class="form-group">
            <label asp-for="GenericContentDTO.GenericContent.IsActive" class="control-label"></label>
            @Html.CheckBoxFor(model => model.GenericContentDTO.GenericContent.IsActive)
        </div>

    </div>

    <div id="meta" class="tab-pane fade">
        <div class="form-group">
            <label asp-for="GenericContentDTO.GenericContent.MetaDescription" class="control-label"></label>
            @Html.TextAreaFor(model => model.GenericContentDTO.GenericContent.MetaDescription, new { @class = "form-control", @rows = 5 })
            <span asp-validation-for="GenericContentDTO.GenericContent.MetaDescription" class="text-danger"></span>
        </div>

        <div class="form-group">
            <label asp-for="GenericContentDTO.GenericContent.MetaKeywords" class="control-label"></label>
            @Html.TextAreaFor(model => model.GenericContentDTO.GenericContent.MetaKeywords, new { @class = "form-control", @rows = 5 })
            <span asp-validation-for="GenericContentDTO.GenericContent.MetaKeywords" class="text-danger"></span>
        </div>
    </div>


    <div class="form-group text-right">
        <a asp-area="" asp-page="/Generic-Contents" class="btn btn-secondary">Cancel</a>
        <button type="submit" class="btn btn-danger" asp-page-handler="Delete">Delete Content</button>
        <button type="submit" class="btn btn-primary" asp-page-handler="Update">Update Content</button>
    </div>

</div>

</form>

@section Scripts {
@{await Html.RenderPartialAsync("_ValidationScriptsPartial");}    
}

Thanks

Obelix
  • 1
  • 2
  • 1
    Maybe check this post: https://stackoverflow.com/questions/59335713/razorpages-page-remote-not-working-on-model – pcalkins Feb 21 '22 at 21:44
  • What do you mean for you force the remote validation false? Do you mean you just return Json(false) instead of `CheckSlug.Exists`? Or do you mean the `asp-page-handler` can work with failure validation but cannot work with the success validation? – Rena Feb 22 '22 at 07:57
  • Hi Rena, Thanks for the reply. What i mean is that if i intentionally input a value on the field that will return the validation as false on the PageRemote validated field, then correct it back to an accepted value, afterwards, if i click the Update or the Delete button, the correct handler is triggered, but on the other hand, if i load the page with a valid value on the field, and do not change the value of the mentioned field in order to fail the validation first, and press any of the buttons, they will not respect their respective handler, and instead fire the base OnPost handler. – Obelix Feb 22 '22 at 10:55
  • Hi pcalkins, Thanks for the reply. However i fail to see the relevance of the answer posted on the provided URL, since the issue is not the PageRemote not being fired, which it is, but the handlers of the Delete and Update buttons only firing if the PageRemote first returns the validation as false, and then the input value corrected so that it returns back to true. A more detailed explanation is provided on my response to Rena. – Obelix Feb 22 '22 at 10:57
  • Hi @Obelix, got your situation now. But it works fine in my project. Could you please check the Console panel in browser if any error display when you click the button? – Rena Feb 23 '22 at 07:27
  • Hi @Rena, No Errors on the console... the strangest is that if i inspect the HTML, the handler is there on the URL that the button should call... but the fact is that it does not call it, nor does it ever get written to the browser's address bar – Obelix Feb 23 '22 at 16:17
  • Hi @Obelix, please check the generated html code if `asp-handler` generate the correct `formaction`? Or it just still keep `asp-handler`? – Rena Feb 24 '22 at 09:58
  • Hi @Rena, as i pointed out before, the html is correctly generated, but in fact never actually called. If i change the handler to the form itself, then it is called correctly and fires the correct handler, but that happens for both the buttons, rendering it impossible to distinguish which one was called. – Obelix Feb 25 '22 at 00:57

0 Answers0