I have spent the better part of a week now trying to encapsulate a jqGrid control into something that could be cleanly and easily reused from various ASP.NET views. In some ways, I think I have met those goals, but in others, I think I have failed miserably. On the plus side, actually creating a grid is quite easy:
1: <%=Html.JQGrid("treeTabel",
2: new JQGridOptions
3: { Caption="Components",
4: DataUrl = Html.BuildUrlFromExpression<SandboxController>(c => c.JQGridTreeViewData((int)ViewData["EstimateId"], null)),
5: PagerId="PagerId",
6: IsTreeGrid = true,
7: CellEditEnabled = true,
8: },
9: new[]
10: {
11: new JQGridColumn("id", "ID") { Visible = true, IsExpandColumn = true, Editable = false},
12: new JQGridColumn("ComponentId", "ComponentId") { Visible = false, Editable = false},
13: new JQGridColumn("Name", "Component"),
14: new JQGridColumn("HistoricalCost", "Historical"),
15: new JQGridColumn("EstimatedCost", "Estimated"),
16: new JQGridColumn("TargetCost", "Target"),
17: new JQGridColumn("Risk", "Risk") { EditType = "select" ,EditOptions = new[] { "Unassigned:Unassigned", "Low:Low", "Medium:Medium", "High:High"}},
18: }) %>
That simple (well, sort-of simple), type-safe code produces this horrible mass of HTML and JavaScript:
1: <script type='text/javascript'>1:
2: jQuery(document).ready(function(){3: jQuery('#treeTabel').jqGrid({4: url: '/Sandbox/JQGridTreeViewData/2',5: datatype: 'json',6: height: '475px',7: colNames:['ID','ComponentId','Component','Historical','Estimated','Target','Risk'],8: colModel:[
9: {name:'id',index:'id',sortable:false,width:1},10: {name:'ComponentId',index:'ComponentId',sortable:false,width:1,hidden:true},11: {name:'Name',index:'Name',editable:true,sortable:false,width:1},12: {name:'HistoricalCost',index:'HistoricalCost',editable:true,sortable:false,width:1},13: {name:'EstimatedCost',index:'EstimatedCost',editable:true,sortable:false,width:1},14: {name:'TargetCost',index:'TargetCost',editable:true,sortable:false,width:1},15: {name:'Risk',index:'Risk',editable:true,sortable:false,width:1,edittype:'select',editoptions:{value:'Unassigned:Unassigned;Low:Low;Medium:Medium;High:High'}}16: ],
17: pager: jQuery('#PagerId'),18: rowNum:25,
19: rowList: [10,25,50,100],
20: imgpath: '/Content/jQueryPlugins/JQGrid/themes/steel/images',21: width:(document.body.clientWidth)*(7/10),
22: shrinkToFit: true,23: caption: 'Components',24: loadonce: false,25: treeGrid: true,26: ExpandColumn: 'id',27: treeGridModel: 'adjacency',28: cellEdit: true,29: cellsubmit: 'clientArray'30: }).navGrid('#PagerId',{edit:false,add:false,del:false,search:false,refresh:false})31: ;});
</script>
2: <table id='treeTabel' class='scroll'></table>3: <div id='PagerId' class='scroll' style='text-align:center;'></div>
Obviously that’s a step up. But when you look under the covers, things are anything but clean and neat. Basically, I have several methods that build up a bunch of JavaScript strings, then spit them back out. It reminds me *so* much of old-school PHP, where you have this awful mix of presentation markup and PHP code interwoven together into a blanket that looks like someone threw up on it. Here’s an excerpt:
1: if (options.IsTreeGrid)
2: {
3: JQGridColumn expandColumn = columns.FirstOrDefault(c => c.IsExpandColumn);
4:
5: if (expandColumn == null)
6: {
7: throw new InvalidOperationException("IsTreeGrid is true, but no column found with IsExpandColumn set to true.");
8: }
9:
10: js.AppendLine("treeGrid: true,");
11: js.AppendFormat("ExpandColumn: '{0}',", expandColumn.Name).AppendLine();
12: js.AppendLine("treeGridModel: 'adjacency',");
13: }
14:
15: //If cell editing is enabeled, the changed rows are stored client-side,
16: //and it is the responsibility of the page to provide a mechanism
17: //for posting the data back.
18: if (options.CellEditEnabled)
19: {
20: js.AppendLine("cellEdit: true,");
21: js.AppendLine("cellsubmit: 'clientArray',");
22: }
So, I’m torn. Overall, I think the ASP.NET MVC approach is much, much better than the WebForms approach, but… is this it? Is this really the best we can do? There has to be a better way to do things like this. There has to be a way to keep languages separate. There has got to be a cleaner way to encapsulate and reuse "controls" in ASP.NET MVC. I just haven’t found it yet.
Remember what we’re working with here – HTML was NEVER designed for any of this crap. The fact that we can do the things we can with web technology is pretty amazing in and of itself.
But this wreck we have these days is the result of abusing the shit out of a seriously outdated technology. So, is the best we can do? Yeah, I think it is. I mean, there may be room for small improvements, but overall, "it is what it is". Until HTML is abandoned for whatever the next thing is (and some believed it was Silverlight, but I don’t think that’s the case at all, although Silverlight will have it’s place I believe).
Now, unlike you, I don’t get "offended" with seeing c#/php/javascript/whatever code blocks inside the markup itself. That’s what it’s there for – as long as those code blocks ARE presentation related. It’s when people put business logic or other junk in there that we have troubles. MVC goes a long way to helping enforce the developer to do things correctly and cleanly. It definitely has a leg up on the jarbled mess that is ASP.NET Webforms.
A fellow developer at work said something I thought was interesting, after we conducted an interview: "I wonder how many ASP.NET developers when asked ‘When you enter in a value into a textbox, how does that value truly get to the page on the next load?’ would only think that the answer is ‘Viewstate!’ and have no idea about POST values."
@Rob:
I agree on most everything you said. But if the solution isn’t to abandon HTML for Silverlight or Flash, what *is* the solution? Is it even on the horizon yet?
I don’t really mind the C# code in the markup for doing presentation logic. To me, that’s perfectly fine (as long as it is presentation related). What sparked this post though is that I’ve got a crap-ton of JavaScript embedded in my C# code. I guess it’s not any worse than the WebForms story for doing custom controls, but it just feels like there should be some better way to do it. I actually thought over the weekend about embedding the JavaScript as a resource in the assembly, then using some sort of templating engine to modify the JavaScript based on the options specified by the caller. I think that would make me feel a little cleaner.
Actually, when I’m using JavaScript, I aim to actually make all my JavaScript external (not to say that I never break that rule, it’s just how I try to be). Anything that could alter the JavaScript, if possible, I try to make a parameter to the function – then in my markup I call it with the proper parameters – you can even tweak it with the code blocks if you really need C# to look at some properties.
It is of course, easier said than done when you get complex JavaScript, but attempting to keep your JavaScript logic separated and external will usually help me think through it better so that it’s simple to consume by other pages (or views in MVC). Then that also helps keep the languages separated. I haven’t messed with ASP.NET MVC yet, so I don’t know if this is a good idea or not (probably not), but you could look at making the JavaScript parameters either exposed on the model or introduce a code-behind to your view, so that on your markup could look something like:
<script src="/js/TreeGrid/DoSomething.js" type="text/javascript"></script>
<script type="text\javascript">
DoSomething(<%=IsTreeGrid%>,<%=ExpandColumn%>,<%=TreeGridModel%>);
</script>
As for what the replacement is? No idea. I don’t think it’s there yet. I find WPF interesting though – it’s a declarative markup language that provides action-based commands in the markup itself. Something that uses some of those ideas I think would be great for whatever replaces HTML, but it’s not like I’ll have anything to do with that (if it ever happens).
Of course, the true issue is that even if HTML’s true replacement came out, HTML is so widespread that it’d take 10-15 years at least for it to be truly abandoned.
Hmm, that’s an interesting idea. I’m not completely sure how I would apply it to what we have with jqGrid, but it’s probably doable (and wouldn’t make me feel like I’ve committed some horrible atrocity). I’ll give that a shot.
You know, you really should start a blog, and maybe you could post examples of things you’ve worked on that might be interesting. You could also have an alternate ago, perhaps call him "Evil Rob", where you post about the things *not* to do. Oh well, maybe someday…
Hah, you assume I work on stuff that might be interesting. We’re on the tail end of the project now, so it’s mostly cleanup, loose ends, and bug fixes. Although we actually just discussed something Friday that might be a neat blog post.