ListView in Views scaffolding

2 views
Skip to first unread message

Kyle Baley

unread,
Jan 9, 2009, 5:34:18 PM1/9/09
to sharp-arc...@googlegroups.com
Was wondering what everyone thought of using ListViews to render the HTML in the views for the scaffolding. My first reaction was WTF but I wanted to give it a chance to see if it was better than using a foreach loop.
 
Now that I've given it that chance: WTF?
 
Compare:
 
 <asp:ListView ID="monumentList" runat="server">
    <LayoutTemplate>
        <asp:PlaceHolder ID="itemPlaceHolder" runat="server" />
    </LayoutTemplate>
    <ItemTemplate>
        <tr>
    <td>
                <%# ((Monument) Container.DataItem).Name %>
            </td>
            <td>
                <%# Html.ActionLink<MonumentsController>(c => c.Show(((Monument)Container.DataItem).ID), "Details")%>
            </td>
            <td>
                <%# Html.ActionLink<MonumentsController>(c => c.Edit(((Monument)Container.DataItem).ID), "Edit")%>
            </td>
            <td>
                <%# Html.ActionLink<MonumentsController>(c => c.Delete(((Monument)Container.DataItem).ID), "Delete")%>
            </td>
        </tr>
    </ItemTemplate>
</asp:ListView>
 
With this:
 
<%
foreach ( var monument in ViewData.Model )
{%>
    <tr>
        <td><%=monument.Name %></td>
        <td><%=Html.ActionLink<MonumentsController>( c => c.Show( monument.ID ), "Details ") %></td>
        <td><%=Html.ActionLink<MonumentsController>( c => c.Edit( monument.ID ), "Edit") %></td>
        <td><%=Html.ActionLink<MonumentsController>( c => c.Delete( monument.ID ), "Delete") %></td>
    </tr>
  
<%} %>
 
That's for an object with a single property but I think you can see how they'd compare for an object with many more. The second also means we can remove the code from the PageLoad event in the View, which makes it more compatible with the upcoming MVC release where there will be no code-behind by default for views.

Billy

unread,
Jan 9, 2009, 5:55:16 PM1/9/09
to S#arp Architecture
LOL! I can't complain about that change and will make it so. This
also gives me an opportunity to emphasize the CrudScaffolding
templates are customizable for each of your projects, so you can tweak
them to your heart's desire. For example, on my current project, my
CrudScaffolding/Templates/Web/Views/IndexTemplate.tt looks like the
following to generate an ExtJS listing view (sorry for the long amount
of code, but I'd like to show just how versatile the templates are for
meeting any needs you may have):

/////////////////////////// BEGIN SAMPLE
CODE //////////////////////////////////////////////

<#@ import namespace="System.Collections" #>

<#+
public class IndexTemplate : BaseTemplate
{
public IndexTemplate(string solutionName, string domainObjectName,
NameValueCollection properties, string[] namespaceHierarchy)
: base(solutionName, domainObjectName, properties,
namespaceHierarchy) { }

protected override void RenderCore()
{
#>
<%@ Page Title="<#= DomainObjectNamePlural #>" Language="C#"
MasterPageFile="~/Views/Shared/Site.Master" AutoEventWireup="true"
CodeBehind="Index.aspx.cs" Inherits="<#= AppendNamespaceHierarchyTo
(SolutionName + ".Web.Views") #>.<#= DomainObjectNamePlural #>.Index"
%>
<%@ Import Namespace="<#= AppendNamespaceHierarchyTo(SolutionName +
".Core") #>" %>
<%@ Import Namespace="<#= AppendNamespaceHierarchyTo(SolutionName +
".Controllers") #>" %>

<asp:Content ContentPlaceHolderID="MainContentPlaceHolder"
runat="server">
<% string rootPath = ResolveUrl("~"); %>

<h1><#= DomainObjectNamePlural #></h1>

<% if (ViewContext.TempData["message"] != null){ %>
<p><%= ViewContext.TempData["message"]%></p>
<% } %>

<div id="div<#= DomainObjectName #>Grid"></div>

<script type="text/javascript">
//<![CDATA[
var <#= DomainObjectNamePlural #>Store = new Ext.data.JsonStore({
data: <%= Newtonsoft.Json.JavaScriptConvert.SerializeObject
(ViewData.Model) %>,
fields: [ <#+ foreach (string propertyName in Properties.AllKeys )
{ #>'<#= propertyName #>', <#+ } #>'ID' ],
sortInfo: {field: 'ID', direction: 'ASC'}
});

var <#= DomainObjectNamePlural #>ColumnModel = new Ext.grid.ColumnModel
([
<#+ string separator = "";
foreach (string propertyName in Properties.AllKeys ) { #>
<#= separator #>{header: '<#= propertyName #>', dataIndex: '<#=
propertyName #>'}
<#+ separator = ",";
} #>
]);

Ext.onReady(function(){
Ext.state.Manager.setProvider(new Ext.state.CookieProvider());
Ext.QuickTips.init();

var ds = <#= DomainObjectNamePlural #>Store;
var cm = <#= DomainObjectNamePlural #>ColumnModel;
cm.defaultSortable = true;

// create the grid
var grid = new Ext.grid.GridPanel({
ds: ds,
cm: cm,
sm: new Ext.grid.RowSelectionModel({singleSelect:true}),
renderTo: 'div<#= DomainObjectName #>Grid',
width: Ext.GRID_WIDTH,
height: 418,
stripeRows: true,
viewConfig: {
forceFit:true
},
// inline toolbars
tbar:[{
text:'New...',
tooltip:'Create new <#= DomainObjectName #>',
handler: function(){
grid.suspendEvents();
window.location.href = '<%= rootPath %><#=
DomainObjectBaseUrl #>Create';
},
iconCls:'add'
}, '-', {
text:'Edit...',
tooltip:'Edit selected <#= DomainObjectName #>',
handler: function(){
var selected = grid.getSelectionModel().getSelected();

if(selected) {
grid.suspendEvents();
window.location.href = '<%= rootPath %><#=
DomainObjectBaseUrl #>Edit/' + selected.data.ID;
} else {
alert('Please select a row first.');
}
},
iconCls:'edit'
},'-',{
text:'Delete...',
tooltip:'Delete selected <#= DomainObjectName #>',
handler: function(){
var selected = grid.getSelectionModel().getSelected();

if(selected) {
if(confirm('Really delete?')) {
grid.suspendEvents();
window.location.href = '<%= rootPath %><#=
DomainObjectBaseUrl #>Delete/' + selected.data.ID;
}
} else {
alert('Please select a row first.');
}
},
iconCls:'remove'
},'->'],
bbar: new Ext.PagingToolbar({
pageSize: 15,
store: ds,
displayInfo: true,
displayMsg: 'Record {0} - {1} of {2}',
emptyMsg: "No records found"
}),
plugins:[new Ext.ux.grid.Search({
position:'top',
mode: 'local'
})]
});

// show record on double-click
grid.on("rowdblclick", function(grid, row, e) {
grid.suspendEvents();
window.location.href = '<%= rootPath %><#= DomainObjectBaseUrl
#>Show/' + grid.getStore().getAt(row).data.ID;
});
});
//]]>
</script>
</asp:Content>
<#+
}
}
#>

/////////////////////////// END SAMPLE
CODE //////////////////////////////////////////////

T4 is your friend. (Unless you have VisualSVN installed, of
course. ;)

Billy

PS - For any ExtJS geeks reading this, yes, I know I can simplify the
grid with a reusable JavaScript object, but we do a lot of tweaking
for each page and I like the granularity.

Kyle Baley

unread,
Jan 10, 2009, 7:13:14 AM1/10/09
to sharp-arc...@googlegroups.com
Yes, that's a good point. I have customised them for my current project as well to incorporate security checks. I only orate here when I think the changes would be good to incorporate in the trunk.
 
Still wish they worked with VisualSVN though...

Billy

unread,
Jan 11, 2009, 1:36:21 PM1/11/09
to S#arp Architecture
Trunk and the project template both now have the simplified index
generator with the ListView removed.

Billy


On Jan 10, 5:13 am, "Kyle Baley" <k...@baley.org> wrote:
> Yes, that's a good point. I have customised them for my current project as
> well to incorporate security checks. I only orate here when I think the
> changes would be good to incorporate in the trunk.
>
> Still wish they worked with VisualSVN though...
>
> > DomainObjectBaseUrl #>Edit/' + selected.data.ID <http://selected.data.id/>
> > ;
> >                } else {
> >                    alert('Please select a row first.');
> >                }
> >            },
> >            iconCls:'edit'
> >        },'-',{
> >            text:'Delete...',
> >            tooltip:'Delete selected <#= DomainObjectName #>',
> >            handler: function(){
> >                var selected = grid.getSelectionModel().getSelected();
>
> >                if(selected) {
> >                    if(confirm('Really delete?')) {
> >                        grid.suspendEvents();
> >                        window.location.href = '<%= rootPath %><#=
> > DomainObjectBaseUrl #>Delete/' + selected.data.ID<http://selected.data.id/>
> > ;

Kyle Baley

unread,
Jan 11, 2009, 2:03:18 PM1/11/09
to sharp-arc...@googlegroups.com
Thanks Billy. I hope some day soon to actually make contributions back to the project rather than pick at it from the balcony like Waldorf and Statler.

Billy

unread,
Jan 11, 2009, 2:16:05 PM1/11/09
to S#arp Architecture
Your blog posts and dimecasts are a great contribution! I tell ya
what, I'll give you 1/2 point of contribution credit for every blog
post you make about it. It's not as much as the 10 points of credit I
give for each LOC written, but you'll get there, I know it!!

Billy


On Jan 11, 12:03 pm, "Kyle Baley" <k...@baley.org> wrote:
> Thanks Billy. I hope some day soon to actually make contributions back to
> the project rather than pick at it from the balcony like Waldorf and
> Statler.
>

Kyle Baley

unread,
Jan 11, 2009, 3:00:57 PM1/11/09
to sharp-arc...@googlegroups.com
10 points per LOC? Excuse me while I introduce some intermediate variables into the code.
Reply all
Reply to author
Forward
0 new messages