I'm trying to use Ajax.BeginForm. I want to send a DateTime through a html form.
The problem is in the controller the Datetime is not initialised. I get the default DateTime. Any idea ? Thanks for your help.
My Model:
public class TestModel
{
public ObjectId _id { get; set; }
public DateTime myDate { get; set; }
}
My Action in TestController
[HttpPost]
public void UpdateTest(TestModel tmp)
{
tmp.myDate.Date <-- get the wrong date
...
...
}
My View
#model Project.Models.TestModel
#using (Ajax.BeginForm(new AjaxOptions()
{
HttpMethod = "POST",
Url = Url.Action("UpdateTest", "Test")
}))
{
#Html.TextBoxFor(model => model._id)
#Html.TextBoxFor(model => model.myDate) // example : 18/05/2013 17:00:00
<input type="submit" value="Submit" />
}
Javascript
<script type="text/javascript" src="/Scripts/jquery.unobtrusive-ajax.min.js">
</script>
You must create custom model binder for DateTime and register it on global.asax
Related
The purpose of the app is to add a price to an offer and choose one or more days for the offer. I have to choose these days with the help of checkboxes. I have two class one is offer other is days.
My class offer :
[Key]
public int Id{ get; set; }
public double price{ get; set; }
public List<Days> days {get; set; }
My class days :
[Key]
public int Id{ get; set; }
public string Name { get; set; }
public bool Select { get; set; }
It's my controller :
public ActionResult AddOffer()
{
return View();
}
The problem is that I can not add the checkboxes. I try this :
public ActionResult AddOffer()
{
Days days= new Jours();
days.Id= 1;
days.Name= "Monday";
days.Select = false;
return View(days);
}
It's not work because it's say "Argument type Days is not assignable to model type Offer. Indeed, in my view I use #model Project.Models.Offer. But I need it. So I don't know how add checkbox with this constraint.
Please, follow this example using Data Annotations attribute:
ViewModel class:
using System;
using System.ComponentModel.DataAnnotations;
namespace MvcRequiredCheckbox
{
public class SampleViewModel
{
[Display(Name = "Terms and Conditions")]
[Range(typeof(bool), "true", "true", ErrorMessage = "You gotta tick the box!")]
public bool TermsAndConditions { get; set; }
}
}
Controller class:
namespace MvcRequiredCheckbox
{
public class HomeController : Controller
{
public ActionResult Index()
{
return View();
}
[HttpPost]
public ActionResult Index(SampleViewModel viewModel)
{
if(!ModelState.IsValid)
{
return View(viewModel);
}
return Content("Success");
}
}
}
View:
#model MvcRequiredCheckbox.SampleViewModel
<!DOCTYPE html>
<html lang="en">
<head>
<title>ASP.NET MVC - Required Checkbox with Data Annotations</title>
<link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css">
<style type="text/css">
.field-validation-error {
color: #ff0000;
display: block;
}
</style>
</head>
<body>
<div class="container">
<div class="col-md-6 col-md-offset-3">
<h1>ASP.NET MVC - Required Checkbox with Data Annotations</h1>
#using (Html.BeginForm())
{
<div class="form-group">
#Html.CheckBoxFor(x => x.TermsAndConditions)
#Html.LabelFor(x => x.TermsAndConditions)
#Html.ValidationMessageFor(x => x.TermsAndConditions)
</div>
<button type="submit" class="btn btn-success submit">Submit</button>
}
</div>
</div>
<hr />
<div class="credits text-center">
<p>
ASP.NET MVC - Required Checkbox with Data Annotations
</p>
<p>
JasonWatmore.com
</p>
</div>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.js"></script>
<script src="//ajax.aspnetcdn.com/ajax/jquery.validate/1.13.1/jquery.validate.js"></script>
<script src="//ajax.aspnetcdn.com/ajax/mvc/5.2.3/jquery.validate.unobtrusive.js"></script>
<script>
// extend jquery range validator to work for required checkboxes
var defaultRangeValidator = $.validator.methods.range;
$.validator.methods.range = function(value, element, param) {
if(element.type === 'checkbox') {
// if it's a checkbox return true if it is checked
return element.checked;
} else {
// otherwise run the default validation function
return defaultRangeValidator.call(this, value, element, param);
}
}
</script>
</body>
From: https://dotnetfiddle.net/JbPh0X
When I first load the page both my date textboxes are failing validation.
This appears to be because the two Date properties are set as required but are null.
My goals are to:
1) Have a model to pass into the controller that contains the criteria to search for.
2) That criteria will get reloaded when the page returns along with the results.
3) When the page 1st loads it will set the dates to a default to DateTime.Now and NOT show any results. When you submit the criteria it will then show results on next page load.
// Model
public class SearchModel
{
public long? StudentId { get; set; }
[Required]
public DateTime? Date1 { get; set; }
[Required]
public DateTime? Date2 { get; set; }
public List<string> Students { get; set; }
}
// View
#model SearchModel
<div>
#using (Html.BeginForm("StudentSearch", "Student", FormMethod.Post))
{
<span>
Date 1 #Html.TextBoxFor(m => m.Date1)
Date 2 #Html.TextBoxFor(m => m.Date2)
<input type="submit" value="Search" />
</span>
}
</div>
<div>
#foreach(var s in model.Students)
{ <span>#s</span> }
</div>
// Controller
[HttpGet]
public ActionResult StudentSearch(SearchModel model)
{
if (model.Date1 == null || model.Date2 == null)
{
model.Date1 = DateTime.Now;
model.Date2 = DateTime.Now;
}
return View();
}
Date time input is very sensitive. User could make a typo and ModelBinder won't be able to bind it to parameter. So, I suggest you to use framework like jQuery UI Datepicker or Kendo Datepicker.
public class StudentController : Controller
{
[HttpGet]
public ActionResult StudentSearch(SearchModel model)
{
if (model.Date1 == null || model.Date2 == null)
{
model.Date1 = DateTime.Now;
model.Date2 = DateTime.Now;
}
return View(model); <=====
}
[HttpPost]
public ActionResult StudentSearchPost(SearchModel model)
{
if (ModelState.IsValid)
{
// Do something
}
return View();
}
}
View
#model DemoWebMvc.Models.SearchModel
<div>
#using (Html.BeginForm("StudentSearchPost", "Student", FormMethod.Post))
{
<span>
Date 1 #Html.TextBoxFor(m => m.Date1)
Date 2 #Html.TextBoxFor(m => m.Date2)
<input type="submit" value="Search"/>
</span>
}
</div>
From Comment: I'm using a datetime picker control. Since the model
defaults to NULL since it's a DateTime? the very first page load shows
my date time fields failing validation. They're not failing because
what the user selects is invalid
The problem is you pass invalid model to View at Page Load, and default model binder tries to bind to model instance and it triggers validation error at Page Load.
If you do not have valid model, you should not send it to view at Page Load to avoid displaying validation error.
public class StudentController : Controller
{
[HttpGet]
public ActionResult StudentSearch()
{
return View();
}
[HttpPost]
public ActionResult StudentSearch(SearchModel model)
{
if (ModelState.IsValid)
{
}
return View(model);
}
}
View
#model DemoWebMvc.Models.SearchModel
#{
Layout = null;
}
#using (Html.BeginForm("StudentSearch", "Student", FormMethod.Post))
{
#Html.ValidationSummary(true)
<span>
Date 1 #Html.TextBoxFor(m => m.Date1)
#Html.ValidationMessageFor(m => m.Date1)<br />
Date 2 #Html.TextBoxFor(m => m.Date2)
#Html.ValidationMessageFor(m => m.Date2)<br />
<input type="submit" value="Search" />
</span>
}
<script src="~/Scripts/jquery-1.10.2.min.js"></script>
<script src="~/Scripts/jquery.validate.min.js"></script>
<script src="~/Scripts/jquery.validate.unobtrusive.min.js"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.min.js"></script>
<script>
$(function () {
$("#Date1").datepicker();
$("#Date2").datepicker();
});
</script>
Just changing the type from DateTime? to DateTime should accomplish what you want. You can set the value to DateTime.Now in the first action or leave it as the default unsigned. Either will work in the view.
Edit: Here's what I mean in a dotnetfiddle: https://dotnetfiddle.net/Ei0LeQ
I am trying to post the form with ReCaptcha to ApiController.
<form>
<label for="name">Name:</label>
<input id="name" name="name" ><br/>
<label for="email">Email:</label>
<input id="email" name="email" type="email" ><br/>
<div class="g-recaptcha" data-sitekey="some_site_key"></div>
<input type="submit" value="Submit"/>
</form>
<script src="https://www.google.com/recaptcha/api.js"></script>
<script>
$(function () {
$('form').submit(function (event) {
var data = $('form').serialize();
console.log('form data: ', data);
$.post("api/test", data,
function (data) {
alert('success');
});
event.preventDefault();
});
});
</script>
Form the console.log() statement, I can see the values posted are under names of name, email and g-recaptcha-response.
I thought I could have a view model at the ApiController, like this:
public IHttpActionResult Post(FormModel model)
{
// automatic model binding to get the posted data.
}
public class FormModel
{
public string Name { get; set; }
public string Email { get; set; }
public string GRecaptchaResponse { get; set; }
}
But obviously there is no rule to bind g-recaptcha-response to GRecaptchaResponse. And a valid property name should not contain a dash -.
So the question is, how can we receive the g-recaptcha-response value at the server (ApiController) end?
Here is what I've found:
In ApiController:
public async Task Post()
{
var obj = await Request.Content.ReadAsAsync<JObject>();
var model = obj.ToObject<FormModel>();
//...
}
For the FormModel, use [JsonProperty]:
public class FormModel
{
public string Name { get; set; }
public string Email { get; set; }
[JsonProperty("g-recaptcha-response")]
public string GRecaptchaResponse { get; set; }
}
I searched the internet for possible problems that might be causing this and found nothing ...
When I put the IEnumerable<HttpPostedFileBase> FilesUploadedEvent as action parameter alongside ViewModel everything seems to be working fine.
[HttpPost]
public ActionResult Create(EventViewModel model, IEnumerable<HttpPostedFileBase> FilesUploadedEvent)
{
if (ModelState.IsValid)
{
...
Problem is when I try to put it inside object in my ViewModel
#model ViewModels.Event.EventViewModel
#using (Html.BeginForm("Create", "Event", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
#Html.ValidationSummary(true, null, new { #class = "validation-msg" })
#Html.EditorFor(m => m.BasicInfoSection)
...
<input type="submit" value="Add" />
}
Controller after removing 2nd parameter:
[HttpPost]
public ActionResult Create(EventViewModel model)
{
if (ModelState.IsValid)
{
...
ViewModel:
public class EventViewModel
{
public BasicInfoSection BasicInfoSection { get; set; }
...
And we put the parameter inside ViewModels object:
public class BasicInfoSection : IValidatableObject
{
public string Remarks { get; set; }
public IEnumerable<HttpPostedFileBase> FilesUploadedEvent { get; set; }
...
and here is editor template for BasicInfoSection :
#model ViewModels.Event.Parts.BasicInfoSection
<input id="FilesUploadedEvent" name="FilesUploadedEvent" type="file" data-role="upload" multiple="multiple" autocomplete="off">
#(Html.TextAreaFor(m => m.Remarks, new { #class = "form-control", style = "height:200px;" }))
...
Also if the form wont validate on some other fields is it possible to return Files in the postBack? I read it is not because of security reasons. Is Ajax validation the only method than?
The name attribute of the file input is not correct. Because FilesUploadedEvent is a property of BasicInfoSection, then in order to be bound on post back, the input would need to be
<input name="BasicInfoSection.FilesUploadedEvent" type="file" .. />
Note also how the textareahas the name attribute name="BasicInfoSection.Remarks"
This is my first post so please go easy on me fellas. I am trying to implement a create form that utilizes jquery autocomplete. The create form allows users to enter data that will be saved to my database, via a submit button. Here is my code:
Controller
// GET: /Inspection1/Create
public ActionResult Create()
{
InspectionInfo model = new InspectionInfo
{
Submitted = DateTime.Now,
Contact = new Contact()
};
ViewBag.CountyName = new SelectList(db.Counties, "CountyName", "CountyName");
return View(model);
}
//
// POST: /Inspection1/Create
[HttpPost]
public ActionResult Create(InspectionInfo inspectioninfo)
{
if (ModelState.IsValid)
{
db.InspectionInfos.Add(inspectioninfo);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(inspectioninfo);
}
// this allows for autocompletion behavior
public ActionResult QuickSearchContact(string term)
{
var contacts = db.Contacts
.Where(r => r.ContactName.Contains(term))
.Take(10)
.Select(r => new { label = r.ContactName });
return Json(contacts, JsonRequestBehavior.AllowGet);
}
Models
public class InspectionInfo
{
[Key]
public int InspectionId { get; set; }
[DataType(DataType.Date)]
public virtual DateTime Submitted { get; set; }
[DataType(DataType.MultilineText)]
[MaxLength(1000)]
public string Comments { get; set; }
[Required]
public Contact Contact { get; set; }
public class Contact
{
[Key]
public string ContactName { get; set; }
View:
<div class="editor-label">
#Html.LabelFor(model => model.Contact)
</div>
<div class="editor-field">
<input type ="text" name ="q" data-autocomplete=
"#Url.Action("QuickSearchContact", "Inspection")"/>
#Html.ValidationMessageFor(model => model.Contact.ContactName)
</div>
JS
$(document).ready(function () {
$(":input[data-autocomplete]").each(function () {
$(this).autocomplete({ source: $(this).attr("data-autocomplete")});
});
The autocomplete function seems to be working fine. It will pull column data from the database as I require. However, any data entered in the autocomplete text box, appears NULL in the database after the user has saved the form. Help here would be greatly appreciated.
For model binding to work, generally input names must match property names of your model. Surprisingly, you have named your input "q"
<input type ="text" name ="q" data-autocomplete="..."/>
Just rename it according to your model
<input type ="text" name="Contact.ContactName" data-autocomplete="..."/>
You don't have your on the code above but, instead of using
<input type ="text" name ="q" data-autocomplete= "#Url.Action("QuickSearchContact", "Inspection")"/>
use:
#EditorFor(x = x.NameOfTextBox)
then either have an input button wrapped in a using tag
#using (Html.BeginForm("Create", "NameOfController", FormMethod.Post){
//all your model stuff goes here
}
or use and actionlink instead of :
#Html.ActionLink("Submit", "Create", "NameOfController", Model)
The provided information doesn't tell, but is is likely that the autocomplete part is not written within the form elements of the view:
#using (Html.BeginForm())
{
<p>
...
</p>
}
In MVC the form is defined within the brackets { .... } like above.