I'm using "Play 2.0"-Framework (v. 2.0.1) and running into some troubles with form validation of a date value.
Peace of my Model code:
#Entity
public class Appointment extends Model {
public Date start;
public Date end;
}
Peace of my template code:
<input type="text" id="start" name="start" placeholder="yyyy-mm-dd" />
<!-- ALSO tested with chrome beta v. 20 with html5 support-->
<input type="date" id="end" name="end" placeholder="yyyy-mm-dd" />
My Controller:
public class Appointment extends Controller {
static Form<Appointment> appointmentForm = form(Appointment.class);
//on calling form page
public static Result create() {
return ok(create.render("create", appointmentForm));
}
//called on saving form data
public static Result save() {
Form<Appointment> filledForm = appointmentForm.bindFromRequest();
if (filledForm.hasErrors()) {
return badRequest(
create.render("create", filledForm)
);
} else {
Appointment.create(filledForm.get());
return redirect(routes.Appointment.index());
}
}
}
If I select a date via jquery ui datepicker or type in myself in a format like "yyyy-mm-dd" or doesn't matter, but must be correct format I run into a validation error in my controller save()-method with the check "filledForm.hasErrors()" and the error message "wrong date format".
I thought it would be converted automatically from play, so that I don't have to add a convertion by my self. What can I do to solve this problem? Is it still an issue from play 2.0?
Thanky you.
Cheers,
Marco
I think you must define the format of the date. See Play Framework 2.0: Custom formatters for custom formatters. Perhaps a custom binder is necessary see https://groups.google.com/d/topic/play-framework/Xi2xAiCnINs/discussion
Just as some hints to give you a direction. Hopefully someone can give you a better answer.
Related
i need to use check box to fire off a java if statement without having to bind the check box to any entity or object here is what i need in code
<input type="checkbox" th:field="${deleteImages}" th:checked="*{false}">Delete all images</input>
the controller would be this
public String updateProduct(#ModelAttribute("product") Product product,
#ModelAttribute("deleteImages") Boolean deleteImages) {
if (deleteImages) {
imageService.deleteImages(savedProduct.getId());
}
i can not figure out how to fix this checkbox.. i don't need it tied up to any object just return true when clicked and false when unchecked
If you want to do this without binding it to an object, just use RequestParams. Like this:
Form:
<form method="POST" class="action-buttons-fixed">
<input type="checkbox" name="deleteImages" />
<button>Go</button>
</form>
Java:
#PostMapping("/whatever")
public String updateProduct(#RequestParam(required = false, defaultValue = "false") boolean deleteImages) {
if (deleteImages) {
imageService.deleteImages(savedProduct.getId());
}
}
You can't, however, mix bound and unbound properties on the same form. If you want to do that, you should just create a new Form object that contains the both the product and the deleteImages variable.
class Form {
private Product product;
private boolean deleteImages;
}
And then change your form bindings appropriately.
I use the following to do validations on a form in blazor(server)
<EditForm Model="#place" OnValidSubmit="HandleValidSubmit" Context="placesEdit" >
<DataAnnotationsValidator />
<InputText #bind-Value="place.Name"/>
<ValidationMessage For="() => place.Name" />
</EditForm>
#{place=new Place(); }
the property Name as a [required] - Attribute. This works fine. When submitting the form I see the error message and the HandleValidSubmit isn't called
But when I try to do the same with a List the validation isn't happening. No error is displayed and HandleValidSubmit is called instead even if the requirements are not met:
<EditForm Model="#places" OnValidSubmit="HandleValidSubmit" Context="placesEdit" >
<DataAnnotationsValidator />
#foreach(var place in places) {
<InputText #bind-Value="place.Name"/>
<ValidationMessage For="() => place.Name" />
}
</EditForm>
#{places=new List<Place>(); }
What has do be done that the Validator also works in the loop?
Try if this helps:
Add the Microsoft.AspNetCore.Components.DataAnnotations.Validation NuGet package.
This is a pre-release package and latest version is 3.2.0-rc1.20223.4. There is a plan to include this on the native Blazor SDK but that version should work up to .NET 5. https://github.com/dotnet/aspnetcore/issues/22238#issuecomment-634266426
Replace your DataAnnotationsValidator with ObjectGraphDataAnnotationsValidator
You can check if your issue gets resolved with Step 2. If not, continue to Step 3.
Annotate your list property with ValidateComplexType.
You will need to create a class that will contain your list property.
check the docs for more information: https://learn.microsoft.com/en-us/aspnet/core/blazor/forms-validation#nested-models-collection-types-and-complex-types
If you're using IValidatableObject like me, the above solution won't work. The workaround is to create another property to link the validation into.
e.g.
public class MyModel : IValidatableObject
{
public List<Place> Places { get; } = new List<Place>();
public object PlacesValidation { get; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
var isValid = ...;
if (isValid)
yield return new ValidationResult("Places is invalid.", new[] { nameof(PlacesValidation ) });
}
}
<ValidationMessage For="() => Model.PlacesValidation"/>
I'm trying to implement a custom client side validation, but it is not working. I'm basing myself on the article on Codeproject http://www.codeproject.com/Articles/275056/Custom-Client-Side-Validation-in-ASP-NET-MVC3
I also looked here on SO, but I think I'm implementing it in the correct manner, but I'm overlooking something.
My goal is to validate a date (required, date format and not earlier than another date on the form). The first two can be done with data annotations, the last I have to do with custom validation.
I have on my base class some dataannotations (ClassLibrary is in VB.NET):
Imports System.ComponentModel
Imports System.ComponentModel.DataAnnotations
<MetadataType(GetType(CM_CONTRACTVALIDATIONData))>
Partial Public Class CM_CONTRACTACTIVATION
'...
End Class
Public Class CM_CONTRACTVALIDATIONdata
'...
<DataType(DataType.Date)>
<Required()>
Public Property TakeBackDeviceWhen
'..
End Class
In the javascript file I have added the custom method:
//validation
$.validator.addMethod("checkPickupDate", function (value, element) {
return false;
});
$("#form").validate({
rules: {
TakeBackDeviceWhen: {
checkPickupDate: true
}
},
messages: {
TakeBackDeviceWhen: {
checkPickupDate: "Test"
}
}
}
);
My chtml file is as follow:
#Html.TextBox("TakeBackDeviceWhen", Model.TakeBackDeviceWhen.HasValue ? Model.TakeBackDeviceWhen.Value.ToShortDateString() : "", new { style = "Width: 200px" })
The resulting HTML is as follow:
<input id="TakeBackDeviceWhen" class="hasDatepicker" type="text" value="" style="Width: 200px" name="TakeBackDeviceWhen" data-val-required="The TakeBackDeviceWhen field is required." data-val="true">
It seems that neither my type validation and my custom validation isn't implemented.
What is going wrong?
OK, solved it. I hope :-)
What did I learned today:
(1) Don't use EditorFor: when you scaffold it from a MVC template, input fields are generated to EditorFor, it seems that you can't add custom unobtrusive validation tags. So, I was trying to get this fixed, untill I changed it to TextBoxFor.
(2) You can add custom validation methods in jQuery, but you can't mix them with unobtrusive validation. After adding a custom method, you have to also add it to the unobtrusive adapters. And don't forget to add jQuery on the bottom :-s (I got this from jQuery.validator.unobtrusive.adapters.addMinMax round trips, doesn't work in MVC3)
$(function () {
$.validator.addMethod("checkpickupdate", function (value, element) {
if (value == "20/09/2012") {
return false;
} else {
return true;
}
});
$.validator.unobtrusive.adapters.addBool("checkpickupdate");
} (jQuery));
(3) Add validation tags to the input field in the htmlAttributes:
#Html.TextBox("TakeBackDeviceWhen", Model.TakeBackDeviceWhen.HasValue ? Model.TakeBackDeviceWhen.Value.ToShortDateString() : "",
new {
style = "Width: 200px",
data_val = "true",
data_val_required = "verplicht!",
data_val_date = "moet datum zijn",
data_val_checkpickupdate = "wow"
})
(4) Datatype data annotations will not enforce a validation. You have to add it like in (3). You can add a custom ValidationAttribute like (for server side validation):
public class MustBeDateAttribute : ValidationAttribute {
public override bool IsValid(object value) {
try
{
DateTime dte = DateTime.Parse(value.ToString());
return true;
}
catch (Exception)
{
return false;
throw;
}
}
}
And this is the resulting html output:
<input type="text" value="" style="Width: 200px" name="TakeBackDeviceWhen" id="TakeBackDeviceWhen" data-val-required="required!" data-val-date="has to be a date" data-val-checkpickupdate="custom error" data-val="true" class="hasDatepicker valid">
As I'm using my ClassLibrary in different projects, I'm now going to try to seperate the dataannotations meta data from the class library (maybe with dependency resolver).
I've created a fairly straight forward page with a check box:
#using (Html.BeginForm("MyController", "MyAction", FormMethod.Get))
{
#Html.CheckBoxFor(x => x.MyCheckBox)
<input type="submit" value="Go!" />
}
The URL is populated with the MyCheckBox value twice!? As such:
MyAction?MyCheckBox=true&MyCheckBox=false
It only duplicates the value if the check box is true. If set to false it will only appear once in the query string.
The code above is simplified as I have a couple of drop downs and a textbox on the form which work fine. I don't think there's anything unusual about the code which I've left out from this question.
Has anyone had a similar issue with query string parameters being duplicated?
This behaviour is by design of the checkbox control. The standard HTML checkbox control passes no value if it is not checked. This is unintuitive. Instead, the ASP.Net checkbox control has 2 elements, the standard control which is visible and also a hidden control with a value of 'False'.
Therefore, if the checkbox is not checked, there will be one value passed: False.
If it is checked, there will be two values, True and False. You therefore need to use the following code to check for validity in your code:
bool checkboxChecked = Request.QueryString["MyCheckBox"].Contains("True");
Accepted answer is correct however in my case in a recent development the MVC behaviour is misleading.
The MVC Html.CheckBox(...) and Html.CheckBoxFor(...) generate an extra input of 'type=hidden' with the same ID as the checkbox control, leading to the duplicate URL parameters. I got around this problem by simply including the mark up desired as follows:
#if(checkTrue){
<input type="checkbox" id="MyCheckBox" name="MyCheckbox" checked="checked">
}else{
<input type="checkbox" id="MyCheckBox" name="MyCheckbox">
}
Would be better wrapped upin a helper to use in place of the MVC code so the value check is encapsulated.
As part of my application, the controller maintains sets of query parameters using both form injection and link injection using helpers in order to preserve state (of paging/filtering controls for example) when clicked to navigate within the same controller scope. As a result of this feature, the check box element is always set back to false if the standard MVC helpers are used. It's a good thing I noticed and did not waste much time on this bug.
In my model, I had a collection of checkboxes like so:
public class PrerequisitesViewModel
{
public List<StudentPrerequisiteStatusViewModel> PrerequisiteStatuses { get; set; }
}
public class StudentPrerequisiteStatusViewModel
{
public long Id { get; set; }
public string Name { get; set; }
public bool IsSelected { get; set; }
}
In order to get everything to bind correctly, I had to actually convert the values from the querystring and parse them manually with the following code:
// fix for how MVC binds checkboxes... it send "true,false" instead of just true, so we need to just get the true
for (int i = 0; i < model.PrerequisiteStatuses.Count(); i++)
{
model.PrerequisiteStatuses[i].IsSelected = bool.Parse((Request.QueryString[$"PrerequisiteStatuses[{i}].IsSelected"] ?? "false").Split(',')[0]);
}
Alas, it works, but I can't believe this is necessary in MVC! Hopefully, someone else knows of a better solution.
I solve this issue with use #Html.HiddenFor
<input id="checkboxId" type="checkbox" value="true" onchange="changeCheckboxValue()">
#Html.HiddenFor(m => m.MyCheckBox, new { #id = "hiddenId" } )
<script>
function changeCheckboxValue() {
document.getElementById("checkboxId").value = document.getElementById("hiddenId").checked;
}
</script>
i have this scenario where i need to accept three meeting schedules , below are the details
meeting Schedule model
public class MeetingSchedule
{
public DateTime Date { get; set; }
}
Form looks like
<form action="#Url.Action("Schedule")" method="post" >
<input type="text" name="meetingSchedules[1].Date" id="schedule2" class="datepicker" />
<input type="text" name="meetingSchedules[2].Date" id="schedule3" class="datepicker" />
</form>
and Action
[HttpPost]
public ActionResult Schedule(List<MeetingSchedule> meetingSchedules)
{}
i set the culture
<system.web>
<globalization uiCulture="en-GB" culture="en-GB" />
</system.web>
Still could not bind Date of format "dd/MM/yyyy", ex: if i choose any one date as 26/10/2011 , the model binder could not bind it , instead show default DateTime Value.
Please help me with this
Thanks
I ran into same issue and found out the problem is with the code.
If you think of action as a plain method, the model binder kicks in when the action method is invoked, prior to this if you have set the culture info then you are all set.
In the constructor add following code:
System.Threading.Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("en-GB");
Now try again, the model binder should now be able to recognize the date in dd/MM/yyyy format.