How to update different targets with MVC Ajax helper? - asp.net-mvc

i'm having a hard time dealing with MVC Ajax helper and trying to refresh certain part of the page. In my code, I have a situation like this:
<div id="ajaxLoadedContent">
<table>
<tr>
<th>
<%: Html.Label("ProductId") %>
</th>
<th>
<%: Html.Label("ProductName") %>
</th>
<th>
</th>
</tr>
<% foreach (var product in Model.Products)
{
%>
<tr>
<td>
<%: direccion.ProductId %>
</td>
<td>
<%: direccion.ProductName %>
</td>
<td>
<% using (Ajax.BeginForm("EditProduct", "MyController",
new { ContainerDiv = "ShowEditProducts" },
new AjaxOptions() { UpdateTargetId = "ShowEditProducts", OnSuccess = "updatePlaceholder" }))
{%>
<%: Html.Hidden("ProductId", product.ProductId)%>
<input type="submit" value="Edit" />
<%} %>
</td>
</tr>
<% } %>
</table>
<script type="text/javascript">
function updatePlaceholder(context) {
var html = context.get_data();
var placeholder = context.get_updateTarget();
$(placeholder).html(html);
return false;
}
</script>
<div id="ShowEditProducts"></div>
</div>
Now, when I press the Edit button, then the 'Editor View' appears, but the problem is that when i Submit that form, then there are two options:
The data is OK, the changes are made, the form dissapears and the list is refreshed.
The data is NOT OK, the forms stays and the validation messages appear.
The problem is that the UpdateTargetId are different for each option.
Option 1 refreshes "ShowEditProducts", while Option 2 refreshes "ajaxLoadedContent". Also, ehe 'Edit View' contains JavaScript that is executed when the view is loaded using the "Edit" button but is not executed when the form contains errors and is re-rendered.
The code of the EditView looks like this:
<% using (Ajax.BeginForm("SubmitEdit", new AjaxOptions() { UpdateTargetId = Model.ContainerDiv}))
{%>
<script type="text/javascript">
// My JavaScript
</script>
<table>
<tr>
<td>
<%: Html.Label("Id")%></br>
<%: Html.TextBoxFor(x => x.Product.ProductId)%></br>
</td>
<td>
<%: Html.Label("Name")%></br>
<%: Html.TextBoxFor(x => x.Product.ProductName)%><
</td>
</tr>
</table>
<input type="submit" value="Save" />
<%: Ajax.ActionLink("Cancel", "EmptyView","Shared", new AjaxOptions() { UpdateTargetId = Model.ContainerDiv})%>
<% } %>
So, now my two big problems are:
1. The JavaScript of the 'Edit View' don't get executed when the the view is re-renderd.
2. How can i update one target is there are errors and another one when there are not.
Thanks

The object being replaced is being set on this line:
var placeholder = context.get_updateTarget();
So instead, you should add a condition here. Eg, instead of returning HTML directly, if you return an object that looks like this:
public class Result
{
public bool Success { get; set; }
public string Html { get; set; }
}
Then you could do something like this:
var result = response.get_response().get_object();
if (result.Success)
$("#ShowEditProducts").html(html);
else
$("#ajaxLoadedContent").html(html);
Alternatively, instead of returning a Success boolean, you could have a property for the ID of the control to replace. This would be more reusable, and might make sense if you want the decision of which control to replace to be done server-side.

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);
}

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>

ASP.Net MVC 2 - drop down list that controls a grid

I am just starting on MVC so this should be an easy question to answer.
I am using MVC 2 for ASP.Net.
I have a drop down list which when changes should cause the grid to change as well.
I have found a way to catch the change selection event and refresh the whole form using the code below.
The $('#TheForm').submit(); command causes the Index method of the Controller to run, and resets everything back as before.
What I want of course is for this method to pick up the new value of the dropdownlist, extract and display the new data in the View accordingly.
Is this how I should do it, or should I do more on the client side instead?
$(function () {
$("#StatusId").change(function () {
$('#TheForm').submit();
});
});
<p>
<% using (Html.BeginForm("Index", "Home", FormMethod.Post, new { id = "TheForm" })){%>
<%: Html.DropDownList("StatusId", (SelectList) ViewData["Status"]) %>
<%}%>
</p>
<p>
<% if (Request.IsAuthenticated) { %>
<%: Html.ActionLink("Add new item", "Add") %>
<% } %>
</p>
<table>
<tr>
<th>
Title
</th>
<th>
Date
</th>
<th>
Status
</th>
</tr>
<% foreach (var item in Model) { %>
<tr>
<td>
<%: Html.ActionLink(item.Title, "Details", new { id = item.ItemCode }) %>
</td>
<td>
<%: String.Format("{0:g}", item.DateCreated) %>
</td>
<td>
<%: item.Status %>
</td>
</tr>
<% } %>
</table>
<p>
<%: Html.Label("Page: ") %>
<% for (int i = 1; i < Convert.ToInt32(ViewData["NumberOfPages"]); i++)
{ %>
<%: Html.ActionLink(i.ToString(), "Index", new { page = i })%>
<% } %>
</p>
The Index action needs a parameter StatusId.
It has to filter the list of items by StatusId. If this does not work please post the code of the action.

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

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.

Ajax.ActionLink, how to send the selected object in the partial view? asp.net mvc

I guess this is a noob question, but here it comes:
I have a list of products:
<% foreach (var item in Model) { %>
<tr>
<td>
<%= Html.Encode(item.code) %>
</td>
<td>
<%= Html.Encode(String.Format("{0:g}", item.date)) %>
</td>
<td>
<%= Html.Encode(item.category) %>
</td>
<td>
<%= Html.Encode(item.description) %>
</td>
<td>
<%= Html.Encode(String.Format("{0:F}", item.price)) %>
</td>
......
}
And a partial view after all of these (in the same page):
<div id="productForEdit">
<fieldset>
<legend>Your Selected Product</legend>
<% Html.RenderPartial("~/Views/Products/Edit", productObject); %>
</fieldset>
</div>
How do I use Ajax.ActionLink, so that when I will click the description of a product, the product will be plugged in the Partial View from the bottom of the page?
I tried some combination with UpdateTargetId="productForEdit", but I had no success.
The purpose is to have a quick edit tool in the page.
I think this should work:
<td>
<%= Ajax.ActionLink(Html.Encode(item.description), /* link text */
"GetProduct", /* action name */
"Product", /* controller name */
new { productCode = Model.code }, /* route values */
new AjaxOptions() { InsertionMode = InsertionMode.Replace,
UpdateTargetId = "productForEdit" }) %>
</td>
This does expect a ProductController with an action named GetProduct, which takes a parameter productCode. Have this action return the view "Products/Edit".
You could also pass the whole product as a parameter to the action method, but that changes nothing to the basic idea. Good luck!

Resources