Submit same Partial View called multiple times data to controller? - asp.net-mvc-4

I have added a button in my view. When this button is clicked partial view is added. In my form I can add as much partial view as I can. When Submitting this form data I am unable to send all the partial view data to controller.
I have made a different model having all the attributes and I have made a list of that model to my main model. Can anyone please give me some trick so that I can send all the partial view content to my controller?
In My View
<div id="CSQGroup">
</div>
<div>
<input type="button" value="Add Field" id="addField" onclick="addFieldss()" />
</div>
function addFieldss()
{
$.ajax({
url: '#Url.Content("~/AdminProduct/GetColorSizeQty")',
type: 'GET',
success:function(result) {
var newDiv = $(document.createElement("div")).attr("id", 'CSQ' + myCounter);
newDiv.html(result);
newDiv.appendTo("#CSQGroup");
myCounter++;
},
error: function(result) {
alert("Failure");
}
});
}
In My controller
public ActionResult GetColorSizeQty()
{
var data = new AdminProductDetailModel();
data.colorList = commonCore.getallTypeofList("color");
data.sizeList = commonCore.getallTypeofList("size");
return PartialView(data);
}
[HttpPost]
public ActionResult AddDetail(AdminProductDetailModel model)
{
....
}
In my Partial View
#model IKLE.Model.ProductModel.AdminProductDetailModel
<div class="editor-field">
#Html.LabelFor(model => model.fkConfigChoiceCategorySizeId)
#Html.DropDownListFor(model => model.fkConfigChoiceCategorySizeId, Model.sizeList, "--Select Size--")
#Html.ValidationMessageFor(model => model.fkConfigChoiceCategorySizeId)
</div>
<div class="editor-field">
#Html.LabelFor(model => model.fkConfigChoiceCategoryColorId)
#Html.DropDownListFor(model => model.fkConfigChoiceCategoryColorId, Model.colorList, "--Select Color--")
#Html.ValidationMessageFor(model => model.fkConfigChoiceCategoryColorId)
</div>
<div class="editor-field">
#Html.LabelFor(model => model.productTotalQuantity)
#Html.TextBoxFor(model => model.productTotalQuantity)
#Html.ValidationMessageFor(model => model.productTotalQuantity)
</div>

Your problem is that the partial renders html based on a single AdminProductDetailModel object, yet you are trying to post back a collection. When you dynamically add a new object you continue to add duplicate controls that look like <input name="productTotalQuantity" ..> (this is also creating invalid html because of the duplicate id attributes) where as they need to be <input name="[0].productTotalQuantity" ..>, <input name="[1].productTotalQuantity" ..> etc. in order to bind to a collection on post back.
The DefaultModelBinder required that the indexer for collection items start at zero and be consecutive, or that the form values include a Index=someValue where the indexer is someValue (for example <input name="[ABC].productTotalQuantity" ..><input name="Index" value="ABC">. This is explained in detail in Phil Haack's article Model Binding To A List. Using the Index approach is generally better because it also allows you to delete items from the list (otherwise it would be necessary to rename all existing controls so the indexer is consecutive).
Two possible approaches to your issue.
Option 1
Use the BeginItemCollection helper for your partial view. This helper will render a hidden input for the Index value based on a GUID. You need this in both the partial view and the loop where you render existing items. Your partial would look something like
#model IKLE.Model.ProductModel.AdminProductDetailModel
#using(Html.BeginCollectionItem())
{
<div class="editor-field">
#Html.LabelFor(model => model.fkConfigChoiceCategorySizeId)
#Html.DropDownListFor(model => model.fkConfigChoiceCategorySizeId, Model.sizeList, "--Select Size--")
#Html.ValidationMessageFor(model => model.fkConfigChoiceCategorySizeId)
</div>
....
}
Option 2
Manually create the html elements representing a new object with a 'fake' indexer, place them in a hidden container, then in the Add button event, clone the html, update the indexers and Index value and append the cloned elements to the DOM. To make sure the html is correct, create one default object in a for loop and inspect the html it generates. An example of this approach is shown in this answer
<div id="newItem" style="display:none">
<div class="editor-field">
<label for="_#__productTotalQuantity">Quantity</label>
<input type="text" id="_#__productTotalQuantity" name="[#].productTotalQuantity" value />
....
</div>
// more properties of your model
</div>
Note the use of a 'fake' indexer to prevent this one being bound on post back ('#' and '%' wont match up so they are ignored by the DefaultModelBinder)
$('#addField').click(function() {
var index = (new Date()).getTime();
var clone = $('#NewItem').clone();
// Update the indexer and Index value of the clone
clone.html($(clone).html().replace(/\[#\]/g, '[' + index + ']'));
clone.html($(clone).html().replace(/"%"/g, '"' + index + '"'));
$('#yourContainer').append(clone.html());
}
The advantage of option 1 is that you are strongly typing the view to your model, but it means making a call to the server each time you add a new item. The advantage of option 2 is that its all done client side, but if you make any changes to you model (e.g. add a validation attribute to a property) then you also need to manually update the html, making maintenance a bit harder.
Finally, if you are using client side validation (jquery-validate-unobtrusive.js), then you need re-parse the validator each time you add new elements to the DOM as explained in this answer.
$('form').data('validator', null);
$.validator.unobtrusive.parse($('form'));
And of course you need to change you POST method to accept a collection
[HttpPost]
public ActionResult AddDetail(IEnumerable<AdminProductDetailModel> model)
{
....
}

Related

Bind(Exclude = "ReviewerName") is deleting my ReviewerName data from table while I am want to protect Mass Assignment or overposting

I am working with ASP.Net MVC 4 and entity framework. I have a table for inserting and updating reviews against restaurant by it's customer. While updating customer can't change their name. they can change only only change Rating and Comment. So, I have delete the input code for reviewerName(Customername) FROM MY VIEW PAGE OF THE mvc. But hacker can change the name or reviews_id by "Mass Assignment" / "Over posting". Means they can send ReviewerName by posting it in url as query string.
~/Reviews/Edit?ReviewerName=Hacker
To prevent it I have use Bind model binding with exclude parameter. Yes it prevent hacker to inject ReviewerName. But also deleting reviewer name from database table. Is there any way to solve this rather using "include".My controller code is below.
public ActionResult Edit([Bind(Exclude = "ReviewerName")] RestaurantReview review)
{
if (ModelState.IsValid)
{
_db.Entry(review).State = System.Data.EntityState.Modified;
_db.SaveChanges();
return RedirectToAction("Index", new { id = review.RestaurantId });
}
return View(review);
}
In Edit Page, make the field read only which you don't want to be changed by customer.
<div class="form-group">
#Html.LabelFor(model => model.Fname, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.TextBoxFor(model => model.Fname, new { #readonly = "readonly" })
#Html.ValidationMessageFor(model => model.Fname)
</div>
</div>
No need to exclude anything from Model in Controller .Hope this will help :)

Form submission for mvc model enumerable using editor template

I have an editor template for my model view ViewSetup. My view to use template is simplified as
#model IEnumerable<ViewSetup>
#Html.EditorFor(s => s)
My ViewSetup editor template has form submission like below:
using (Ajax.BeginForm("Edit", new AjaxOptions() { HttpMethod = "Post" }))
{
#Html.HiddenFor(p => p.TradingPartner.ID)
<input type="submit" value="Save" />
}
So basically i need a form to be submitted for each element of the Enumerable. But I'm facing a problem on form submission. My controller to process post is:
public ActionResult Edit(ViewSetup formDataSent)
{
formDataSent.Save();
}
As per default model binding I'm getting null for TradingPartner property since the name in the html is :
<input name="[0].TradingPartner.ID" type="hidden" value="1"/>
What I need is to submit only the ViewSetup object on each element instead of an array. If I can get the index part in the name removed that could work for me. But I'm not sure how to get just the ViewSetup object on form submission.
I bet that if you base your editor on one item instead of a list of items then you would gain more flexibility.
#model IEnumerable<ViewSetup>
#foreach (var item in Model)
{
#Html.EditorFor(modelItem => item.TradingPartner)
}
I had the same issue, what resolved it for me was this:
#foreach (var item in Model)
{
#Html.EditorFor(modelItem => item.TradingPartner, null, "")
}
The third property of the EditorFor being blank will get rid of the "[0]" from your objects.

How to access data in partialview loaded using Ajax

I'm currently building a website where I have to update two separate targets from a single Ajax.BeginForm. I got it working by using an additional container to container the two separate targets. As in:
Original Form
#model Mod1
#using (Ajax.BeginForm("LoadData", new AjaxOptions{UpdateTargetID = "Div1"}))
{
<select id="sel1" name="sel1" onchange="$(this.form).submit">
// ...
</select>
}
#using (Ajax.BeginForm("ProcessData", new AjaxOptions{UpdateTargetID = "Div2"}))
{
<div id="Div1"></div>
// ...
<input type="submit" value="GO!" />
}
Code File
public ActionResult LoadData(int sel1)
{
// loading data from database
return PartialView(mod1);
}
Partial View
#model Mod2
<select id="sel2" name="sel2">
#foreach (var item in Model.SelectItems)
{
<option value="#item.Value">#item.Text</option>
}
</select>
#foreach (var item in Model.CheckBoxItems)
{
<label>#item.Text<input type="checkbox" id="chk1" name="chk1" value="#item.Value"></label>
}
For the processing method, I have tried:
public ProcessData(Mod1 mod1, string[] chk1, int sel2)
However I am unable to retrieve the values for either chk1 or sel2 upon form submission. examination of chk1 and sel2 in Debug mode, chk1 is null while sel2 is 0 (no such value in the select options). Can anyone please offer some insight into the reason for this and also how I can go about solving it. Thank you in advance.
If I understand you correctly you can do what you want y having two submit buttons on the same form, each calling a separate action method. That way each submit button will have access to all the fields in the form. For a detailed explanation of how you can do that see my answer here:
How to use ajax link instead of submit button for form?
Edit
In response to comment: the action method LoadData should return a partial view that contains the other two controls and have the whole begin form included in it:
#using (Ajax.BeginForm("LoadData", new AjaxOptions{
UpdateTargetID = "Div1",
InsertionMode = InsertionMode.Replace
}))
{
<select id="sel1" name="sel1" onchange="$(this.form).submit">
// ...
</select>
}
<div id="Div1">
</div>
<div id="Div2">
</div>
Move this to another partial view:
#using (Ajax.BeginForm("ProcessData", new AjaxOptions{UpdateTargetID = "Div2"}))
{
// ...
<input type="submit" value="GO!" />
}

Change id attribute of an html.editorfor helper in MVC4 with Razor

I have looked through some various answers related to this but all were for mvc3 or not for Razor.
I have a single page that has multiple forms, in partial views, that are for editing a different models. However, most of those models have a 'name' field. I am looking to be able to specify an editorfor with a specific id as such:
<div class="editor-label">
#Html.LabelFor(model => model.Name)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Name, new {id = "PersonName"})
#Html.ValidationMessageFor(model => model.Name)
</div>
I have tried various other things but have not found a satisfactory way to handle this. The two choices I seem to have are:
1) Create a form manually using the normal html helpers and construct the model in the controller
2) Rename all the fields in the model to fit the format
Neither of these are exciting to me so I am hoping for an alternative but am afraid that the id is what is used when binding the form to a model.
I realise this is somewhat late but I just ran into this problem myself... I thought "there has to be a way to use the EditorFor and still override the ID"... there is. Do the following and your input has an ID and Name of whatever you like.
#Html.EditorFor(model => model.myField, null, "txtMyField")
Hope this helps and hope it helps other people too.
Note: from what I understand, the reason this is typically not done is to avoid other, dynamically created controls ID's and Names from conflicting.
Change your helper from EditorFor to TextBoxFor the EditorFor, doesn't have a overload to override html properties
Change this
#Html.EditorFor(model => model.Name, new {id = "PersonName"})
To this
#Html.TextBoxFor(model => model.Name, new {id = "PersonName"})
Based on bob's comment :
EditorFor #Html.EditorFor(model => model.FieldName, new { htmlAttributes = new { id = "myuniqueid"} })
Credits to Bob.
Alternatively you can add htmlAttributes
#Html.EditorFor(Model => Model.myField, new { htmlAttributes = new { #class = "form-control", #id = "txtMyField" } })

Orchard Module - How to return strongly typed Model rathen than dynamic from Driver

I created a ContactUs module that sends email when user click on Submit button.
Everything works perfectly. However, I am curious if it is possible to return a strongly typed Model rather than dynamic class.
For example, following is my Drivers\ContactUsDriver.cs Display function:
protected override DriverResult Display(ContactUsPart part, string displayType, dynamic shapeHelper)
{
return ContentShape("Parts_ContactUs",
() => shapeHelper.Parts_ContactUs(
Name: part.Name));
}
As you can see, above is returning a dynamic Parts_ContactUs.
Now, here's snapshot of my Views\Parts\ContactUs.cshtml:
#model dynamic
#using (Html.BeginForm("Send", "ContactUs", new { area = "ContactUs" }, FormMethod.Post))
{
<fieldset>
<legend>Contact Us</legend>
<div id="contact-us" class="area">
#Html.TextBox("Name", "")
</div>
<div id="submitArea" class="button">
<input type="submit" value="Submit Message">
</div>
</fieldset>
}
As you can see above the View is bound to #model dynamic. As a result, I have to do following
#Html.TextBox("Name", "")
Is there a way I can bind to Model say ContactUsModel and thus do following instead?
#Html.TextBoxFor(m => m.Name)
Particularly, I am interested so I can write a jquery validation with DataAnnotation attribute.
It's perfectly possible. Just provide a desired model type as your first argument when creating a shape:
protected override DriverResult Display(
ContactUsPart part,
string displayType,
dynamic shapeHelper)
{
return ContentShape("Parts_ContactUs",
() => shapeHelper.Parts_ContactUs(typeof(MyClass), Name: part.Name));
}