How to dynamically update a label in Razor c# - label

How to add code in OnInitializedAsync to update the label?
<div id="container">
<label id="labelId" class="label"></label>
</div>
#code {
protected override async Task OnInitializedAsync()
{
// code to set the display text for the label here
}
}

<div id="container">
<label id="labelId" class="label">#_labelText</label>
</div>
#code {
private string _labelText;
protected override void OnInitialized()
{
_labelText = "This is a label.";
}
}
If you are not calling any async methods then you should use OnInitialized instead of OnInitializedAsync.

Related

Passing form data from View Component to Controller in .NET Core MVC

I have a Component View and I try to update data from a form in it, I call the controller but in the controller I receive null :
public class CustomerPropertyViewComponent: ViewComponent
{
private MyDBContext context;
public CustomerPropertyViewComponent(MyDBContext _contex)
{
context = _contex;
}
public async Task<IViewComponentResult> InvokeAsync(int id)
{
CustomerPropertyModelView c = new CustomerPropertyModelView();
TblCustomerProperty t = new TblCustomerProperty(context);
c = t.getAllInfo(id);
if (c.model == null)
{
c.model = new TblCustomerProperty();
c.model.CustomerId = id;
}
return View(c);
}
}
and in the view I have
#model SunSystemDotNetCoreVersion.Models.helpers.CustomerPropertyModelView
<form asp-action="Update" asp-controller="CustomerProperties"
data-ajax="true"
data-ajax-method="POST"
method="post">
<div class="row w-100">
<div class="col-6">
<div class="row align-items-center h-100">
<div class="col-5 text-right">
Number of Bedrooms
</div>
<div class="col-7 p-1 p-1">
#Html.TextBoxFor(model => model.model.Bedrooms, new { #class = "form-control", Type = "number" })
</div>
<div class="col-5 text-right">
Current Heating System
</div>
<div class="col-7 p-1">
<select asp-for="model.HeatingSystemTypeId" class="form-control"
asp-items="#(new SelectList(Model.HeatingsList,"HeatingSystemTypeId","Title"))">
<option value="0">-Plaese Select-</option>
</select>
</div>
.....
<div class="col-12">
<button type="submit" >Save</button>
</div>
</div>
</form>
I have reduced most of the view code but it contains all data that the model needs. and this is my controller:
public class CustomerPropertiesController : Controller
{
private readonly MyDBContext_context;
public CustomerPropertiesController(MyDBContextcontext)
{
_context = context;
}
public IActionResult Update(TblCustomerProperty modelView) {
//here modelView is null
return View();
}
it should be work I don't know why it keeps sending null to my controller.
You could F12 to check the html elements in browser,and you will find the name of these elements are like:model.Bedrooms.Because your main model is CustomerPropertyModelView but your input belongs to TblCustomerProperty which named model in CustomerPropertyModelView.If your backend code recieve CustomerPropertyModelView as parameter,it will not be null.But if you recieve TblCustomerProperty as parameter,you need specify the suffix.
Change like below:
public IActionResult Update([Bind(Prefix ="model")]TblCustomerProperty modelView)
{
return View();
}

Custom Model Validation in ASP.Net core v3.1 MVC ajax form not seems to be working

I'm working on an ASP.Net core 3.1 MVC project in which I have to create a custom Validator, and I want it to be working for client as well as server side (e.g. Required Attribute).
I developed a simple custom validator like below just for POC -
public class ImportantAttribute : ValidationAttribute, IClientModelValidator
{
public void AddValidation(ClientModelValidationContext context)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
AttributeUtils.MergeAttribute(context.Attributes, "data-val", "true");
AttributeUtils.MergeAttribute(context.Attributes, "data-val-important", FormatErrorMessage(context.ModelMetadata.GetDisplayName()));
}
public class AttributeUtils
{
public static bool MergeAttribute(
IDictionary<string, string> attributes,
string key,
string value)
{
if (attributes.ContainsKey(key))
{
return false;
}
attributes.Add(key, value);
return true;
}
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
if (value != null)
{
string val = value.ToString();
if (val.Contains("hello"))
{
return ValidationResult.Success;
}
}
return new ValidationResult("Value not valid");
}
}
and used this attribute on a property and created a View using the same model.
Them modified the form tag to become an ajax form like -
<form asp-action="Index" role="form" data-ajax="true" data-ajax-method="post">
<div asp-validation-summary="All" class="text-danger"></div>
<div class="form-group">
<label asp-for="Name" class="control-label"></label>
<input asp-for="Name" class="form-control" value="SGSM" />
<span asp-validation-for="Name" class="text-danger"></span>
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-primary" />
</div>
</form>
Then I added below java script -
$(document).ready(() => {
console.log('I\'m ready bro');
$.validator.addMethod("important",
function (value, element, params) {
console.log('1', value);
return value.contains('hello');
}, "Not OK");
$.validator.unobtrusive.adapters.add("important",
['important'],
function (options) {
console.log('2', options);
options.rules["important"] = options.important;
options.messages["important"] = options.message;
});
});
When I run this by providing any value to the text box and submitting the form it don't show any error message on the page, but if I put break point in the Action Method the ModelState shows correct info.
If I make the form as regular form (i.e. non-ajax form) everything works as expected.
I have searched a lot but could not find any thing related.
Based on your code and requirement, I made some some modifications on custom client-side validation code, which works well for me, you can refer it.
<div class="row">
<div class="col-md-4">
<form asp-action="Index" method="post" role="form" data-ajax="true" data-ajax-method="post" data-ajax-complete="completed">
<div asp-validation-summary="All" class="text-danger"></div>
<div class="form-group">
<label asp-for="Name" class="control-label"></label>
<input asp-for="Name" class="form-control" />
<span asp-validation-for="Name" class="text-danger"></span>
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-primary" />
</div>
</form>
</div>
</div>
#section Scripts {
#{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
<script src="~/lib/jquery-ajax-unobtrusive/dist/jquery.unobtrusive-ajax.js"></script>
<script>
$(document).ready(() => {
console.log('I\'m ready bro');
});
completed = () => {
alert('Request completed!');
};
$.validator.addMethod("important",
function (value, element, params) {
console.log('1', value);
return value.includes('hello');
}, "Not OK");
$.validator.unobtrusive.adapters.add("important",
['important'],
function (options) {
console.log('2', options);
var element = $(options.form).find('input#Name')[0];
options.rules["important"] = [element, ''];
options.messages["important"] = options.message;
});
</script>
}
Test Result

Use recaptcha v3 without form and button

I would like to use recaptcha on our forms but our forms are specific as they are made by our CMS Sitefinity. They have no <form> eitheir <button>. Its structure looks like this:
<div class="sfFormsEditor sfTopLbls ">
[...]
<div class="g-recaptcha" data-sitekey="Site key"></div>
<div class="sfFormSubmit sfSubmitBtnMedium">
<input type="submit" value="Envoyer la demande" data-sitekey="Site key" data-callback="onSubmit" data-action="submit">
</div>
</div>
I tried to use this script:
<script src='https://www.google.com/recaptcha/api.js'></script>
<script>
$(".sfFormSubmit input").attr('data-sitekey','Site key');
$(".sfFormSubmit input").attr('data-callback','onSubmit');
$(".sfFormSubmit input").attr('data-action','submit');
$(document).ready(function(){
$(".sfFormSubmit input").on('click',function(){
e.preventDefault();
grecaptcha.execute('Site key', {action: 'submit'});
});
});
</script>
The captcha is visible but when filling the form without clicking on the captcha, the form is submitted.
Is there a way to use recaptcha on such a form?
Thank you
Best
Sophie
You need to build a custom field control. For webforms, you can start here:
https://www.progress.com/documentation/sitefinity-cms/tutorial-build-a-custom-field-control-webforms-
It may look something like this:
ReCaptcha.ascx
<%# Control Language="C#" %>
<%# Register Assembly="Telerik.Sitefinity" TagPrefix="sf" Namespace="Telerik.Sitefinity.Web.UI" %>
<% if ( !Request.RawUrl.ToLower().Contains("/sitefinity/content/forms/entries/") ) { %>
<script src='https://www.google.com/recaptcha/api.js'></script>
<asp:Label runat="server" ID="titleLabel" CssClass="sfTxtLbl" Text="Captcha" AssociatedControlID="TextBox1" Visible="false" />
<div class="sfFieldWrp" style="display: none;">
<asp:TextBox ID="TextBox1" CssClass="sfTxt" runat="server" Text="OK" />
<sf:SitefinityLabel runat="server" ID="descriptionLabel" WrapperTagName="div" CssClass="sfDescription" />
<sf:SitefinityLabel runat="server" ID="exampleLabel" WrapperTagName="div" CssClass="sfExample" />
</div>
<div class="col-xs-12 col-sm-12 col-md-12">
<div class="g-recaptcha" data-sitekey="__YourReCaptchaSiteKey___"></div>
</div>
<% } %>
ReCaptcha.cs
// not including the full code, as most of it is auto-generated, but here are
// the most important methods:
public override bool IsValid()
{
// check the captcha with google
var encodedResponse = HttpContext.Current.Request.Form["g-Recaptcha-Response"];
var isCaptchaValid = ReCaptchaModel.Validate(encodedResponse);
return isCaptchaValid;
}
ReCaptchaModel.cs
public class ReCaptchaModel
{
public bool Success { get; set; }
public List<string> ErrorCodes { get; set; }
public static bool Validate(string encodedResponse)
{
if (string.IsNullOrEmpty(encodedResponse)) return false;
var client = new WebClient();
var secret = __YourReCaptchaSecretKey__ ;
if (string.IsNullOrEmpty(secret)) return false;
var googleReply = client.DownloadString(string.Format("https://www.google.com/recaptcha/api/siteverify?secret={0}&response={1}", secret, encodedResponse));
var serializer = new JavaScriptSerializer();
var reCaptcha = serializer.Deserialize<ReCaptcha>(googleReply);
return reCaptcha.Success;
}
}
ReCaptcha.js
Type.registerNamespace("SitefinityWebApp.CustomControls.FormControls.CustomFormWidgets.ReCaptcha");
SitefinityWebApp.CustomControls.FormControls.CustomFormWidgets.ReCaptcha.ReCaptcha = function (element) {
this._textbox = null;
this._dataFieldName = null;
SitefinityWebApp.CustomControls.FormControls.CustomFormWidgets.ReCaptcha.ReCaptcha.initializeBase(this, [element]);
}
SitefinityWebApp.CustomControls.FormControls.CustomFormWidgets.ReCaptcha.ReCaptcha.prototype = {
/* --------------------------------- set up and tear down ---------------------------- */
/* --------------------------------- public methods ---------------------------------- */
// Gets the value of the field control.
get_value: function () {
return jQuery(this._textbox).val();
},
// Sets the value of the text field control depending on DisplayMode.
set_value: function (value) {
jQuery(this._textbox).val(value);
},
/* --------------------------------- event handlers ---------------------------------- */
/* --------------------------------- private methods --------------------------------- */
/* --------------------------------- properties -------------------------------------- */
get_textbox: function () {
return this._textbox;
},
set_textbox: function (value) {
this._textbox = value;
},
get_dataFieldName: function () {
return this._dataFieldName;
},
set_dataFieldName: function (value) {
this._dataFieldName = value;
}
}
SitefinityWebApp.CustomControls.FormControls.CustomFormWidgets.ReCaptcha.ReCaptcha.registerClass('SitefinityWebApp.CustomControls.FormControls.CustomFormWidgets.ReCaptcha.ReCaptcha', Telerik.Sitefinity.Web.UI.Fields.FieldControl);

How do you connect a POST to a different razor page loaded via AJAX into a modal popup?

Edit: Have marked up where the error in the original code was stopping this from working.
I can find plenty of info and examples of this on MVC, but doesn't seem to apply for Razor Pages?
Simple scenario: I have a page (FooList) showing a list of Foo items. Each has an Edit button. This opens a modal popup with the layout (and data) coming from a second page (FooEdit).
The Edit form appears and populates fine, but I can't work out how to get it to post the data back to the FooEdit code behind?
List page, FooList.cshtml
#page
#model Pages.FooListModel
<table>
#foreach (var item in Model.FooListVM)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.Name)
</td>
<td>
<a onclick="openModal(#item.ID);">Edit</a>
</td>
</tr>
}
</table>
<div class="modal fade" id="editModal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-header border-bottom-0">
<h5 class="modal-title" id="exampleModalLabel">Edit Foo</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<form> <---- Edit: ** This shouldn't be here **
<div class="modal-body">
</div>
</form> <---- Edit
</div>
</div>
</div>
<script>
function openModal(i) {
$.get("FooEdit?id="+i,
null,
data => {
$("#editModal").modal("show");
$("#editModal .modal-body").html(data);
});
};
</script>
Code behind, FooList.cshtml.cs
public class FooListModel : PageModel
{
public IList<FooListVM> FooListVM { get; set; }
public void OnGet()
{
FooListVM = new List<FooListVM>
{
new FooListVM { ID = 1, Name = "Foo 1" },
new FooListVM { ID = 2, Name = "Foo2" }
};
}
}
public class FooListVM
{
public int ID { get; set; }
public string Name { get; set; }
}
Second page for the popup, FooEdit.cshtml
#page
#model Pages.FooEditModel
#(Layout=null)
<form method="post">
<input asp-for="FooEditVM.Name" class="form-control" /><br />
<input asp-for="FooEditVM.Stuff1" class="form-control" /><br />
<input asp-for="FooEditVM.Stuff2" class="form-control" /><br />
<input type="submit" value="Save"/>
</form>
And the code behind for the popup, FooEdit.cshtml.cs
public class FooEditModel : PageModel
{
[BindProperty]
public FooEditVM FooEditVM { get; set; }
public void OnGet(int id)
{
FooEditVM = new FooEditVM
{
Name = $"This is item {id}",
Stuff1 = "Stuff1",
Stuff2 = "Stuff2"
};
}
public void OnPost(int id)
{
// How do we get to here???
var a = FooEditVM.Name;
}
}
public class FooEditVM
{
public string Name { get; set; }
public string Stuff1 { get; set; }
public string Stuff2 { get; set; }
}
I've been through all the MS Tutorial stuff on Asp.net Core 2.2, but it doesn't seem to cover this.
Also as a side question, although it works, is there a "ASP helper tag" way of doing the ajax bit?
Have realised the problem was the 'form' tag in the Modal Dialog markup that was clashing the 'form' tag from the partial page. Removing it fixed everything using:
In FooEdit.cshtml
<form id="editForm" asp-page="FooEdit">
. . .
</form>
In FooEdit.cshtml.cs
public void OnPost()
{
// Fires in here
}
I'm pretty sure the fooedit page is going to need some jQuery to handle this.
See below for what I would do in the fooedit page.
#page
#model Pages.FooEditModel
#(Layout=null)
<form id=fooedit method="post" action="FooEdit">
<input asp-for="FooEditVM.Name" class="form-control" /><br />
<input asp-for="FooEditVM.Stuff1" class="form-control" /><br />
<input asp-for="FooEditVM.Stuff2" class="form-control" /><br />
<input type="submit" value="Save"/>
</form>
<SCRIPT language="JavaScript" type="text/Javascript">
<!--
$(document).ready(function(e) {
$("#fooedit").submit(function(e) {
e.preventDefault();
var form_data = $(this).serialize();
var form_url = $(this).attr("action");
var form_method = $(this).attr("method").toUpperCase();
$.ajax({
url: form_url,
type: form_method,
data: form_data,
cache: false,
success: function(returnhtml){
$("#editModal.modal-body").html(returnhtml);
}
});
});
});
</SCRIPT>

Validation error message not displayed in Asp.Net Core 2 MVC partial view

I have an index page with a list of "workbooks" titles and for each workbook, there is a "share" button. When pressing the button a bootstrap model (i.e. dialog) appears which displays the title of the workbook and a textarea allowing the user to type in a sharees email addresses.
When the user presses on the "share" button, I am calling a javascript function which calls a controller action that returns a partial view containing the modal dialog with a form inside it. The problem is that after pressing the submit button (i.e. "Share") there are no validation errors being shown to the user and I am not sure why that is. Could anyone provide some ideas, please?
That is my main (index.cshtml) page:
#model DNAAnalysisCore.Models.WorkBookModel
#{
}
#section BodyFill
{
<script type="text/javascript">
function showSharingView(title) {
var url = "#Url.Action("ShowShareDialog", "Home")" + "?workbookTitle=" + encodeURI(title);
$('#shareFormContainer').load(url,
function() {
$('#shareFormModal').modal("show");
});
}
function hideSharingView() {
$('#shareFormModal').modal("hide");
}
</script>
<div id="shareFormContainer" >
<!--PLACEHOLDER FOR SHARE DIALOG -->
</div>
<div class="workbook-container">
<table class="table">
<tbody>
#foreach (var workbook in Model.Workbooks)
{
<tr>
<td>#Html.ActionLink(workbook.Name, "Open", "OpenAnalytics", new {id = Model.Id, workbook = workbook.Name})</td>
<td>
<button title="Share" class="share-button" onclick='showSharingView("#workbook.Name")'> </button>
</td>
</tr>
}
</tbody>
</table>
</div>
}
That is my Controller:
public class HomeController : Controller
{
[HttpGet]
public IActionResult ShowShareDialog(string workbookTitle)
{
var shareModel = new ShareModel
{
Title = workbookTitle
};
return PartialView("_ShareView", shareModel);
}
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult ShareWorkbook(string emails, string title)
{
var share = new ShareModel
{
Emails = emails
};
// TODO EMAIL THE SHARED WORKBOOK using the 'title' of the workbook and the 'email' string value
// return no content to avoid page refresh
return NoContent();
}
}
This is my partial view/modal dialog (_ShareView):
#using DNAAnalysisCore.Resources
#model DNAAnalysisCore.Models.ShareModel
<!-- Modal -->
<div class="modal fade" id="shareFormModal" role="dialog">
<div class="modal-dialog modal-md">
<!-- Modal content-->
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">Share Workbook - #Model.Title</h4>
</div>
#using (Html.BeginForm("ShareWorkbook", "Home", FormMethod.Post))
{
<div class="modal-body">
<label>#BaseLanguage.Share_workbook_Instruction_text</label>
<div class="form-group">
<textarea class="form-control" asp-for="Emails" rows="4" cols="50" placeholder="#BaseLanguage.ShareDialogPlaceholder"></textarea>
#* TODO add client-side validation using jquery.validate.unobtrusive.js. See US268276 *#
<span asp-validation-for="Emails" class="text-danger"></span>
</div>
<input asp-for="Title" />
</div>
<div class="modal-footer">
<button type="submit" onclick="hideSharingView()" class="btn btn-primary">Share</button>
<button id="btnCancelDialog" type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
</div>
}
</div>
</div>
</div>
#section Scripts {
#{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); }
}
This is my ShareModel:
public class ShareModel
{
[HiddenInput]
public string Title { get; set; }
[Required]
public string Emails { get; set; }
}
The form is not added to the page when the page loads, the unobtrusive validation will not pick it up.A simple solution is to use $.validator.unobtrusive.parse("#id-of-the-form");.Refer to here.
1.Add id to your form in _ShareView partial view:
#using (Html.BeginForm("ShareWorkbook", "Home", FormMethod.Post,new { #id="partialform"}))
2.Introduce validation file _ValidationScriptsPartial.cshtml into main page(Index.cshtml) and manually register the form with the unobtrusive validation.
#section Scripts
{
#await Html.PartialAsync("_ValidationScriptsPartial")
<script type="text/javascript">
function showSharingView(title) {
var url = "#Url.Action("ShowShareDialog", "Home")" + "?workbookTitle=" + encodeURI(title);
$('#shareFormContainer').load(url,
function() {
$('#shareFormModal').modal("show");
$.validator.unobtrusive.parse("#partialform");
});
}
function hideSharingView() {
$('#shareFormModal').modal("hide");
}
</script>
}

Resources