Working with multiple html helpers of same name in mvc4 - asp.net-mvc-4

I have multiple html helpers. When I click login button I am disabling user_register div using jquery and when I enter details the username and password the model binder is able to bind properly but when I click Register I am disabling user_login div and enabling user_register div and when I enter the details only email and firstname is what the model binder is able to bind and not username, password. Is this becoz I am using the same html helpers more than once. How to handle this. I have the following code
<div class="user_login">
<label>Email / Username</label>
#Html.TextBoxFor(model => model.Email)
<br />
<label>Password</label>
#Html.TextBoxFor(model => model.Password)
<br />
<div class="action_btns">
<div class="one_half"><i class="fa fa-angle-double-left"></i>Back
</div>
<div class="one_half last">
<input type="submit" style="border: none" name="action" class="btn btn_red" value="Login" />
</div>
</div>
Forgot password?
</div>
<div class="user_register">
<label>Full Name</label>
#Html.TextBoxFor(model => model.First_Name)<br />
<label>Email Address</label>
#Html.TextBox(model=>model.Email)<br />
<label>User Name</label>
#Html.TextBoxFor(model => model.User_Name)<br />
<label>Password</label>
#Html.TextBox(model=>model.Password") <br />
</div>
The controller follows here
public ActionResult Index(Customer customer, string Action)
{
//something here
}

You have not shown you complete view, but assuming all those controls are generated inside one form element, then all controls will post back unless you make the inputs disabled. If so, then the first input with name="Email" is bound and the second one is ignored by the DefaultModelBinder. (since the first one is only hidden, its value is probably empty). Your script needs to ensure that the 2 inputs in the login section are disabled, for example
#Html.TextBoxFor(model => model.Email, new { id = "login-email" })
and
$('#login-email').prop('disabled', true);
Note the id attribute so you can select the correct one (and prevent duplicate id's which is invalid html.
An alternative would be to create 2 separate forms.

Related

Generating a valid __RequestVerificationToken from C#

One of the most popular books on ASP.NET Core is "Pro ASP.NET Core 3" by Adam Freeman.
In chapters 7-11, he builds an example application, SportsStore.
As you can see, each product in the listing gets its own 'Add To Cart' button:
If we do 'view source' on this page, we'll see the following HTML for that item in the product list:
<div class="card card-outline-primary m-1 p-1">
<div class="bg-faded p-1">
<h4>
Kayak
<span class="badge badge-pill badge-primary" style="float:right">
<small>$275.00</small>
</span>
</h4>
</div>
<form id="1" method="post" action="/Cart">
<input type="hidden" data-val="true" data-val-required="The ID field is required." id="ID" name="ID" value="1" />
<input type="hidden" name="returnUrl" value="/" />
<span class="card-text p-1">
A boat for one person
<button type="submit" class="btn btn-success btn-sm pull-right" style="float:right">
Add To Cart
</button>
</span>
<input name="__RequestVerificationToken" type="hidden" value="CfDJ8KKqNOS0gwdMvC0-bdjTwWlvCcBJldeidwIX5b2f24gYblS9X1sqCwJWIEsKKOSf8kut0SQsQRLF3R1XBSYZkPGnta9YzRK4tcQl8dq_0uWmjeUhm8yMe90fWDt_x0smmAD1lmb9-BxQF8y_7-IQSz4" /></form>
</div>
Note the input tag towards the bottom:
<input name="__RequestVerificationToken" type="hidden" value="CfDJ8KKqNOS0gwdMvC0-bdjTwWlvCcBJldeidwIX5b2f24gYblS9X1sqCwJWIEsKKOSf8kut0SQsQRLF3R1XBSYZkPGnta9YzRK4tcQl8dq_0uWmjeUhm8yMe90fWDt_x0smmAD1lmb9-BxQF8y_7-IQSz4" />
If we look at the Views\Shared\ProductSummary.cshtml file in the SportsStore project, we'll see the code that is involved with generating these listing items:
#model Product
<div class="card card-outline-primary m-1 p-1">
<div class="bg-faded p-1">
<h4>
#Model.Name
<span class="badge badge-pill badge-primary" style="float:right">
<small>#Model.Price.ToString("c")</small>
</span>
</h4>
</div>
<form id="#Model.ID" asp-page="/Cart" method="post">
<input type="hidden" asp-for="ID" />
<input type="hidden" name="returnUrl" value="#ViewContext.HttpContext.Request.PathAndQuery()" />
<span class="card-text p-1">
#Model.Description
<button type="submit" class="btn btn-success btn-sm pull-right" style="float:right">
Add To Cart
</button>
</span>
</form>
</div>
As you can see, the form element in this case doesn't have an explicit inclusion of the input tag with the __RequestVerificationToken value. This form thus appears to be a tag helper which takes care of generting the input tag with the __RequestVerificationToken token.
As an experiment, let's suppose I have added the following method to Controllers\HomeController:
[HttpGet]
public ContentResult ButtonExample()
{
var token = "...";
return new ContentResult()
{
ContentType = "text/html",
StatusCode = (int)HttpStatusCode.OK,
Content =
String.Format(
#"<!DOCTYPE html>
<html>
<body>
<form id=""1"" method=""post"" action=""/Cart"">
<input type=""hidden"" data-val=""true"" id=""ID"" name=""ID"" value=""1"" />
<button type=""submit"">Add to Cart</button>
</form>
<input name=""__RequestVerificationToken"" type=""hidden"" value=""{0}"" />
</body>
</html>",
token)
};
}
As you can see, this generates a very simple page with a single button which is intended to add the product with ID value 1 (i.e. the Kayak) to the cart.
I of course need to pass an appropriate value for the __RequestVerificationToken.
My question is, is there a way to get this value from C# so that I can include it in the method above?
The idea as shown above would be to set the token value here:
var token = "...";
This is then interpolated into the string that generates the HTML using String.Format.
UPDATE
This page mentions the following:
To generate the anti-XSRF tokens, call the #Html.AntiForgeryToken method from an MVC view or #AntiForgery.GetHtml() from a Razor page.
So I guess the question is, how do we do the equivalent from C# directly instead of from an MVC view or Razor page?
You can add the below code to your form which will generate the __RequestVerificationToken. It is used to prevent CSRF attacks Prevent XSRF/CSRF attacks.
<form action="/" method="post">
#Html.AntiForgeryToken()
</form>

Razor tag helper bool Radio buttons clientside 'Required' validation

I am using Razor Pages Tag helpers for a Yes/No in a form on a page. All other fields in the form have client side unobtrusive validation, but the bool? Yes/No does not. I have a few radio Yes/No's like this, how do I get the client side to work for them?
[Required]
public bool? Have7DaysWorth { get; set; }
I tried moving away from tag helpers too, butit doesn't hook up with this:
<label asp-for="Have7DaysWorth" class="control-label"></label>
<div>
<input type="radio" id="daysRadio1" name="Have7DaysWorth "
checked="#(Have7DaysWorth == true ? "checked": null)"
class="custom-control-input" value="true">
<label class="custom-control-label" for="daysRadio1">Yes</label>
</div>
<div>
<input type="radio" id="pharmacyRadio2" name="Have7DaysWorth "
checked="#(Have7DaysWorth == false ? "checked": null)"
class="custom-control-input" value="false">
<label class="custom-control-label" for="daysRadio1">No</label>
</div>
<span asp-validation-for="Have7DaysWorth " class="text-danger"></span>
I know from searching there are some suggestions about pre-selecting one, but that isn't then a conscious value that a user has entered into a form, so not an option here.
I have tried some other ways, but they seemed to lose the value when the modelstate wasn't valid and was returned.
How do I get the client side to work for bool radios in the expected way that I want?
You're not using asp-for on the radios, so it has no idea it should be required, as there's no involvement with the model, and thus the Required attribute on that property.
An old thread but if you're having to upgrade a razor page website to Bootstrap 5 you'll run into this issue.
This will not work as you cannot use asp-for with C# statement not inside an attribute
<input asp-for="MarketingPrefs" type="radio" class="btn-check" value="true" #(Model.MarketingPrefs.HasValue && Model.MarketingPrefs.Value ? "checked" : null)>
If you omit the asp-for like this then local validation won't work
<input id="MarketingPrefsYes" name="MarketingPrefs" type="radio" class="btn-check" value="true" #(Model.MarketingPrefs.HasValue && Model.MarketingPrefs.Value ? "checked" : null)>
This is how to do it
<div class="btn-group" role="group">
#Html.RadioButtonFor(model => model.MarketingPrefs, "true", new { #class = "btn-check", #id="MarketingPrefsYes" })
<label class="btn btn-outline-secondary" for="MarketingPrefsYes">Yes</label>
#Html.RadioButtonFor(model => model.MarketingPrefs, "false", new { #class = "btn-check", #id="MarketingPrefsNo" })
<label class="btn btn-outline-secondary" for="MarketingPrefsNo">No</label>
</div>

ASP.NET Core 2.2. Razor Pages - How to populate a form control based on another field

I have a form control "ConnectorType" which I turned into a dropdown list with pre-defined values (just 3qty currently)
When the user selects and item from this dropdown list, depending on the value selected I then want to populate another text box form control underneath.
To better explain, please see image below:
Example, if TCP Server IN is selected then the form control underneath (textbox)should automatically say "Inbound"
Ideally this text box should also have an attribute/configuration that prevents the user from entering their own text, grayed out perhaps. Once the create form is submitted, the textbox that contains this value "Inbound" will then be added to the SQL Table using Enitity Framework.
The solution requires that this field dynamically changes each time a new item is selected from the list.
Current code for the drop down list:
Page Model Class:
public IEnumerable<SelectListItem> ConnectorTypeList { get; private set; } // temp
public IActionResult OnGet()
{
// prepare the list in here
ConnectorTypeList = new SelectListItem[]
{
new SelectListItem ("TCP Server IN", "TCP Server IN"),
new SelectListItem ("TCP Server OUT", "TCP Server OUT"),
new SelectListItem ("SMTP Server IN", "SMTP Server IN")
};
return Page();
}
Page View:
<div class="form-group">
<label asp-for="ConnectorModel.ConnectorType" class="control-label"></label>
<select asp-for="ConnectorModel.ConnectorType" class="form-control" asp-items="#Model.ConnectorTypeList"></select>
<span asp-validation-for="ConnectorModel.ConnectorType" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="ConnectorModel.DataFlow" class="control-label"></label>
<input asp-for="ConnectorModel.DataFlow" class="form-control" />
<span asp-validation-for="ConnectorModel.DataFlow" class="text-danger"></span>
</div>
Note the current form-control I'm wanting to modify is the "ConnectorModel.DataFlow" in the above page view code. At the moment it's just a simple textbox that the user can enter their own choice of text.
I'm going round in circles having read up on page handlers etc. It seems there is a onchange event but unsure how to implement this and somehow link it back to the page model class, run a method then postback the result. I'm not looking for a JQuery script as it seems this should not be required in the newer framework, not sure I just don't want a complicated long solution given I will be using a lot of these throughout the app. Thanks in advance...
The easiest way is to use onchange() on your <select> tag and assign data to input using js.(Add id attribute for <select> and <input> before)
If you would like to prevent the user from entering their own text, just use readonly attribute for you input.
<input asp-for="DataFlow" id="dataFlow" class="form-control" readonly/>
The Sample Page View:
<div class="row">
<div class="col-md-4">
<form method="post">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="ConnectorModel.ConnectorType" class="control-label"></label>
<select asp-for="ConnectorModel.ConnectorType" id="connectorTypeList" class="form-control" asp-items="#Model.ConnectorTypeList" onchange="assignData()">
<option>Select ConnectorType</option>
</select>
<span asp-validation-for="ConnectorModel.ConnectorType" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="ConnectorModel.DataFlow" class="control-label"></label>
<input asp-for="ConnectorModel.DataFlow" id="dataFlow" class="form-control" readonly />
<span asp-validation-for="ConnectorModel.DataFlow" class="text-danger"></span>
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-primary" />
</div>
</form>
</div>
</div>
#section Scripts{
<script>
function assignData() {
var contentType = $("#connectorTypeList").val();
if (contentType == "TCP Server IN") {
$("#dataFlow").val("Inbound");
}
}
</script>
}

Razor Pages - Run a server side method NOT using post

In Razor pages ASP.NET Core, how do I do a basic onclick event for a button which is of type button?
Do I need to wire up an AJAX GET request to get the below "Resend Code" button to work? There is plenty of chatter about OnPost this and that.. but I don't want to post.
Can't be this hard?
<form method="post">
<div asp-validation-summary="All"></div>
<div class="form-group-item">
<label asp-for="Input.TwoFactorCode"></label>
<input asp-for="Input.TwoFactorCode" class="input" />
<span asp-validation-for="Input.TwoFactorCode"></span>
</div>
<div class="form-group-item">
<label class="margin-0" asp-for="Input.RememberMachine">
<input asp-for="Input.RememberMachine" />
#Html.DisplayNameFor(m => m.Input.RememberMachine)
</label>
</div>
<div>
<button type="button" asp-page-handler="ResendCode" class="btn btn-light">Resend Code</button>
<button type="submit" class="btn btn-secondary">Confirm</button>
</div>
</form>
As it stands, the button won't do anything. You can use JavaScript to intercept the button click and then fire a get request using AJAX (jQuery example below):
$('.btn.btn-light').on('click', function(){
$.get('?handler=ResendCode', data, function(){
...
});
});
You can try changing the button to use formmethod="get":
<button type="submit" formmethod="get" asp-page-handler="ResendCode" class="btn btn-light">Resend Code</button>
Note, this will only work for buttons that have type="submit" or type="image" (other type-values don't cause the form to submit). Also it's an HTML5 attribute.
Reference:
https://html.com/attributes/input-formmethod/
https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button#attr-formmethod
maybe try making your own custom handler methods other than OnPost() and OnGet()

How to reset reactive form in angular 5 using material form field

I need to reset my form to my original state after submitting the form. I'm using material form fields in my FormGroup to create the input fields. So, when i call reset method from formgroup, the form doesn't revert back to its original state, instead it validates make all the fields to invalid state.
My form template
<form [formGroup]="createUserForm" (ngSubmit)="createUser()" novalidate class="add_user_form normal_form">
<div class="form_input_field">
<i class="fas fa-user"></i>
<mat-form-field>
<input formControlName="uname" matInput placeholder="UserName*">
<mat-error *ngIf="createUserForm.controls['uname'].hasError('required') && formSubmitted">
Please enter your username
</mat-error>
</mat-form-field>
</div>
<div class="form_button_field">
<button mat-raised-button class="form_btn_submit" color="primary">Submit</button>
<button mat-raised-button type="reset" class="form_btn_cancel" color="warn">Cancel</button>
</div>
</form>
My Submit function
createUser() {
if(this.createUserForm.valid) {
this.createUserForm.reset();
}
}
How to bring back the form to original state after resetting?