Ajax.BeginForm() with checkbox syntax error - c#

I have an Ajax.BeginForm() in a partial view that contains a CheckBox for a bool value. The model is as follows;
public class ViewBusinessAdd
{
[Required(ErrorMessage="Name must be supplied")]
[Display(Name = "Business Name")]
public string Name { get; set; }
[Required(ErrorMessage = "Contact must be supplied")]
[Display(Name = "Business Contact")]
public string Contact { get; set; }
[Display(Name = "Phone Number")]
public string Number { get; set; }
public string Postcode { get; set; }
public Dictionary<string, string> States { get; set; }
public string AddressRegion { get; set; }
public bool IsFacebookPost { get; set; }
public List<RecommendationViewAttribute> Attributes { get; set; }
}
The CheckBox is rendered using the Html helpers;
<div class="control-group">
<label class="control-label">
#Html.LabelFor(m => m.IsFacebookPost, "Post recommendation")
<img src="~/Content/images/f_logo.png" alt="Facebook" />
</label>
<div class="controls">
#Html.CheckBoxFor(m => m.IsFacebookPost)
</div>
</div>
This produces the following HTML when rendered;
<input data-val="true" data-val-required="The IsFacebookPost field is required." id="IsFacebookPost" name="IsFacebookPost" type="checkbox" value="true" /><input name="IsFacebookPost" type="hidden" value="false" />
When submitting the form with, it produces this error in Chrome;
Uncaught SyntaxError: Unexpected token u
If I remove the CheckBox the form submits without any error. If I convert this to a non-Ajax form, it also submits but that's not going to work with the page design unfortunately.
I'm absolutely stumped on this - I even changed this to a RadioButton and the same behavior exists. Does anyone have any ideas?
Edit: Forgot to add it's a Javascript error.
Edit: The error is coming from jQuery library on the return below;
parseJSON: function( data ) {
// Attempt to parse using the native JSON parser first
if ( window.JSON && window.JSON.parse ) {
return window.JSON.parse( data );
}
But this only happens if I use the Razor HTML helpers to generate the checkboxes.

I got bit by this and burned about 6 hours trying to figure out what the issue was. According to the Jquery developers this is intended behavior in 1.9.1. If you use the jQuery Migrate 1.1.1 plugin everything should work, other than the console warnings which I think can be turned off.
http://bugs.jquery.com/ticket/13412
See the bug report, which isn't actually a bug :)

Well it seems there was a bug introduced with jQuery 1.9.1. I've downgraded to 1.8.3 and the Razor helpers for the Checkboxes now work correctly. Steps to downgrade if anyone is interested;
Uninstall-Package jQuery -force
Install-Package jQuery -version 1.8.3

Related

ASP.NET Core: Validate Individual Dynamic String Input Fields

The App: It is fed an order #, and the part ID, part #, and quantity for each part are displayed (so 3 input fields per line, per part). All those values appear as input fields that can be modified. In the PageModel, I store all those values in Lists and then iterate through them to find ones that have been modified. If I find modifications, I update the appropriate parts of the database. The app is to be used 100% in-house by people who know what they are doing. Everything I mentioned above is working swimmingly. The functionality is there.
The relevant front-end is this:
#{ int index = 0;}
#foreach (var part in Model.PartInfoList)
{
<tr class="row table-bordered">
<td class="col-4">
<input type="hidden" asp-for="OldPartIDList" value="#part.PART_ID" name="OldPartIDList"/> #*This holds the "old" part ID's so the order can still be looked up when the user wants to change the part ID.*#
<input type="text" class="form-control mb-2 mr-sm-2" asp-for="NewPartIDList" value="#part.PART_ID" id="#part.PART_ID" name="NewPartIDList[#index]" onchange="HighlightField(this)" />
#*The id here is used to determine if the field should be highlighted or not.*#
<span class="text-danger" asp-validation-for="NewPartIDList[index]"></span>
</td>
<td class="col-4">
<input type="text" class="form-control mb-2 mr-sm-2" asp-for="PartNumberList" value="#part.PART_NUMBER" id="#part.PART_NUMBER" name="PartNumberList[#index]" onchange="HighlightField(this)" />
#*The id here is used to determine if the field should be highlighted or not.*#
<span class="text-danger" asp-validation-for="PartNumberList[index]"></span>
</td>
<td class="col-4">
<input type="text" class="form-control mb-2 mr-sm-2" asp-for="QuantityList" value="#part.QUANTITY" id="#part.QUANTITY" name="QuantityList[#index]" onchange="HighlightField(this)" />
#*The id here is used to determine if the field should be highlighted or not.*#
<span class="text-danger" asp-validation-for="QuantityList[index]"></span>
</td>
</tr>
index++;
}
The lists are declared as such:
[DisplayName("Part ID"), BindProperty, Required]
//[Range(1, 999999999, ErrorMessage = "Please enter a number between 1 and 999,999,999.")]
[ResubmitSampleCenterOrderValidatorClass(FieldOptions.PART_ID, 1, 999999999, null)]
public List<int> NewPartIDList { get; set; }
[DisplayName("Part Number"), BindProperty, Required]
//[StringLength(48, MinimumLength = 1, ErrorMessage = "Please enter a part number.")]
//[ResubmitSampleCenterOrderValidatorClass(FieldOptions.PART_NUMBER, 1, 999999999, PartNumberArray: PartNumberList.ToArray())]
public List<string> PartNumberList { get; set; }
[DisplayName("Quantity"), BindProperty, Required]
//[Range(1, 99999, ErrorMessage = "Please enter a number between 1 and 99,999.")]
[ResubmitSampleCenterOrderValidatorClass(FieldOptions.QUANTITY, 1, 99999, null)]
public List<int> QuantityList { get; set; }
The comments are what I've been trying. More on that later.
The Problem: Validation, on the part number input fields specifically. I cannot use
[StringLength(48, MinimumLength = 1, ErrorMessage = "Please enter a part number.")]
because the code will try to convert the string list to a string and that obviously can't happen. I just want to:
1) Make sure each part # cannot exceed 48 characters.
2) Have the error show up below that exact input field and NOT the others (This is actually an issue with the part ID and quantity fields as well, but I care more about the part # fields right now.)
Things I've tried:
As you can see from the comments above, I tried normal validation and it did not work for the above reasons. I then decided to go to the custom validation route:
public class ResubmitSampleCenterOrderValidatorClass: ValidationAttribute
{
public FieldOptions FieldDescriptor { get; set; }
public int MinLength { get; set; }
public int MaxLength { get; set; }
public List<string> PartNumberList { get; set; }
public string[] PartNumberArray { get; set; }
public ResubmitSampleCenterOrderValidatorClass(FieldOptions FieldDescriptor, int MinLength, int MaxLength, string[] PartNumberArray)
{
this.FieldDescriptor = FieldDescriptor;
this.MinLength = MinLength;
this.MaxLength = MaxLength;
this.PartNumberArray = PartNumberArray;
PartNumberList = new List<string>();
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
switch(FieldDescriptor)
{
case FieldOptions.PART_NUMBER:
break;
}
return null;
}
}
FieldOptions is just an enum, of which PART_NUMBER is 1. Anyways, I couldn't find a way to pass the string list into the custom validator because it isn't and cannot be static. I also couldn't find a way to get my numbers out of value for the part ID and quantity, but that's a separate issue.
I also put indexes on the name attributes for my input fields on the front end, hoping that could eventually lead to a solution. It was a trick I used in a JS/jQuery app I wrote a few years ago to do something very similar. Is there a way to access specific elements from the back-end, so I could just iterate through each one of them and validate them myself?
I have googled and googled, but I can't find anyone else with this issue. Maybe I haven't found the magic search words yet. I feel that if my part # validation woes are solved, I can use that solution on the problems I'm having with the other two fields.
All ideas are welcome! Let me know if you need more clarification! Thank you and have a fantastic day!
EDIT: More information on the syntax error I get when I try to pass
public List<string> PartNumberList { get; set; }
to the custom validator class. The class itself is found above. The syntax error reads:
An object reference is required for the non-static field, method, or property 'ReviewOrderModel.PartNumberList'.

Date formats in input forms - C#

I've been following this tutorial to learn ASP.NET Core MVC with Entity Framework. My problem is that when I run my solution and try to add a new entry on the students table using my browser, it automatically imposes a date format in the form of MM/dd/yyyy, as shown here. I want it in the format yyyy-MM-dd.
My Student model is the following:
public class Student
{
public int StudentID { get; set; }
[Required]
[Display(Name = "Last Name")]
public string LastName { get; set; }
[Required]
[Display(Name = "First Name")]
public string FirstMidName { get; set; }
[Display(Name = "Enrollment Date")]
[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
public DateTime EnrollmentDate { get; set; }
public ICollection<Enrollment> Enrollments { get; set; }
}
And in my view, the date input is implemented as follows:
<div class="form-group">
<label asp-for="EnrollmentDate" class="control-label"></label>
<input asp-for="EnrollmentDate" class="form-control" />
<span asp-validation-for="EnrollmentDate" class="text-danger"></span>
</div>
What's even weirder is that the specified format works outside input forms, such as here, in the index page where it shows every student in the table.
I can't seem to find a solution for this problem anywhere, most people just say to add ApplyFormatInEditMode = true on the data annotation and it should work.
Any help is appreciated, thanks in advance!
This problem occurs because of the [DataType(DataType.Date)] declaration on your EnrollmentDate. When the page is rendered, this element is converted to
<input type="date">
When the browser sees this it will impose its own internal datepicker on the input element if it supports it. Here's a nice link to all the information on the Mozilla site.
One problem with this is every browser has its own styling/conventions, which makes for a different user experience across platforms.
You can write a tiny piece of JavaScript to overwrite the data type on the client and replace the browser datepicker with a common one. This example just
$("input[type=date]")
.datepicker({ dateFormat: "yy-m-d", changeYear: true, yearRange: "-4:+0" })
.attr("type", "text");
Note how in the last line we change the type attribute to text.
And one issue is that it relies on the view model date formatting for the initial load and the javascript to format when using the datapicker.
Sean T pointed out that a solution already existed here.
Apparently this has nothing to do with C# or anything, it has to do with the way browsers handle date input and try to use a format that the user is more familiar with.
So, to change this behavior on Google Chrome (the browser I use), I followed these steps.
For reference, here are instructions on how to do it in Firefox. Couldn't find for Opera, and Edge seems to either not have a way to change this, or it's bound with windows' regional settings (see this).

Require field if checkbox is checked

I have a field in a form that I need to be required if a checkbox in the same form is checked.
There doesn't seem to be a way to do this with regular model validation, so I created a remote validation method in my controller.
The problem is, since the field isn't required always, the validation doesn't even fire if I put it on the field. So I tried putting the validation on the checkbox, and now I get a different problem where the validation doesn't fire when I add text to the field.
Is there a way to do what I'm needing with custom validation, or do I need to do something in JavaScript? If so, what do I need to do?
Form:
<form>
<input type="checkbox" asp-for="NotRecommended" checked=#Model.NotRecommended /> <label>Not Recommended</label>
<textarea class="form-control" id="Notes" asp-for="Notes"></textarea>
<span asp-validation-for="NotRecommended" class="text-danger"></span>
</form>
Model:
public class DetailsViewModel
{
[DisplayName("Not Recommended")]
[Remote("RequireNoteForFlag", AdditionalFields = "Notes", ErrorMessage = "Note is required when flagging someone.")]
public bool NotRecommended { get; set; }
[DataType(DataType.MultilineText)]
[MaxLength(1500)]
[DisplayName("Notes")]
public string Notes { get; set; }
}
Remote Validator
public IActionResult RequireNoteForFlag(bool NotRecommended, string Notes)
{
if (NotRecommended && String.IsNullOrWhiteSpace(Notes)) return Json("Note is required when flagging an Assessor");
else return Json(true);
}
Joe gave me pretty much the whole answer in the comments, but hasn't posted it as an answer, so I'll post it for anyone else who might need to do this.
Create the Attribute
https://github.com/joeaudette/cloudscribe/blob/master/src/cloudscribe.Web.Common/DataAnnotations/RequiredWhenAttribute.cs
Joe's Client-Side Validation
https://github.com/cloudscribe/cloudscribe/blob/master/src/cloudscribe.Web.StaticFiles/js/cloudscribe-validation-requiredwhen.js
Modified Client-Side Validation
jQuery.validator.addMethod("requiredwhen", function (value, element, param) {
var otherPropId = $(element).data('val-other');
if (otherPropId) {
var otherProp = $(otherPropId)[0].checked;
if (otherProp) {
return element.value.length > 0;
}
}
return true;
});
jQuery.validator.unobtrusive.adapters.addBool("requiredwhen");
View
<input type="checkbox" asp-for="NotRecommended" /> <label asp-for="NotRecommended"></label>
<textarea class="form-control" id="Notes" asp-for="Notes"></textarea>
<span asp-validation-for="NotRecommended" class="text-danger"></span>
Model
[DisplayName("Not Recommended")]
public bool NotRecommended { get; set; }
[DataType(DataType.MultilineText)]
[MaxLength(1500)]
[DisplayName("Notes")]
[RequiredWhen("NotRecommended", true, AllowEmptyStrings = false, ErrorMessage ="A note is required when flagging an Assessor.")]
public string Notes { get; set; }
I have implemented this with a custom validator in a similar way. However I applied the annotation to the field that would be required and included a condition in the arguments, eg:
[RequiredIf("NotRecommended = true")]
public string Notes { get; set; }
But, as you may already be aware, there is no way to cross-reference another property in data annotations so you will need to build in the javascript to handle this.
Unobtrusive validation adds a data-val="true" and data-val-required="error message" attribute to the input element so adding this in via javascript when the checkbox is checked will work.
You will also need to reinitialise the form validation so that the inputs with newly applied validation attributes are added.

Performing client-side validation in MVC4 View against ViewModel

I would like to know what is the best approach to handle client-side, javascript or jQuery driven validation of MVC4 fields against attributes placed on a ViewModel's fields.
First, let's pick the example. A login creation screen for Administrators shown the first time the application starts (just not to say to the site owner "use admin/admin as login the first time").
ViewModel:
public class AdministratorViewModel : AbstractViewModel
{
[Required]
[Display(ResourceType = typeof(ManageAdminsViewModelResources), Name = "lblUsername")]
public string Username { get; set; }
[Required]
[Display(ResourceType = typeof(ManageAdminsViewModelResources), Name = "lblEmailAddress")]
[EmailAddress]
public string EmailAddress { get; set; }
[Required]
[Display(ResourceType = typeof(ManageAdminsViewModelResources), Name = "lblPassword")]
[AdminPassword]
public string Password { get; set; }
[Required]
[Display(ResourceType = typeof(ManageAdminsViewModelResources), Name = "lblPasswordConfirm")]
[Compare("Password")]
public string PasswordConfirm { get; set; }
[Display(ResourceType = typeof(ManageAdminsViewModelResources), Name = "lblLastLogin")]
public DateTime? LastLogin { get; set; }
[Display(ResourceType = typeof(ManageAdminsViewModelResources), Name = "lblPasswordExpiry")]
public DateTime? PasswordExpiry { get; set; }
[Display(ResourceType = typeof(ManageAdminsViewModelResources), Name = "lblBlocked")]
public bool Blocked { get; set; }
}
Partial view (only a few fields needed when creating the first admin)
#using (Html.BeginForm())
{
#Html.ValidationSummary(false)
#Html.AntiForgeryToken()
<fieldset>
<legend>#ManageAdminsViewResources.legendCreateAdmin</legend>
<div class="desktoptile">
#Html.LabelFor(m=>m.Username)
#Html.EditorFor(m => m.Username)
#Html.ValidationMessageFor(m => m.Username)
</div>
<div class="desktoptile">
#Html.LabelFor(m=>m.Password)
#Html.PasswordFor(m => m.Password)
#Html.ValidationMessageFor(m => m.Password)
</div>
<div class="desktoptile">
#Html.LabelFor(m=>m.PasswordConfirm)
#Html.PasswordFor(m => m.PasswordConfirm)
#Html.ValidationMessageFor(m => m.PasswordConfirm)
</div>
<div class="desktoptile">
#Html.LabelFor(m=>m.EmailAddress)
#Html.EditorFor(m => m.EmailAddress)
#Html.ValidationMessageFor(m => m.EmailAddress)
</div>
<input type="submit" value="#ManageAdminsViewResources.btnCreate"/>
</fieldset>
}
Controller
[AllowAnonymous]
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult CreateFirst(AdministratorViewModel viewModel)
{
if (!ModelState.IsValid) return View(viewModel);
[...................]
Currently
If I enter an invalid email address, an empty password, etc. in the form and hit Submit I'm correctly notified of the errors. Ok, let's go on
What I want
Since I'm doing a Metro-stylish design, I would like that every time the user unfocuses a text box validation for that field occurs.
Writing hard-coded jQuery fragments is not the best option. I would like a data-driven approach, possibly embedded in MVC4 which I'm currently learning.
So, given a ViewModel with standard and custom attributes (for which, no matter what, a little Javascript is required, think about the Org.Zighinetto.AdminPasswordAttribute that checks password complexity), how do I enforce client-side validation the most unobtrusive way, without specifying client-side tags on each and every html tag and writing the least possible amount of code?
Is still there any secret in ASP.NET MVC 4 validation that I have to unhide?
Well, you would have to invoke jQuery validate using jQuery (because it's written in jQuery :))
You could add a global event for your inputs, then invoke it on the blurred element. Something like:
$("input").blur(function () {
$(this).valid();
});
From my (learning) point of view, the correct answer should be:
Explanation:
Validation of common attributes is done by MVC4 automatically when jQuery Unobtrustive Validator is loaded into the page, otherwise only server-side validation is performed.
Most, if not all of MVC4 ValidationAttributes implement IClientValidation. This interface wraps jQuery Validator validation functions in server-side code. It's hard to explain how exactly it works, but saying that this interface returns the name of the client-side function (either provided by jQuery distribution or implemented by user), while basically wrong at least gives the idea to a novice user trying to understand how validation works.
Answer:
Continue using data-driven model/viewmodel annotations.
Check if NuGet package jQuery Unobtrusive Validation is loaded in the page, then implement IClientValidation as needed (I found a tutorial here about multiple errors), fields are validated automatically.

fluentvalidation does not show error or update Modelstate

This is my code I have setup with the latest fluent validation library.
public class ReleaseViewModelValidator : AbstractValidator<ReleaseViewModel>
{
public ReleaseViewModelValidator() {
RuleFor(r => r.Name).NotEmpty().Length(1, 30).WithMessage("Name must not be longer than 30 chars.");
}
}
[FluentValidation.Attributes.Validator(typeof(ReleaseViewModel))]
public class ReleaseViewModel { public int ReleaseId { get; set; }
[Remote("ReleaseExists", "Release", ErrorMessage = "This name already exists.")]
public string Name { get; set; }
public DateTime CreatedAt { get; set; } }
GlOBAL.ASAX:
FluentValidationModelValidatorProvider.Configure();
VIEW:
model ILMD.Web.Models.ReleaseViewModel #* Remote Validation*# <script src="#Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
#using (Html.BeginForm("Create", "Release"))
{
<p class="editor-label">#Html.LabelFor(model => model.Name)</p>
<p class="editor-field">#Html.EditorFor(model => model.Name)</p>
<p class="editor-field">#Html.ValidationMessageFor(model => model.Name)</p>
}
All my unit tests for the ReleaseViewModelValidator show green light. Fine.
But less cool is that entering some live data like 31 chars or entering nothing I do not see any client side error message.
Do I still have to include something in my partial view? The ModelState is also not correct its always true.
Try this
[FluentValidation.Attributes.Validator(typeof(ReleaseViewModelValidator))]
Because in the FluentValidation attributes validator you have specify the actual validator (ReleaseViewModelValidator) not the ReleaseViewModel

Categories

Resources