Client form validation not working with modal dialog in MVC - asp.net-mvc

I am changing a create form to become a modal dialog and jquery Unobtrusive validation stops working and don't know how to fix it.
Index.cshtml has a link to trigger a modal dialog.
Create
#section scripts{
<script type="text/javascript">
$('#createCustomer').on('click', function () {
$.get('/Customer/Create', function (data) {
$('#modalContainer').html(data);
$('#myModal').modal({});
});
});
Create.cshtml is a partial view.
#model Demo.Web.Models.CustomerVm
#using (Html.BeginForm("Create", "Customer", FormMethod.Post, new { #id="createForm" }))
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Customer</h4>
<hr />
#Html.ValidationSummary(true)
<div class="form-group">
#Html.LabelFor(model => model.Name, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Name)
#Html.ValidationMessageFor(model => model.Name)
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
}
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
On the controller there is an actionmethod which returns a partial view for create form.
public ActionResult Create()
{
return PartialView("Create");
}
view modal
public class CustomerVm
{
[Required]
public int Id { get; set; }
[Required]
public string Name { get; set; }
}
before i changed it to be a modal dialog .. everything was working but now i don't know how to fix it. How can i make validation work with dialog? Obviously, I don't want to rewrite validation rules on client script .. i want to get it working from view model .. thanks.

Because the form is not added to the page when the page loads, the unobtrusive validation will not pick it up. There are two ways to fix this.
Include the form on the page during the initial load. This will cause the form to be recognized and validation will occur. You can throw the partial view in a hidden div. Then your JavaScript will just show the modal dialog.
Manually register the form with the unobtrusive validation after adding it to the page. Something like $.validator.unobtrusive.parse("#id-of-the-form");

If you are loading the dialog dynamically just register the unobtrusive validation in the containing element's change event:
$('#modal-container').change(
function() {
$.validator.unobtrusive.parse("#your-form-id");
});

In partialview of create page -> modal-header, model-body, modal-footer and javascript code in the <script>your code </script> - don't put <script>your code</script> in #section Scripts{} and it will work.

Just add the following scripts in your Create form view:
<script src="#Url.Content("~/Scripts/jquery.validate.min.js")"
type="text/javascript">
</script>
<script src="#Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")"
type="text/javascript">
</script>

Adding a new comment to share a more modern solution:
Use BundleConfig.cs in the App_Start folder of your MVC project.
namespace MySolution
{
public class BundleConfig
{
public static void RegisterBundles(BundleCollection bundles)
{
bundles.Add(new StyleBundle("~/Content/css").Include(
"~/Content/Site.min.css",
"~/Content/bootstrap.min.css"));
bundles.Add(new ScriptBundle("~/Scripts/js").Include(
"~/Scripts/jquery-3.3.1.min.js",
"~/Scripts/jquery.validate.min.js",
"~/Scripts/jquery.validate.unobtrusive.min.js"));
}
}
}

Related

MVC4 Post form null value

I have a grid when the client click on Edit for one like a form is open. the User can Edit only some values (let the user to edit only the Shipping date for the current order) but when I send the Form the values of the non editable field are NULL on Post
When I display :
#Html.Display(model => model.Rep)
or :
#Html.TextBoxFor(model => model.ClientName, new { disabled = "disabled", #readonly = "readonly" })
the Values are displayed correctly but when I submit the form the Value are Null.
the View :
#model Models.Orders
#{
Layout = null;
}
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link href="~/Content/pure-release-0.5.0/pure-min.css" rel="stylesheet" />
</head>
#using (Html.BeginForm())
{
#Html.ValidationSummary(true)
<body>
<form class="pure-form">
<fieldset>
<legend>A Stacked Form</legend>
<div class="pure-g">
<div class="pure-u-1 pure-u-md-1-3" aria-disabled="true" aria-readonly="true">
<label for="first-name">#Html.LabelFor(model => model.Rep)</label>
#Html.Display(model => model.Rep)
</div>
<div class="pure-g">
<div class="pure-u-1 pure-u-md-1-3" aria-readonly="true">
<label for="first-name">#Html.LabelFor(model => model.ClientName)</label>
#Html.TextBoxFor(model => model.ClientName, new { disabled = "disabled", #readonly = "readonly" })
</div>
</div>
</div>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
</form>
</body>
}
</html>
<script type="text/javascript">
$(document).ready(function () {
$(".Titre").click(function () {
$(this).next('.Contenu').slideToggle("slow");
});
$("#Model").prop('disabled', true);
});
</script>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}^
The model :
public class Orders
{
public int ID { get; set; }
public string Rep { get; set; }
public string ClientName { get; set; }
}
Controller :
When the User click on Edit on the Grid:
public ActionResult Edit(int id = 0)
{
Orders order = db.Orders.Find(id);
if (order == null)
{
return HttpNotFound();
}
return View(order);
}
On post:
[HttpPost]
public ActionResult Edit(Orders order)
{
if (ModelState.IsValid)
{
db.Entry(order).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
return View(order);
}
When I debug I find theat the Values on order are NULL
I Thought that the problem was the waay that I sent data from my grid to the form but i change the easyui to Use GridMVC and still have the problem.
I used : in View TextBoxFor for readOnly + disabled as attribut but same problem
I tried :
in the Model : [ReadOnly(true)]
+ in the View : #Html.EditorFor(model => model.Rep) but I was able to edit Rep That I want to block
I tried to make the EditorFor readonly with Javascript but I was able to edit
Can you help me please, I tried all what I found but there is something missing in my code
Thanks
This is by design. Readonly values are not submitted to server (at least by modern browsers).
If you want to submit this value you can create a hidden field instead of a textbox:
#Html.HiddenFor(model => model.ClientName)
This will effectively submit your value to server
I ran into the same issue. Trying to short cut with the MVC CRUD template. I like what #PaulTaylor suggested:
Attribute [Bind(Exclude=“”)] fails to prevent over-posting
The problem is because you are using 2 forms: one nested to the other. You have to remove the second and add the css class to the first. The Html.BeginForm() will create another form.
You can try something like this:
#using (Html.BeginForm("Edit","controllerName",null,FormMethod.Post,{#class="pure-form"}))
{
#Html.ValidationSummary(true)
<body>
#Html.HiddenFor(model => model.ID)
<fieldset>
<legend>A Stacked Form</legend>
<div class="pure-g">
<div class="pure-u-1 pure-u-md-1-3" aria-disabled="true" aria-readonly="true">
<label for="first-name">#Html.LabelFor(model => model.Rep)</label>
#Html.Display(model => model.Rep)
</div>
<div class="pure-g">
<div class="pure-u-1 pure-u-md-1-3" aria-readonly="true">
<label for="first-name">#Html.LabelFor(model => model.ClientName)</label>
#Html.TextBoxFor(model => model.ClientName, new { disabled = "disabled", #readonly = "readonly" })
</div>
</div>
</div>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
</body>
}
Your text box is disabled. The inputs with disabled attribute set do not get submitted with the form. You can verify this by using network tab of Chrome dev tool or your favorite browser. If you want the text boxes disabled, use just readonly and not the disabled. So, your text box should be as below:
#Html.TextBoxFor(model => model.ClientName, new {#readonly = "readonly" })
Afterwards, you can use jQuery and CSS to make them look like disabled by graying them out if you like but do not use disabled if you want the values to come through.

Submit Partial View Data with main form in asp.net MVC

How Do I submit Data in Partial View on submit on Main View.the partial view doesn't have a separate submit button
I'm trying to Add Multiple Products to in a single Order.Each Product is Partial View.
User can add multiple Products on click of "plus sign". I want all Products to submitted when I submit the Order.
My Place Order View is
#model MyApp.Models.Order_Detail
#section scriptsection {
<script src="#Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
<script src="../../Scripts/jquery-1.4.4.min.js" type="text/javascript"></script>
<script type="text/javascript">
$(document).ready(function () {
$(".addProduct").live("click", function () {
$.get("AddProduct",
function (data) {
$("#Products").append(data);
HideshowMinusButton();
});
});
</script>
}
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
<legend>Order</legend>
<div class="editor-label">
Customer
</div>
<div class="editor-field">
#Html.DropDownListFor(c=>c.Order.CustomerID,(List<SelectListItem>)ViewBag.Customers)
</div>
<div>
<fieldset id="Products">
<legend>Products</legend>
#Html.Partial("AddProduct")
</fieldset>
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Order.OrderDate)
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
On Submit Button Click How Do I get all Products in my Order?
[HttpPost]
public ActionResult PlaceOrder(Order_detail Ord) //Ord Doesn't Contain Products ??
{
//Add Order to Db
return View("Success");
}
have a look at this page, i think it will be able to help you.
http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx/
you can change your action method to
public ActionResult PlaceOrder(Order_detail Ord, Products[] products)
or use http://www.nuget.org/packages/BeginCollectionItem/ to model bind into the Order_detail model.
You can use form collection in the post method to get the data in the partial view.
[HttpPost]
public ActionResult PlaceOrder(Order_detail Ord, FormCollection fc)
{
string value = fc.getValue("textBoxName");
}
You will get the value in form collection only if the control has name property.
Ex:
<input name="users" type="text"/>
then
String Value = fc.getvalue("users");

HttpPostedFileBase in ASP.NET MVC is null while using jQuery Dialog

File upload paths in MVC is null with jquery dialog
I have created a MVC application having two file uploads in view and created a POST action for this, with IEnumerable parameter to catch the files. When i am submitting the form the files are coming fine in the HttpPostedFileBase collection, but if the file upload controls are in a dialog(jquery pop up) the IEnumerable object is null. Please help me.
The following are the codes i have done.
View
#using (Html.BeginForm("Details", "StudentRegistration", FormMethod.Post, new{ #class = "form ideal-form",enctype = "multipart/form-data"}))
{
<div id="divSignatureCapturePopUp" title="Capture Photo" style="display:none; float:left;">
<input id="fileUploadSignature" type="file" name ="fileUploadImages" style="width:200px"/>
</div>
}
<input type="button" id="buttonCaptureSignature" name="CaptureSignature" class="ideal-button" value="Capture Signature" />
<script type="text/javascript">
$(document).ready(function () {
$("#buttonCaptureSignature").click(function () {
$("#divSignatureCapturePopUp").dialog({
width: 560,
});
});
}
</script>
controller
[HttpPost]
public ActionResult Details(IEnumerable<HttpPostedFileBase> fileUploadImages)
{
}
Seems like jqueryui widget move file input field outside form. Check posted data in chrome dev console in 'Network' tab and make sure that file data is in request.
If this suggestion is right you can modify form this way:
<div id="divSignatureCapturePopUp" title="Capture Photo" style="display:none; float:left;">
#using (Html.BeginForm("Details", "StudentRegistration", FormMethod.Post, new{ #class = "form ideal-form",enctype = "multipart/form-data"}))
{
<input id="fileUploadSignature" type="file" name ="fileUploadImages" style="width:200px"/>
<input type="submit" value="rtwert" />
}
</div>
Оtherwise possible get files in other way:
[HttpPost]
public ActionResult Details()
{
var files = Request.Files;
}

Create new parent and child on the same page

My MVC application has a classic parent-child (master-detail) relations.
I want to have a single page that create both the new parent and the children on the same page. I have added an action the returns a partial view that and adds the child HTML to the parent’s view, but I don’t know how to associate the newly created child in the action to the original parent (in other word, how to I add the new child entity to the collection of these entities in the parent entity).
I guess that when I submit the form the action should get the parent entity with the newly created children in its collection.
So to make things short, what should be the code of the action that creates the child entity and how is the child added to its parent collection?
I have read a lot of posts here (and other sites) and couldn’t find an example.
The application uses MVC 4 and Entity Framework 5.
Code sample (I removed some of the code the keep it simple).
The model is Form (parent) and FormField (child) entities.
public partial class Form
{
public int ID { get; set; }
public string Name { get; set; }
public virtual ICollection<FormField> FormFields { get; set; }
}
public partial class FormField
{
public int ID { get; set; }
public string Name { get; set; }
public int FormID { get; set; }
}
The following partial view (_CreateFormField.cshtml) creates new FormField (child).
#model FormField
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
<legend>FormField</legend>
<div class="editor-label">
#Html.LabelFor(model => model.Name)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Name)
#Html.ValidationMessageFor(model => model.Name)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.FormID)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.FormID)
#Html.ValidationMessageFor(model => model.FormID)
</div>
</fieldset>
}
And the following view (Create.cshtml) is the one the creates the Form.
#model Form
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
<legend>Form</legend>
<div class="editor-label">
#Html.LabelFor(model => model.Name)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Name)
#Html.ValidationMessageFor(model => model.Name)
</div>
<div>
#Html.ActionLink(
"Add Field",
"CreateFormField",
new { id = -1},
new { #class = "form-field" })
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
<div id="CreateFormField"></div>
#section Scripts {
<script>
$(function () {
$('.form-field').on('click', function (e) {
$.get($(this).prop('href'), function (response) {
$('#CreateFormField').append(response)
});
e.preventDefault();
});
});
</script>
#Scripts.Render("~/bundles/jqueryval")
}
The following actions handle the creation in the FormController.
[HttpPost]
public ActionResult Create(Form form)
{
if (ModelState.IsValid)
{
db.Forms.Add(form);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(form);
}
public ActionResult CreateFormField(string id = null)
{
// I guess something is missing here.
return PartialView("_CreateFormField", new FormField());
}
Thanks in advance,
Sharon.
I think the best and simplest way for you is that you have a view for creating Form and at the bottom of it put a fieldset to assign FormFields to it.
For the fieldset, you should have two partial views: One for create and another for edit. The partial view for creating should be something like this:
#model myPrj.Models.Form_FormFieldInfo
#{
var index = Guid.NewGuid().ToString();
string ln = (string)ViewBag.ListName;
string hn = ln + ".Index";
}
<tr>
<td>
<input type="hidden" name="#hn" value="#index" />
#Html.LabelFor(model => model.FormFieldID)
</td>
<td>
#Html.DropDownList(ln + "[" + index + "].FormFieldID",
new SelectList(new myPrj.Models.DbContext().FormFields, "ID", "FieldName"))
</td>
<td>
<input type="button" onclick="$(this).parent().parent().remove();"
value="Remove" />
</td>
</tr>
By calling this partial view in the create place view ajaxly, you can render some elements for each tag. Each line of elements contains a label, a DropDownList containing tags, and a remove button to simply remove the created elements.
In the create place view, you have a bare table which will contain those elements you create through the partial view:
<fieldset>
<legend>Form and FormFields</legend>
#Html.ValidationMessageFor(model => model.FormFields)</label>
<table id="tblFields"></table>
<input type="button" id="btnAddTag" value="Add new Field"/>
<img id="imgSpinnerl" src="~/Images/indicator-blue.gif" style="display:none;" />
</fieldset>
and you have the following script to create a line of elements for each tag:
$(document).ready(function () {
$("#btnAddField").click(function () {
$.ajax({
url: "/Controller/GetFormFieldRow/FormFields",
type: 'GET', dataType: 'json',
success: function (data, textStatus, jqXHR) {
$("#tblFields").append(jqXHR.responseText);
},
error: function (jqXHR, textStatus, errorThrown) {
$("#tblFields").append(jqXHR.responseText);
},
beforeSend: function () { $("#imgSpinnerl").show(); },
complete: function () { $("#imgSpinnerl").hide(); }
});
});
});
The action method GetFormFieldRow is like the following:
public PartialViewResult GetFormFieldRow(string id = "")
{
ViewBag.ListName = id;
return PartialView("_FormFieldPartial");
}
and your done for the create... The whole solution for your question has many codes for views, partial views, controllers, ajax calls and model binding. I tried to just show you the way because I really can't to post all of them in this answer.
Here is the full info and how-to.
Hope that this answer be useful and lead the way for you.
You can use #Html.RenderPartial(_CreateFormField.cshtml) htlper method inside your parent cshtml page.
For mode info, http://msdn.microsoft.com/en-us/library/dd492962(v=vs.118).aspx
I am providing an pseudo code example,
#Foreach(childModel in model.FormFields)
{
#Html.RenderPartial("childView.cshtml","Name", childModel)
}
Please try it in proper c# syntactical way and you will get your partial views rendered for each collection item.
The problem is that your partial view needs to use:
#model Form //Instead of FormField
and then inside of the partial view you must use model => model.FormField.x
<div class="editor-label">
#Html.LabelFor(model => model.FormField.Name)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.FormField.Name)
#Html.ValidationMessageFor(model => model.FormField.Name)
</div>
This only works for one-to-one relationships (or so I read in this thread: Here). It also does not matter if you have #Html.Form inside of the partial view.
After making those changes I had no problem getting all of the posted data back to the controller.
EDIT: I've run into problems with this as anyone would soon figure out.
The better way to do this (that I've seen) is instead use an EditorTemplate. This way the editor stays independent of the Parent, and you can add a Form on any page, not simply a page where you are also adding a FormField.
You would then replace
#Html.Partial("view")
with
#Html.EditorFor()
Basically, I've found out that it's far better to use an #Html.EditorFor instead of a partial view when doing editing (It is called a view - not an editor).
If you want to use grid based layout you may want to try Kendo UI grid
Use jQueryto add FormField on click of button.
Similar I've used it as follows
<div class="accomp-multi-field-wrapper">
<table class="table">
<thead>
<tr>
<th>Name</th>
<th>FormId</th>
<th>Remove</th>
</tr>
</thead>
<tbody class="accomp-multi-fields">
<tr class="multi-field">
<td> <input name="FormFields[0].Name" type="text" value=""></td>
<td> <input name="FormFields[0].FormId" type="text" value=""></td>
<td> <button type="button" class="remove-field">X</button></td>
</tr>
</tbody>
<tr>
<th><button type="button" class="add-field btn btn-xs btn-primary addclr"><i class="fa fa-plus"></i> Add field</button> </th>
</tr>
</table>
</div>
and jQuery is as follows for adding field and removing
<script>
$('.accomp-multi-field-wrapper').each(function () {
var $wrapper = $('.accomp-multi-fields', this);
$(".add-field", $(this)).click(function (e) {
var $len = $('.multi-field', $wrapper).length;
$('.multi-field:first-child', $wrapper).clone(false, true).appendTo($wrapper).find('select,input,span').val('').each(function () {
$(this).attr('name', $(this).attr('name').replace('\[0\]', '\[' + $len + '\]'));
});
$(document).on("click",'.multi-field .remove-field', function () {
if ($('.multi-field', $wrapper).length > 1) {
$(this).closest('tr').remove();
//
$('.multi-field', $wrapper).each(function (indx) {
$(this).find('input,select,span').each(function () {
$(this).attr('name', $(this).attr('name').replace(/\d+/g, indx));
})
})
}
});
</script>
Hope so this is going to help you.

Unobtrusive Javascript - And Ajax Requests

I've got a simple example here. Basically a form which when submitted will reload itself via an ajax request. The problem is when this happens, the unobtrusive javascript no longer works. I assume I could add the validate and unobtrusive files in the html i get back from the ajax call, but there must be an easier way to re-wire the validators, no?
Notice I'm hijacking my submit button in order to do an AJAX request which will replace the form in the dom, from the html which is returned from the ajax request.
Model:
public class Foo
{
public int Bar { get; set; }
}
Controller:
public class FooController : Controller
{
public ActionResult Index()
{
return View(new Foo{});
}
[HttpPost]
public ActionResult Form(Foo model)
{
return View(model);
}
[HttpPost]
public ActionResult Index(Foo model)
{
return View();
}
}
Index.cshtml
#model PartialPostBackValidation.Models.Foo
#{
ViewBag.Title = "Index";
}
<h2>#Html.ActionLink("Index", "Index")</h2>
<script src="#Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
<script>
$(function () {
$("body").on("click", ".ajax-submit", function () {
var form = $(this).parents("form");
$.post(
form.attr("action"),
form.serialize(),
function (html) {
form.replaceWith(html);
}
);
return false;
});
});
</script>
#{Html.RenderPartial("Form");}
Form.cshtml
#model PartialPostBackValidation.Models.Foo
#{
ViewBag.Title = "Index";
Layout = null;
}
#using (Html.BeginForm("Form", "Foo")) {
#Html.ValidationSummary(true)
<fieldset>
<legend>Foo</legend>
<div class="editor-label">
#Html.LabelFor(model => model.Bar)
</div>
<div class="editor-field">
#Html.TextBoxFor(model => model.Bar)
#Html.ValidationMessageFor(model => model.Bar)
</div>
<p>
<input type="submit" value="Create" class="ajax-submit" />
</p>
</fieldset>
}
To get the validation to work you simply have to re-enable it on the form once content is loaded dynamically:
$('#form-id').removeData('validator');
$('#form-id').removeData('unobtrusiveValidation');
$.validator.unobtrusive.parse('#form-id'); <<<<<< Just having this could be enough but some people complain that without removingData first it doesn’t always work.
p.s. Ofcourse you're going to need to add an id attribute to your #using (Html.BeginForm("Form", "Foo"))

Resources