Bind my form to a Model - asp.net-mvc-3

I have a ViewModel which contains a List of my Model, like so:
public class OrderConfirm
{
public ICollection<DayBookQuoteLines> SalesLines { get; set; }
public ICollection<DayBookQuoteLines> LostLines { get; set; }
public string Currency { get; set; }
}
I then use this ViewModel in my View like so:
#model btn_intranet.Areas.DayBook.Models.ViewModels.OrderConfirm
#{
ViewBag.Title = "Daybook - Order Confirmation";
}
<h6>Sales Lines</h6>
<div id="SalesOrders">
#using (Ajax.BeginForm("ConfirmSalesOrder", new AjaxOptions()
{
InsertionMode = InsertionMode.Replace,
UpdateTargetId = "SalesOrders",
OnBegin = "SalesOrderConfirm"
}))
{
#foreach(var item in Model.SalesLines)
{
<p>#item.ItemName</p>
<p>#item.Qty</p>
#* Other Properties *#
}
<input type="submit" value="Submit Sales Order" />
}
</div>
<h6>Lost Lines</h6>
<div id="LostOrders">
#using (Ajax.BeginForm("ConfirmLostOrder", new AjaxOptions()
{
InsertionMode = InsertionMode.Replace,
UpdateTargetId = "LostOrders",
OnBegin = "LostOrderConfirm"
}))
{
#foreach(var item in Model.SalesLines)
{
<p>#item.ItemName</p>
<p>#item.Qty</p>
#* Other Properties *#
}
<input type="submit" value="Submit Lost Order" />
}
</div>
The problem is, in my [HttpPost] actions, both ConfirmSalesOrder and ConfirmLostOrder. The value of my Model passed as a parameter is null:
[HttpPost]
public ActionResult ConfirmSalesOrder(List<DayBookQuoteLines> quoteLines)
{
// Process order...
return PartialView("Sales/_ConfirmSalesOrder");
}
so quoteLines is null. How can I bind the form to my model?

You don't have any input field in your form that will send the values to the server. You are only displaying them. That's why they are null when you submit the form => nothing is sent to the server.
But if inside this form the user is not supposed to modify any of the values all you need to do is to pass an id to the controller action that will allow you to fetch the model from the exact same location from which you fetched it in your GET action that rendered this form.
In this case your action will look like this:
[HttpPost]
public ActionResult ConfirmSalesOrder(int id)
{
List<DayBookQuoteLines> quoteLines = ... fetch them the same way as in your GET action
// Process order...
return PartialView("Sales/_ConfirmSalesOrder");
}
If on the other hand the user is supposed to modify the values in the form you need to provide him with the necessary input fields: things like textboxes, checkboxes, radio buttons, dropdownlists, textereas, ... And in order to generate proper names for those input fields I would recommend you using editor templates instead of writing foreach loops in your views.
UPDATE:
Seems like the user is not supposed to edit the data so there are no corresponding input fields. In this case in order to preserve the model you could during the AJAX request you could replace the Ajax.BeginForm with a normal Html.BeginForm an then manually wire up the AJAX request with jQuery. The advantage of this approach is that now you have far more control and you could for example send the entire model as a JSON request. To do this you could store the model as a javascript encoded variable inside the view:
<script type="text/javascript">
var model = #Html.Raw(Json.Encode(Model));
</script>
and then AJAXify the form:
$('#formId').submit(function() {
$.ajax({
url: this.action,
type: this.method,
contentType: 'application/json',
data: JSON.stringify({ quoteLines: model }),
success: function(result) {
$('#someTargetIdToUpdate').html(result);
}
});
return false;
});

Related

how to pass a model to view in ajax-based request in mvc4

I'm creating an ajax-based Quiz in MVC. Below is the Question view. When the form is submitted I save the user selection in the controller then need to send the next question to the view without reloading the page. Is it possible to send/update the model from the controller in the ajax request
#model DataAccess.Question
#{
ViewBag.Title = "Survey";
Layout = "~/Views/Shared/_Layout.cshtml";
}
#using (Ajax.BeginForm("Survey", "Tools", new AjaxOptions { UpdateTargetId = "QuestionContent", HttpMethod = "Post", InsertionMode = InsertionMode.Replace }, new { QuestionId = Model.QuestionId }))
{
<div id="QuestionContent">
<h2>Welcome To Quiz</h2>
<fieldset>
<p>
Question
#Model.QuestionId of #ViewBag.QuestionCount:
</p>
<p>
#Model.Description.
</p>
<ul style="list-style:none;">
#foreach (var item in Model.Answers)
{
<li> #Html.RadioButton("ChoiceList", item.score) #item.AnswerDesc</li>
}
</ul>
<input type="submit" value="Next" id="submitButton" />
</fieldset>
</div>
}
It's much easier to implement an AJAX POST using jQuery and return a JSON object that contains all the next Q&A info. Use js/jQuery to set <div>'s or any other html element. Posting back and reloading is such a pain and becoming an outdated approach.
For example you could have this ViewModel class:
public class Answer
{
public int QuestionId {get; set;}
public string Answer {get; set;}
}
Build a view that has a div & input control for the Q & A.
Implement the Answer Button to POST via AJAX:
$.ajax({
type: "POST",
url: "/Exam/HandleAnswer" ,
data: { QuestionId: _questionId, Answer: $("#txt_answer").val() },
success: function (resp) {
if (resp.Success) {
$("#div_Question").text( resp.NextQuestionMessage);
_questionId = resp.NextQuestionId,
$("#txt_answer").val(''"); //clear
}
else {
alert(resp.Message);
}
}
});
In your ExamController:
[HttpPost]
public ActionResult HandleAnswer(Answer qa)
{
//use qa.QuestionId to load the question from DB...
//compare the qa.Answer to what the DB says...
//if good answer get next Question and send as JSON or send failure message..
if (goodAnswer)
{
return Json(new { Success = true, NextQuestionMessage = "What is the capital of of Texas", NextQuestionId = 123});
}
else{
return Json(new { Success = false, Message = "Invalid response.."});
}
}

JQuery AJAX Post to Controller data incorrect

Here is what I am trying to do:
My goal is to display a list of Trending Opinions (A custom Model) from the page's model when the page loads. If a user clicks the "Show more Trending Opinions" button, it uses ajax to call a method on a controller that will then retrieve an additional number of items, come back to the page and display them. Then it adds say 20 more. Then they can repeat the process and click it again, etc.
Exactly the same as a normal site does when you click "Show More" on a list of items.
If the way I am approaching this is incorrect and you know of any tutorial (or just out of your head) showing the correct way to do this in MVC 4, please let me know. I am not dead-set on the way I am doing it at the moment, this is just the "correctest" way I have found.
I followed the answer to a similar question: How to Update List<Model> with jQuery in MVC 4
However, the data coming through to my controller is incorrect and I can't figure out what the issue is.
Let me put as much info as I can, because I have no idea where the error may be.
Model for page (OpinionModel has a few public properties):
public class IndexModel
{
public IList<OpinionModel> TopTrendingOpinions { get; set; }
}
The View:
<div id="TrendingOpinions">
<p>What is trending at the moment</p>
#using (Html.BeginForm("LoadMoreTrendingOpinions", "AjaxHelper",
method: FormMethod.Post,
htmlAttributes: new { #class = "form-horizontal", id = "LoadTrendingOpinionsForm" }))
{
#Html.EditorFor(x => x.TopTrendingOpinions)
<input type="submit" value="Load More Trending Opinions" />
}
<script type="text/javascript">
$('#LoadTrendingOpinionsForm').submit(function () {
$.ajax({
url: this.action,
type: this.method,
data: {
topTrendingOpinions: $(this).serialize()
},
success: function (result) {
alert(result);
}
});
return false;
});
</script>
</div>
**There is also an EditorTemplate for my model.
The Controller:**
[HttpPost]
public ActionResult LoadMoreTrendingOpinions(IList<MyGoldOpinionMVC.Models.OpinionModel> topTrendingOpinions)
{
var dataHelper = new Data.DataHelper();
var moreTrendingOpinions = dataHelper.LoadMoreTrendingOpinions(topTrendingOpinions.LastOrDefault().Id);
// var partialView = PartialView("../PartialViews/_ListOfPostedOpinion", moreTrendingOpinions);
return View(moreTrendingOpinions);
}
So here is the order of events:
When running the site, the form shows a list of OpinionModels (Using the Editor Template displaying correct data). When I click the SUBMIT button, it goes to the controller (I have a breakpoint) and the data for the "topTrendingOpinions" parameter is a List with one item in it, but that item is null. So in other words, it is not passing through the list that is clearly being used to populate the form.
The only way I have been able to get a list to post back to the controller is to build it manually with jquery. my understanding is this.serialize on a form click is going to try to serialize the whole from which would get very ugly. How I would do this is
<input type="button" class="btnMore" value="Load More Trending Opinions" />
$('.btnMore').on('click', function () {
$.ajax({
url: '#Url.Action("LoadMoreTrendingOpinions", "AjaxHelper")',
type: 'post',
contentType: 'application/json; charset=utf-8',
data: {
Id: '#ViewBag.Id'
},
success: function (result) {
//add results to your table
}
});
});
and set the id of the last record sent through the view bag on your controller so you have a reference to go off of for pulling the next chunk. Let me know if you have any questions
When posting lists you have to be really careful that your inputs are named correctly. If they are not, the default model binder fails to parse them into classes when posted resulting the object being null in the controller.
In your case you are posting a list of models inside a model, but not the whole model. I'd use PartialView instead of editortemplate, just to make working with field names easier. In my example we are posting a list of FooModels contained in IndexModel
Model
public class FooModel
{
public string Foo { get; set; }
public string Bar { get; set; }
}
public class IndexModel
{
public IList<FooModel> Foos { get; set; }
}
View
#using (Html.BeginForm("LoadMoreTrendingOpinions","AjaxHelper",
method: FormMethod.Post,
htmlAttributes: new { #class = "form-horizontal", id = "LoadTrendingOpinionsForm" }))
{
#Html.Partial("FooModelsPartial", Model.Foos)
<input type="submit" value="Load More Trending Opinions" />
}
FooModelsPartial
#model IList<FooModel>
#for (int i = 0; i < Model.Count(); i++)
{
#Html.EditorFor(model => model[i].Foo)
#Html.EditorFor(model => model[i].Bar)
}
Notice how we are using for instead of foreach loop. This is because editors in foreach loop are not named correctly. In this case we want our fields to be [0].Foo, [0].Bar, [1].Foo, [1]. Bar etc.
Controller:
[HttpPost]
public ActionResult LoadMoreTrendingOpinions(IList<FooModel> topTrendingOpinions)
{
// do something with toptrending thingy
var model = new IndexModel();
model.Foos = topTrendingOpinions;
return View("Index", model);
}
Now the real question in my opinion is do you really want to post the whole list of models to get bunch of new ones related to one of them? Wouldn't it be more convenient to post the id of opinion you'd want to read more of, returning partialview containing the requested more trending opinions and appending that to some element in the view with jquery?
Html:
#using (Html.BeginForm("LoadMoreTrendingOpinions","AjaxHelper",
method: FormMethod.Post,
htmlAttributes: new { #class = "form-horizontal", id = "LoadTrendingOpinionsForm" }))
{
<div id="more">#Html.Partial("FooModelsPartial", Model.Foos)</div>
<input type="submit" value="Load More Trending Opinions" />
}
Javascript:
$('#LoadTrendingOpinionsForm').submit(function () {
$.ajax({
url: this.action,
type: this.method,
data: {
id: 1 /* The id of trending item you want to read more of */
},
success: function (result) {
$("#more").html(result)
}
});
return false;
});
Controller:
[HttpPost]
public ActionResult LoadMoreTrendingOpinions(int id)
{
var moreTrendingOpinions = dataHelper.LoadMoreTrendingOpinions(id);
return PartialView("FooModelsPartial", moreTrendingOpinions);
}

ASP MVC-3 : Problems updating the data of an AJAX form after making a post

I have the following problem when updating a for via AJAX after it is submitted. For some reason some hidden fields that are on the HTML that is returned are not being updated, which is weird because when I run the debugger they appeared to have the correct value.
This is the relevant part of my form
<div id="itemPopUpForm">
#{Html.EnableClientValidation();}
#Html.ValidationSummary()
<div id="formDiv">
#{ Html.RenderPartial("ItemData", Model, new ViewDataDictionary() { { "Machines", ViewBag.Machines }, { "WarehouseList", ViewBag.WarehouseList }, { WebConstants.FORM_ID_KEY, #ViewData[WebConstants.FORM_ID_KEY] } }); }
</div>
</div>
Then the partial view contains hidden fields like these which are the ones not being updated
#using (Html.BeginForm("Index", "Item", FormMethod.Post, new { id = "frmItem", name = "frmItem" }))
{
#Html.AntiForgeryToken()
#Html.HiddenFor(model => model.Item.SodID)
#Html.HiddenFor(model => Model.Item.ItemID) //The itemID needs updating when an item is copied
#Html.HiddenFor(model => model.Item.Delivery.DeliveryAddressID, new { #id = "delAddressID" })
And this is the javascript method that updates the form
function ajaxSave() {
if (!itemValid()) return;
popup('ajaxSplash');
$.ajax({
type: "POST",
url: '#Url.Action("Index")',
data: $("#frmItem").serialize(),
success: function (html) {
console.log(html);
$("#formDiv").html(html);
initItemPage();
alert("Item was saved successfully");
},
error: function () { popup('ajaxSplash'); onFailure(); }
});
}
The action Index returns the Partial View "ItemData" and when I check the Item Model it does have the correct value, but when I see the html returned it is still set to 0.
If you intend to modify a model property in your POST action don't forget to remove it from ModelState first, otherwise HTML helpers will use the originally posted value when rendering:
[HttpPost]
public ActionResult Index(MyViewModel model)
{
// remove the value from modelstate
ModelState.Remove("Item.ItemID");
// update the value
model.Item.ItemID = 2;
return PartialView(model);
}
I'm having the same problem and it seems like the helper HiddenFor evaluates with required unobtrusive validation even if in the model one does not annotate the property with [Required].
The HTML rendered by #Html.HiddenFor(m=>m.Step) is :
<input data-val=​"true" data-val-number=​"The field Step must be a number." data-val-required=​"The Step field is required." id=​"Step" name=​"Step" type=​"hidden" value=​"2">​
Hence, it is why it works if we remove it from the ModelState.
Removing the property from the ModelState seems to me like a hack. I would prefer to use
<input type="hidden" id="Step" name="Step" value="#Model.Step" />
instead of the Html.HiddenFor helper.
You can also implement you own HiddenFor helper.

mvc3 how do I clear the submitted form values

I am new to asp .net mvc3. I am trying to create a failrly simple blog comments section.
I have a CommentsViewModel
public class CommentsViewModel
{
public CommentModel NewComment { get; set; }
public IList<CommentModel> CommentsList { get; set; }
}
The corresponding view is like
<div id="CommentsArea">
#Html.Partial("CommentsList", Model.CommentsList)
</div>
#using (Ajax.BeginForm("Create",
"Comment",
new { id = Model.NewComment.detailsId, comment = Model.NewComment },
new AjaxOptions { UpdateTargetId = "CommentsArea" ,
HttpMethod = "Post",
InsertionMode = InsertionMode.Replace}))
{
<div class="display-label">Add Comment</div>
<div class="display-field">
#Html.EditorFor(model => Model.NewComment.Body)
</div>
<input type="submit" value="Post" />
}
Now when user enters Post button I want the "CommentsArea" to be updated with the new comments list and also the form values to be cleared with empty text.
Here is the Post method:
[HttpPost]
public ActionResult Create(int id, CommentModel newcomment)
{
var newsItem = m_db.NewsList.Single(news => news.Id == id);
if (!string.IsNullOrWhiteSpace(newcomment.Body))
{
newcomment.detailsId = id;
newsItem.Comments.Add(newcomment);
m_db.SaveChanges();
}
return PartialView("CommentsList", newsItem.Comments);
}
Now when user clicks Post button the list gets updated properly,
but the form values are not cleared. i.e. if I posted a comment "New Comments", the comments list gets updated with "New Comments", but that text remains inside the edit box of the form.
How do I clear that text?
just call a js function on success of form submit.
#using (Ajax.BeginForm("Create",
"Comment",
new { id = Model.NewComment.detailsId, comment = Model.NewComment },
new AjaxOptions { OnSuccess="ClearInput", HttpMethod = "Post",}))
on js function render create view again
function ClearInput(){
//call action for render create view
$.ajax({})
}

Editor templates/BeginForm does not update the values after returning from action but while debugging i see the data

#using (Ajax.BeginForm("SaveItemAndProperties", "HomeBuilder",
new AjaxOptions
{
UpdateTargetId = "divSaveItemAndProps",
InsertionMode = InsertionMode.Replace
}))
{
#Html.EditorForModel()
<input type="submit" value="Submit" />
}
In Model which is called from EditorForModel
#Html.EditorFor(m => m.PropertyValues)
PropertyValues is a list of properties and is a calling a EditorTemplate.
From the Action I change the value and then try to update the data back to the View
[AcceptVerbs(HttpVerbs.Get | HttpVerbs.Post)]
public PartialViewResult SaveItemAndProperties(PropertyBuilderViewModel modelValues)
{
//Change on property in modelValues
return PartialView("PropertyBuilderControl", modelmodelValues);
}
When i am debugging i see the data propertly but it does not display in the view.
Any idea why it is doing so.
What are you changing in your action? HTML helpers such as TextBoxFor, HiddenFor, DropDownListFor, CheckBoxFor, ... first look at ModelState when binding and after that in the model. So if in your controller action you intend to do something like this:
[AcceptVerbs(HttpVerbs.Get | HttpVerbs.Post)]
public PartialViewResult SaveItemAndProperties(PropertyBuilderViewModel modelValues)
{
modelValues.Foo = "some new value";
return PartialView("PropertyBuilderControl", modelmodelValues);
}
make sure you remove that value from the model state or you won't see any updates once you render the view again:
[AcceptVerbs(HttpVerbs.Get | HttpVerbs.Post)]
public PartialViewResult SaveItemAndProperties(PropertyBuilderViewModel modelValues)
{
ModelState.Remove("Foo");
modelValues.Foo = "some new value";
return PartialView("PropertyBuilderControl", modelmodelValues);
}

Resources