binding asp.net mvc form element to complex object for posting to controller - asp.net-mvc

I am trying to refactor to avoid parsing the FormCollection from the view so i changed this to pass in an strongly typed object. My form elements are the same names as the properties on the LinkUpdater Object. But when i put a breakpoint on the first link in the controller all of the properties are null.
any ideas or suggestions?
View:
<%using (Ajax.BeginForm("AddNewLink", "Links", new AjaxOptions { UpdateTargetId = "LinkList", LoadingElementId = "updating", OnSuccess = "done" }))
{ %>
<fieldset style="text-align:left">
<table>
<tr><td>Url:</td><td> <input style="width:500px" type="text" name="URL" /></td></tr>
<tr><td>Description: </td><td><input style="width:400px" type="text" name="Description" /></td></tr>
<tr><td>Tags: </td><td><input style="width:400px" id="Tags" name="tags" type="text" /></td></tr>
<tr><td><input type="submit" value="Add Link" name="submit" /></td></tr>
</table>
</fieldset>
<% } %>
Controller Post:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult AddNewLink(LinkUpdater linkUpdater_)
{
string[] tags = linkUpdater_.Tags.Replace(" ", "").Split(',');
linkRepository.AddLink(linkUpdater_.URL, linkUpdater_.Description, tags);
.....
}
LinkUpdater class:
public class LinkUpdater
{
public string URL;
public string Description;
public string Tags;
}

Model binder in MVC binds to properties, while you use fields. Change to
public string URL { get; set; }
And by the way, there're other drawbacks, like if you use private set, it will silently skip binding, too.

Is there any particular reason your are not using the strongly-typed HTMLHelpers to render your input fields?
<%using (Ajax.BeginForm("AddNewLink", "Links", new AjaxOptions { UpdateTargetId = "LinkList", LoadingElementId = "updating", OnSuccess = "done" }))
{ %>
<fieldset style="text-align: left">
<table>
<tr>
<td>
Url:
</td>
<td>
<%=Html.TextBox("URL", Model.URL, new { style = "width:500px;" }) %>
</td>
</tr>
<tr>
<td>
Description:
</td>
<td>
<%=Html.TextBox("Description", Model.Description, new { style = "width:400px;" }) %>
</td>
</tr>
<tr>
<td>
Tags:
</td>
<td>
<%=Html.TextBox("Tags", Model.Tags, new { style = "width:400px;" }) %>
</td>
</tr>
<tr>
<td>
<input type="submit" value="Add Link" name="submit" />
</td>
</tr>
</table>
</fieldset>
<% } %>
I'm not sure it will fix your problem, but it's a step in the right direction at least.

Related

How to update Partial View without full replacement

I have this partial view. This works, ie, when the user clicks the button, an ajax trip is made to the server, and it updates the partial view and it comes down and replaces the current div with the updated Div and shows the Promo Message.
However, it seems there should be a better way to do this. In other words, is it necessary to replace the entire partial view? Isn't there a way to send just the data up to the server, and then update just the message when it gets back, like maybe via a JSON call?
Controller:
public ActionResult ApplyPromoCode(OrderViewModel orderViewModel) {
orderViewModel.PromoMessage = "Promo has been applied";
return PartialView("PromoPartial", orderViewModel);
}
Partial View:
#model NTC.PropertySearch.Models.OrderViewModel
#using (Ajax.BeginForm("ApplyPromoCode", "OrderSummary", new AjaxOptions { InsertionMode = InsertionMode.Replace, UpdateTargetId = "promo" }))
{
<div id="promo">
<table>
<td>
#Html.LabelFor(m => m.PromoCode)
</td>
<td>
#Html.TextBoxFor(m => m.PromoCode)
</td>
<td>
#Html.ValidationMessageFor(m => m.PromoCode)
</td>
<td>
<input type="submit" value="Apply Promo Code" />
</td>
<td>
#Html.DisplayFor(m=> m.PromoMessage)
</td>
</table>
</div>
}
you can do this to
Controller
public ActionResult ApplyPromoCode(OrderViewModel orderViewModel) {
//your processing code
return Content("Promo has been applied");
}
View
#model NTC.PropertySearch.Models.OrderViewModel
#using (Ajax.BeginForm("ApplyPromoCode", "OrderSummary", new AjaxOptions { UpdateTargetId = "pcode" }))
{
<div id="promo">
<table>
<td>
#Html.LabelFor(m => m.PromoCode)
</td>
<td>
#Html.TextBoxFor(m => m.PromoCode)
</td>
<td>
#Html.ValidationMessageFor(m => m.PromoCode)
</td>
<td>
<input type="submit" value="Apply Promo Code" />
</td>
<td>
<div id="pcode"></div>
</td>
</table>
</div>
}
Instead of returning a PartialView you can always return a JSON object/array or some XML and use jQuery/JavaScript on your callback function to update the values of your input fields.
Here's an example of some code I use to return JSON from a Controller:
public ActionResult CurrentTags(int entityID)
{
Entity entity = db.Entity.Find(entityID);
var tags = from tag in entity.Tag
select new
{
id = tag.Name,
text = tag.Name
};
return this.Json(tags, JsonRequestBehavior.AllowGet);
}

How to send Id to Controller from Ajax Input Button

I have this grid which has an edit button. How do I add code to the input button so that the value of the Id is sent to the Controller?
#using (Ajax.BeginForm("EditLineItem", "OrderSummary", new AjaxOptions() { InsertionMode = InsertionMode.Replace, UpdateTargetId = "content" })) {
<div id="summaryGrid">
<table >
<tr>
<th>Report Type</th>
<th>Borrower Name</th>
<th>Property Address</th>
<th>Est Comp Date</th>
<th>Report Price</th>
<th>Exp Fee</th>
<th>Disc.</th>
<th>Total Price</th>
</tr>
#{
foreach (var item in Model) {
<tr>
<td >#item.ReportName</td>
<td >#item.BorrowerName</td>
<td >#item.Address</td>
<td >#item.EstimatedCompletionDate</td>
<td >#item.ReportPrice</td>
<td >#item.ExpediteFee</td>
<td >#item.Discount</td>
<td >#item.TotalPrice</td>
<td >#item.Id</td>
<td ><input type="submit" value="Edit" /></td>
</tr>
}
}
</table>
</div>
}
just put a name on your input button.
<input type="submit" name="id" value="edit" />
Then on your action, you should be able to get the value for id.
If you want more complexity then you are going to have to rethink the way you are doing it. Most likely by writing your own JQuery methods.
$('input.edit').on('click', function (evt) {
evt.preventDefault();
var values = $(this).data();
$.post($(this).attr('href'), values, function (result) { /*do something*/ });
});
Html :
<a href="/edit/1" class="edit" type="submit" data-id="1" data-method="edit" />
That's a start, but you could probably tweak it to fit your needs. At that point, you don't need to wrap the whole table with the Ajax.BeginForm.
To add to Khalid's answer: I tested with this form:
<form method="get">
<input type="submit" name="Id1" value="Edit" id="id1" />
<input type="submit" name="Id2" value="Edit" id="id2" />
<input type="submit" name="Id3" value="Edit" id="id3" />
</form>
The post looks like this when clicking on the third button:
http://localhost:34605/HtmlPage.html?Id3=Edit
In other words, the browser passes the name of whichever button is clicked.
This is an example of getting the Id in the controller:
if (Request.QueryString.HasKeys()) {
string key = Request.QueryString.GetKey(0);
int id;
int.TryParse(key.Substring(2, 1), out id);
Response.Write("You selected id: " + id);
}
I have since found an even easier way of doing this:
Use the <button> element instead of <input>
With <button> you can do this:
<button type="submit" value="#item.Id" name="id">Edit</button>
and then in the controller, all you need is this:
public ActionResult EditLineItem(int id)
{ //Do something with id}
Note that this does not work with IE6.

Save all data of MVC4 html grid

Can anyone give me an example of saving all html grid data in one time. I have a view like this.
#model IList<SURVEY.Models.Question>
#using (Html.BeginForm("Index", "Survey", new { ReturnUrl = ViewBag.ReturnUrl }, FormMethod.Post, new { #class = "form-3" }))
{
#foreach(var item in Model)
{
<tr>
<td>#item.Ans1</td>
<td align="center">
<label>
<input type="radio" name="optionAS_#item.QuestionId" value="1" id="optionAS_1" onclick="disableAs(this,#item.QuestionId,1)"/>
</label>
</td>
<td align="center">
<label>
<input type="radio" name="optionAS_#item.QuestionId" value="2" id="optionAS_1" onclick="disableAs(this,#item.QuestionId,2)"/>
</label>
</td>
</tr>
}
}
I am getting null value for these controls in controller post.
[HttpPost]
public ActionResult Index(IList<Question> ques)
{
return View();
}
I am getting ques is null here. Can anyone tell me how can I resolve this?
You should use html helpers to bind properties of your model, your code might be as follows:
#for(var i = 0; i < Model.Count; i++)
{
<tr>
<td>#Html.HiddenFor(_ => Model[i].Id)
Model[i].Ans1
</td>
<td align="center">
<label>
#Html.RadioButtonFor(_ => Model[i].Name)
</label>
</td>
...
</tr>
}
and so for. HiddenFor helper is needed to create hidden input to send Id value to server to give you ability to identify you object. Take a look into Html Helpers in MVC and you will have your model back to server when form is submitted.

ASP.Net MVC returning values from List of Checkboxes

I have a model with a property that is a List. MyObjects simply has an id, a description and a selected boolean property.
I have managed to display the items as checkboxes on my view. I did this via:
<%foreach (var cat in Model.DefaultCategories)
{%>
<tr>
<td>
<%=cat.Category %>
</td>
<td>
<%=Html.CheckBoxFor(x=>cat.Selected) %>
</td>
</tr>
<%
}%>
</table>
However, there is a problem. They all end up, when rendered, with the same names. Here's a portion of my list:
<tr>
<td>
Medical
</td>
<td>
<input id="cat_Selected" name="cat.Selected" type="checkbox" value="true" /><input name="cat.Selected" type="hidden" value="false" />
</td>
</tr>
<tr>
<td>
Salary
</td>
<td>
<input checked="checked" id="cat_Selected" name="cat.Selected" type="checkbox" value="true" /><input name="cat.Selected" type="hidden" value="false" />
</td>
</tr>
They have all been named "cat.Selected".
How can I resolve this?
And then, when I submit, I need to iterate through them. With different names, I assume I can get them in my HttpPost method:
[HttpPost]
public ActionResult Modify(int id, FormCollection formValues)
{
PayeeDto p = new PayeeDto { Name = Request.Form["name"], PayeeId = id };
Services.PayeeServices.Save(p);
return RedirectToAction("Index");
}
The FormCollection will have the different names? At the moment, it just has the single 'cat.selected' item.
There is a way you can submit collections to your action by using names with []. As described here http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx
<% for (int i = 0; i < Model.DefaultCategories.Count; i++) { %>
<td>
<input type="checkbox" name="[<%= i %>].Selected" <% Model.DefaultCategories[i].Selected ? "checked=\"checked\"" : string.Empty %>/>
</td>
<% }%>
Then your action can take a collection of models like so
public ActionResult Modify(int id, ICollection<UpdateModel> updates)
{}
I would recommend you using Editor Templates and stop writing loops in your views. They will take care of generating the proper names so that binding works. Example:
In your main view:
<table>
<thead>
<tr>
<th>Name</th>
<th>Selected</th>
</tr>
</thead>
<tbody>
<%: Html.EditorFor(x => x.DefaultCategories) %>
</tbody>
</table>
and then inside an editor template strongly typed to a Category (~/Views/Home/EditorTemplates/Category.ascx). Also if you want to get the corresponding Name back in your controller action you need to include it (probably as hidden field). Another technique involves adding only the id and then fetching back the relevant information from the database in your controller action:
<%# Control
Language="C#"
Inherits="System.Web.Mvc.ViewUserControl<AppName.Models.Category>" %>
<tr>
<td><%: Model.Name %></td>
<td>
<!-- include the category name as hidden field so that
we can fetch it back in the controller action
-->
<%: Html.HiddenFor(x => x.Name) %>
<%: Html.CheckBoxListFor(x => x.Selected) %>
</td>
</tr>
Now the naming convention is important here. If the DefaultCategories property on your view model is an IEnumerable<Category>, then the editor template needs to be called Category.ascx and placed in ~/Views/Home/EditorTemplates/Category.ascx or if it will be reused between multiple controllers in ~/Views/Shared/EditorTemplates/Category.ascx.
Also your controller action you are submitting to should use a view model as parameter:
[HttpPost]
public ActionResult Modify(MyViewModel model)
{
PayeeDto = Mapper.Map<MyViewModel, PayeeDto>(model);
Services.PayeeServices.Save(p);
return RedirectToAction("Index");
}
This may not be the best answer but I try not to use generated checkboxes in MVC.
I would change
<td>
<%=Html.CheckBoxFor(x=>cat.Selected) %>
</td>
To
<td>
<input type="checkbox" name="<%: cat.value %>" id="<%: cat.value %>" <% cat.Selected ? " checked=\"checked\" " : ""; %> />
</td>

Table row not updating correctly using MVC Ajax

I have a table with each row as an ascx. This control consists of a Ajax form which includes cells which might have inputs. Here is the ascx:
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<NerdDinner.Models.Dinner>" %>
<% using (Ajax.BeginForm("ViewAll", new AjaxOptions
{UpdateTargetId = "Dinner" + Model.DinnerID, InsertionMode = InsertionMode.InsertAfter, OnSuccess = "jsfunction"
})) {%>
<%= Html.Hidden("ID", Model.DinnerID)%>
<td><input type="submit" value="Save" /></td>
<td><strong>'<%=Model.Title%>'</strong></td>
<td><%=Model.EventDate.ToShortTimeString()%> on <%=Model.EventDate.ToShortDateString()%></td>
<td><%=Model.Address%></td>
<td><%= Html.TextBox("HostedBy", Model.HostedBy)%></td>
<td><%= Html.ActionLink("Edit", "Edit", new { id = Model.DinnerID })%></td>
<%} %>
After a submit, I complete my processing and send back the updated ascx to replace the existing one.
However this ascx gets constructed a little different than the original which causes it to render incorrectly
This is what the original ascx looks like in FF:
<tr id="Dinner5">
<form onsubmit="Sys.Mvc.AsyncForm.handleSubmit(this, new Sys.UI.DomEvent(event), { insertionMode: Sys.Mvc.InsertionMode.insertAfter, updateTargetId: 'Dinner5', onSuccess: Function.createDelegate(this, jsfunction) });" method="post" action="/NerdDinner/Dinners/ViewAll"/>
<input id="ID" type="hidden" value="5" name="ID"/>
<td>
</td>
<td>
</td>
<td>12:00 AM on 2/2/2010</td>
<td>ZSA2</td>
<td>
</td>
<td>
</td>
</tr>
This is what the returned control (ajaxContext.get_data()) looks like:
<tr id="Dinner1">
<form onsubmit="Sys.Mvc.AsyncForm.handleSubmit(this, new Sys.UI.DomEvent(event), { insertionMode: Sys.Mvc.InsertionMode.replace, updateTargetId: 'Dinner1' });" method="post" action="/NerdDinner/Dinners/ViewAll">
<input id="ID" type="hidden" value="1" name="ID"/>
<td>
</td>
<td>
</td>
<td>12:00 AM on 1/1/2010</td>
<td>ZSA1</td>
<td class="red-back">
</td>
<td>
</td>
</form>
</tr>
Notice the latter does not contain any tds directly under tr but the form encloses all tds and so nothing is rendered.
In IE, I get an error saying 'htmlfile: Unknown runtime error' in MicrosoftAjax.js.
I'm sure I'm missing something basic here. Any help would be appreciated. Thanks.
A few things. This code from your original snippet doesn't look quite right as the input is outside of the form. If you notice the end of the form tag is self closing in this case.
<form onsubmit="Sys.Mvc.AsyncForm.handleSubmit(this, new Sys.UI.DomEvent(event), { insertionMode: Sys.Mvc.InsertionMode.insertAfter, updateTargetId: 'Dinner5', onSuccess: Function.createDelegate(this, jsfunction) });" method="post" action="/NerdDinner/Dinners/ViewAll"/>
<input id="ID" type="hidden" value="5" name="ID"/>
In your code below it appears as though you're explicitly telling the form to encapsulate all of the td's, note the positioning of the curly braces for the BeginForm method.
<% using (Ajax.BeginForm("ViewAll", new AjaxOptions
{UpdateTargetId = "Dinner" + Model.DinnerID, InsertionMode = InsertionMode.InsertAfter, OnSuccess = "jsfunction"
})) {%>
<%= Html.Hidden("ID", Model.DinnerID)%>
<td><input type="submit" value="Save" /></td>
<td><strong>'<%=Model.Title%>'</strong></td>
<td><%=Model.EventDate.ToShortTimeString()%> on <%=Model.EventDate.ToShortDateString()%></td>
<td><%=Model.Address%></td>
<td><%= Html.TextBox("HostedBy", Model.HostedBy)%></td>
<td><%= Html.ActionLink("Edit", "Edit", new { id = Model.DinnerID })%></td>
<%} %>
I'm not quite sure what you're trying to accomplish but hopefully this gets you started in the right direction.
How about using a nested table? This should help it in Firefox.
<td>
<% using (Ajax.BeginForm("ViewAll", new AjaxOptions
{UpdateTargetId = "Dinner" + Model.DinnerID, InsertionMode = InsertionMode.InsertAfter, OnSuccess = "jsfunction"
})) {%>
<%= Html.Hidden("ID", Model.DinnerID)%>
<table><tr>
<td><input type="submit" value="Save" /></td>
<td><strong>'<%=Model.Title%>'</strong></td>
<td><%=Model.EventDate.ToShortTimeString()%> on <%=Model.EventDate.ToShortDateString()%></td>
<td><%=Model.Address%></td>
<td><%= Html.TextBox("HostedBy", Model.HostedBy)%></td>
<td><%= Html.ActionLink("Edit", "Edit", new { id = Model.DinnerID })%></td>
</tr>
</table>
<%} %>
</td>
Interesting- i ran into this EXACT problem just a few minutes ago.
Apparently, IE8 does not like to update elements, only divs.
this blog post got me on the right path, and i just used a div as my updatetargetid rather than the i was originally using. it's ugly now, but at least it works. i need to figure out how to make it look a little nicer.
hope this helps some!

Resources