Created
February 3, 2011 18:48
-
-
Save davecowart/809935 to your computer and use it in GitHub Desktop.
My implementation of http://blog.stevensanderson.com/2010/01/28/editing-a-variable-length-list-aspnet-mvc-2-style/
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<CHSweb.Models.ViewModels.PropertyDetailsViewModel>" %> | |
<%@ Import Namespace="CHSweb.Helpers" %> | |
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server"> | |
Details | |
</asp:Content> | |
<asp:Content ID="Content3" ContentPlaceHolderID="HeadContent" runat="server"> | |
<script src="/Scripts/jquery.form.js" type="text/javascript"></script> | |
<script src="/Scripts/PropertyEditor.js" type="text/javascript"></script> | |
</asp:Content> | |
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server"> | |
<div> | |
<h1><%: Model.Property.Name %></h1> | |
<div> | |
<%: Html.Hidden("propertyId", Model.Property.Id) %> | |
<% Html.RenderPartial("OperatingStatementList", Model.OperatingStatements); %> | |
</div> | |
</asp:Content> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public static class HtmlPrefixScopeExtensions | |
{ | |
private const string idsToReuseKey = "__htmlPrefixScopeExtensions_IdsToReuse_"; | |
public static IDisposable BeginCollectionItem(this HtmlHelper html, string collectionName) | |
{ | |
var idsToReuse = GetIdsToReuse(html.ViewContext.HttpContext, collectionName); | |
string itemIndex = idsToReuse.Count > 0 ? idsToReuse.Dequeue() : Guid.NewGuid().ToString(); | |
// autocomplete="off" is needed to work around a very annoying Chrome behaviour whereby it reuses old values after the user clicks "Back", which causes the xyz.index and xyz[...] values to get out of sync. | |
html.ViewContext.Writer.WriteLine(string.Format("<input type=\"hidden\" name=\"{0}.index\" autocomplete=\"off\" value=\"{1}\" />", collectionName, html.Encode(itemIndex))); | |
return BeginHtmlFieldPrefixScope(html, string.Format("{0}[{1}]", collectionName, itemIndex)); | |
} | |
public static IDisposable BeginHtmlFieldPrefixScope(this HtmlHelper html, string htmlFieldPrefix) | |
{ | |
return new HtmlFieldPrefixScope(html.ViewData.TemplateInfo, htmlFieldPrefix); | |
} | |
private static Queue<string> GetIdsToReuse(HttpContextBase httpContext, string collectionName) | |
{ | |
// We need to use the same sequence of IDs following a server-side validation failure, | |
// otherwise the framework won't render the validation error messages next to each item. | |
string key = idsToReuseKey + collectionName; | |
var queue = (Queue<string>)httpContext.Items[key]; | |
if (queue == null) { | |
httpContext.Items[key] = queue = new Queue<string>(); | |
var previouslyUsedIds = httpContext.Request[collectionName + ".index"]; | |
if (!string.IsNullOrEmpty(previouslyUsedIds)) | |
foreach (string previouslyUsedId in previouslyUsedIds.Split(',')) | |
queue.Enqueue(previouslyUsedId); | |
} | |
return queue; | |
} | |
private class HtmlFieldPrefixScope : IDisposable | |
{ | |
private readonly TemplateInfo templateInfo; | |
private readonly string previousHtmlFieldPrefix; | |
public HtmlFieldPrefixScope(TemplateInfo templateInfo, string htmlFieldPrefix) | |
{ | |
this.templateInfo = templateInfo; | |
previousHtmlFieldPrefix = templateInfo.HtmlFieldPrefix; | |
templateInfo.HtmlFieldPrefix = htmlFieldPrefix; | |
} | |
public void Dispose() | |
{ | |
templateInfo.HtmlFieldPrefix = previousHtmlFieldPrefix; | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<CHSweb.Data.Models.OperatingStatement>" %> | |
<%@ Import Namespace="CHSweb.Helpers" %> | |
<div id="operatingStatementFormContainer"> | |
<% using (Html.BeginCollectionItem("operatingStatements")) { %> | |
<%: Html.ValidationSummary(true) %> | |
<fieldset> | |
<legend>Fields</legend> | |
<div class="editor-label"> | |
<%: Html.LabelFor(model => model.Year) %> | |
</div> | |
<div class="editor-field"> | |
<%: Html.TextBoxFor(model => model.Year) %> | |
<%: Html.ValidationMessageFor(model => model.Year) %> | |
</div> | |
<div class="editor-label"> | |
<%: Html.LabelFor(model => model.RoomsAvailable) %> | |
</div> | |
<div class="editor-field"> | |
<%: Html.TextBoxFor(model => model.RoomsAvailable) %> | |
<%: Html.ValidationMessageFor(model => model.RoomsAvailable) %> | |
</div> | |
<div class="editor-label"> | |
<%: Html.LabelFor(model => model.RoomsOccupied) %> | |
</div> | |
<div class="editor-field"> | |
<%: Html.TextBoxFor(model => model.RoomsOccupied) %> | |
<%: Html.ValidationMessageFor(model => model.RoomsOccupied) %> | |
</div> | |
<div class="editor-label"> | |
<%: Html.LabelFor(model => model.Occupancy) %> | |
</div> | |
<div class="editor-field"> | |
<%: Html.TextBoxFor(model => model.Occupancy) %> | |
<%: Html.ValidationMessageFor(model => model.Occupancy) %> | |
</div> | |
<div class="editor-label"> | |
<%: Html.LabelFor(model => model.ADR) %> | |
</div> | |
<div class="editor-field"> | |
<%: Html.TextBoxFor(model => model.ADR) %> | |
<%: Html.ValidationMessageFor(model => model.ADR) %> | |
</div> | |
<div class="editor-label"> | |
<%: Html.LabelFor(model => model.RoomRevenue) %> | |
</div> | |
<div class="editor-field"> | |
<%: Html.TextBoxFor(model => model.RoomRevenue) %> | |
<%: Html.ValidationMessageFor(model => model.RoomRevenue) %> | |
</div> | |
<div class="editor-label"> | |
<%: Html.LabelFor(model => model.RBRevenue) %> | |
</div> | |
<div class="editor-field"> | |
<%: Html.TextBoxFor(model => model.RBRevenue) %> | |
<%: Html.ValidationMessageFor(model => model.RBRevenue) %> | |
</div> | |
<div class="editor-label"> | |
<%: Html.LabelFor(model => model.OtherOperatingDepartmentRevenue) %> | |
</div> | |
<div class="editor-field"> | |
<%: Html.TextBoxFor(model => model.OtherOperatingDepartmentRevenue) %> | |
<%: Html.ValidationMessageFor(model => model.OtherOperatingDepartmentRevenue) %> | |
</div> | |
<div class="editor-label"> | |
<%: Html.LabelFor(model => model.RentalsAndOtherIncome) %> | |
</div> | |
<div class="editor-field"> | |
<%: Html.TextBoxFor(model => model.RentalsAndOtherIncome) %> | |
<%: Html.ValidationMessageFor(model => model.RentalsAndOtherIncome) %> | |
</div> | |
<div class="editor-label"> | |
<%: Html.LabelFor(model => model.RoomsExpense) %> | |
</div> | |
<div class="editor-field"> | |
<%: Html.TextBoxFor(model => model.RoomsExpense) %> | |
<%: Html.ValidationMessageFor(model => model.RoomsExpense) %> | |
</div> | |
<div class="editor-label"> | |
<%: Html.LabelFor(model => model.FBExpense) %> | |
</div> | |
<div class="editor-field"> | |
<%: Html.TextBoxFor(model => model.FBExpense) %> | |
<%: Html.ValidationMessageFor(model => model.FBExpense) %> | |
</div> | |
<div class="editor-label"> | |
<%: Html.LabelFor(model => model.OtherOperatingDepartmentExpense) %> | |
</div> | |
<div class="editor-field"> | |
<%: Html.TextBoxFor(model => model.OtherOperatingDepartmentExpense) %> | |
<%: Html.ValidationMessageFor(model => model.OtherOperatingDepartmentExpense) %> | |
</div> | |
<div class="editor-label"> | |
<%: Html.LabelFor(model => model.AGExpense) %> | |
</div> | |
<div class="editor-field"> | |
<%: Html.TextBoxFor(model => model.AGExpense) %> | |
<%: Html.ValidationMessageFor(model => model.AGExpense) %> | |
</div> | |
<div class="editor-label"> | |
<%: Html.LabelFor(model => model.SalesMarketingExpense) %> | |
</div> | |
<div class="editor-field"> | |
<%: Html.TextBoxFor(model => model.SalesMarketingExpense) %> | |
<%: Html.ValidationMessageFor(model => model.SalesMarketingExpense) %> | |
</div> | |
<div class="editor-label"> | |
<%: Html.LabelFor(model => model.FranchiseFees) %> | |
</div> | |
<div class="editor-field"> | |
<%: Html.TextBoxFor(model => model.FranchiseFees) %> | |
<%: Html.ValidationMessageFor(model => model.FranchiseFees) %> | |
</div> | |
<div class="editor-label"> | |
<%: Html.LabelFor(model => model.POMExpense) %> | |
</div> | |
<div class="editor-field"> | |
<%: Html.TextBoxFor(model => model.POMExpense) %> | |
<%: Html.ValidationMessageFor(model => model.POMExpense) %> | |
</div> | |
<div class="editor-label"> | |
<%: Html.LabelFor(model => model.UtilityCost) %> | |
</div> | |
<div class="editor-field"> | |
<%: Html.TextBoxFor(model => model.UtilityCost) %> | |
<%: Html.ValidationMessageFor(model => model.UtilityCost) %> | |
</div> | |
<div class="editor-label"> | |
<%: Html.LabelFor(model => model.ManagementFee) %> | |
</div> | |
<div class="editor-field"> | |
<%: Html.TextBoxFor(model => model.ManagementFee) %> | |
<%: Html.ValidationMessageFor(model => model.ManagementFee) %> | |
</div> | |
<div class="editor-label"> | |
<%: Html.LabelFor(model => model.PropertyTax) %> | |
</div> | |
<div class="editor-field"> | |
<%: Html.TextBoxFor(model => model.PropertyTax) %> | |
<%: Html.ValidationMessageFor(model => model.PropertyTax) %> | |
</div> | |
<div class="editor-label"> | |
<%: Html.LabelFor(model => model.Insurance) %> | |
</div> | |
<div class="editor-field"> | |
<%: Html.TextBoxFor(model => model.Insurance) %> | |
<%: Html.ValidationMessageFor(model => model.Insurance) %> | |
</div> | |
<div class="editor-label"> | |
<%: Html.LabelFor(model => model.EquipmentLease) %> | |
</div> | |
<div class="editor-field"> | |
<%: Html.TextBoxFor(model => model.EquipmentLease) %> | |
<%: Html.ValidationMessageFor(model => model.EquipmentLease) %> | |
</div> | |
<div class="editor-label"> | |
<%: Html.LabelFor(model => model.GroundLease) %> | |
</div> | |
<div class="editor-field"> | |
<%: Html.TextBoxFor(model => model.GroundLease) %> | |
<%: Html.ValidationMessageFor(model => model.GroundLease) %> | |
</div> | |
<div class="editor-label"> | |
<%: Html.LabelFor(model => model.Other) %> | |
</div> | |
<div class="editor-field"> | |
<%: Html.TextBoxFor(model => model.Other) %> | |
<%: Html.ValidationMessageFor(model => model.Other) %> | |
</div> | |
<p> | |
<input type="submit" value="Create" /> | |
</p> | |
</fieldset> | |
<% } %> | |
</div> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<IEnumerable<CHSweb.Data.Models.OperatingStatement>>" %> | |
<div class="listSection"> | |
<h4>Operating Statements</h4> | |
<% using (Html.BeginForm("AddOperatingStatement", "Property", FormMethod.Post, new { id = "operatingStatementForm" })) { %> | |
<table> | |
<thead> | |
<tr> | |
<th>Year</th> | |
<th>Occupancy</th> | |
<th>Room Revenue</th> | |
<th>Rooms Expense</th> | |
<th>AG Expense</th> | |
<th></th> | |
</tr> | |
</thead> | |
<tbody id="operatingStatementRows"> | |
<% foreach (var operatingStatement in Model) | |
Html.RenderPartial("OperatingStatement", operatingStatement); | |
%> | |
</tbody> | |
</table> | |
<%= Html.ActionLink("New Operating Statement", "BlankOperatingStatementRow", null, new { id = "addOperatingStatement" })%> | |
<% } %> | |
</div> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<CHSweb.Data.Models.OperatingStatement>" %> | |
<tr> | |
<td><%: Model.Year %></td> | |
<td><%: Model.Occupancy %></td> | |
<td><%: Model.RoomRevenue.GetValueOrDefault().ToString("C" )%></td> | |
<td><%: Model.RoomsExpense.GetValueOrDefault().ToString("C") %></td> | |
<td><%: Model.AGExpense.GetValueOrDefault().ToString("C") %></td> | |
<td><%: Html.ActionLink("Delete", "Delete", "OperatingStatement", new { id = Model.Id }, new { @class = "deleteLink" } )%></td> | |
</tr> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class PropertyController : Controller { | |
CHSDataContext db = new CHSDataContext(ConfigurationManager.ConnectionStrings["chsdb"].ConnectionString); | |
[HttpPost] | |
public ActionResult AddOperatingStatement(int propertyId, IEnumerable<OperatingStatement> operatingStatements) { | |
try { | |
var operatingStatement = operatingStatements.First(); | |
operatingStatement.PropertyId = propertyId; | |
db.OperatingStatements.InsertOnSubmit(operatingStatement); | |
db.SubmitChanges(); | |
return View("OperatingStatement", operatingStatement); | |
} catch { | |
return Json("Error"); | |
} | |
} | |
public ViewResult BlankOperatingStatementRow() { | |
return View("OperatingStatementEditorRow", new OperatingStatement()); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function getPropertyId() { | |
return $("#propertyId").val(); | |
} | |
$(document).ready(function () { | |
$("#addOperatingStatement").click(function () { | |
$.ajax({ | |
url: this.href, | |
cache: false, | |
success: function (html) { | |
$("#operatingStatementRows").append(html); | |
$("#operatingStatementForm").ajaxForm({ | |
target: '#operatingStatementFormContainer', | |
replaceTarget: true, | |
data: { propertyId: getPropertyId() } | |
}); | |
} | |
}); | |
return false; | |
}); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment