Hanalei, Hawaii Thursday, March 11, 2010

How MVC, jQuery, and SubSonic Will Make You Smile

As I continue to work with the new MVC bits with respect to SubSonic, I keep smacking my forehead (and there's a lot to smack) at how much simpler things can be if I stop overthinking them. Getting to know toolsets like jQuery and SubSonic can offer many of these moments.

As I continue to work with the new MVC bits with respect to SubSonic, I keep smacking my forehead (and there's a lot to smack) at how much simpler things can be if I stop overthinking them. Getting to know toolsets like jQuery and SubSonic can offer many of these moments. Bundle that with the new MVC stuff and your forehead will be burning...


 

Last week I wrote a post about how I had to relearn some concepts as I sat noodling over a rather simple problem:

How do I auto-postback an HTML select box and refresh some bits on my page based on the selection?

It's pretty easy with WebForms and takes all of 10 minutes - but I don't have PostBack with MVC and I love a challenge :). I started to get sort of frustrated at first - slignin javascript around - but then it occurred to me that while I've been relying on WebForms to do this kind of thing for me over the last few years, the "other guys" out there have kept on truckin with javascript and HTML and dernit if those kids haven't come up with some very neat stuff! jQuery, as they say, is the bees knees...

Put things like .NET 3.5, jQuery, SubSonic, and the new MVC framework together, and you have something really, really Super Groovy to look forward to.

Extension Methods
This is part of the fun of .NET 3.5. These methods allow you to "add functionality" to a class when needed - sort of like Ruby's mixins but you don't declare these extensions on the class - you simply have to have a "using" statement that declares the extension namespace - thus "attaching them" at the hip during design-time (see Scott's writeup).

So, the first part of my solution was to create an extension method for a Dictionary<string,string> that creates some HTML. These extension methods (or versions of them, after Eric has his way with my coding) will be part of SubSonic in the future:

ToSelect

 

As you can see, this method will take any Dictionary<string, string> and add a method to it called "ToHtmlSelect()".

At first I was a little hesitant about this. I don't like UI properties and methods hanging off objects - it's muddy. But then as I thought about it - these are extenders! They live separately from the object anyway, and are only present when I "slap them on"- so I think this kind of thing is OK.

Secondly I wasn't too happy seeing HTML in code. It sort of triggers a gag reflex. But as Jon Galloway put it to me:

Dude, HTML's not going to change the way you write select boxes for a long time. I think you're OK here. And you're my hero...

Well he might not have said that last part... but I know he thinks it...

The thing to note here is the third argument in the method: "object attributes". This is ScottGu magic at work and something he shows in his MVC demos. This little bad boy allows you to pass in an anonymous type to declare any extra attributes you might want this select box to have - like "title" or "class" - simply by using new{title="This is my title", _class="MyCSSClass"}.

These attributes are then turned into an Attribute list (a simple hash) by, yep, another Extension method that hangs off the object class (you can see it there in the last line of the method).

I also created another method called "ToHtmlRadioList()" which is an extension of List<string>:

ToRadio

It does the same thing as the first method, above, but this time outputs a bunch of HTML Radio buttons.

ToHtmlRadioList

Here's the whole Controller method:

FetchList

A key thing to remember about these methods is that they live in an assembly that's completely reusable at any time and are not part of the class, base class, or interface - so in effect you've bottled up your UI logic far, far away from your view and are "tacking it on" when you need it. This is a good thing, and we've just removed a mess of noodles from the boiling pot.

Notice the last line? Response.Write? Keep that in mind...

Putting It Together
So now that we have these things, I can put the bits together using jQuery and the new MVC bits. The first thing I want to do is create a controller method to serve up some HTML. I can create an view page if I want, but all I need is the radio list so I'm going to go with less code and files, and just serve up the HTML. I can use JSON here - but to keep this example concise I'm going straight for the gut...

On my main view (called "Index"), I create 3 things: my select box, a button to fire the refresh, and a div to hold the results. Notice the simple one-liner for the select box? ViewData is handed off from the controller to the View page, and "Providers" is simply a Dictionary<string,string> property that's been populated in the request. I can now pop this into HTML using the Extension methods above:

select_html

Finally, hooking this up is trivial with jQuery. I'll admit I haven't used the library long enough to claim that I'm an expert - or even close. But this took all of 5 minutes to read up on and I was off doing something else!

What I want to do now is hook up my "providerSelect" button to fire a request back to the server, sending it the value in the select box above it (called "providerList"). This is easy with jQuery:

jQuery

At first glance, jQuery "syntax" can be a bit scarey cause it doesn't look much like the javascript that I remember. But a quick read on the docs and you're off:

  • $document.ready... is a "sugary" notation that basically means "when the document is done loading, do this stuff"
  • Anything with "$(" in front of it is a jQuery "shorthand" method call. The first line after document.ready is telling jQuery to wire up my button's click event to a new function, which is declared inline
  • The 4 lines that follow grab the value from the current select box, pass it to a URL which in this case is my controller method, "home/FetchTableList". The hash notation after that appends the selection to the URL
  • The "load()" method is the fun part - it grabs the HTML from a request and simple pops it into the specified control. In this case it's my "tableList" div. If you recall, the controller method here is the same as above, that does a Response.Write of a radio button list :). So in this case, "load()" is simply pumping a radio button list into the div.
  • The rest of the stuff is just visual sugar that makes things slide and look "Web 2.0-y".

Final Thoughts
I like this example because the view is light and nimble, and has ZERO logic in it - not one "if" anywhere and no variables. The UI is handled from a central UI class, which in this case is my Extension methods class in SubSonic.

You may be thinking to yourself

"dude, that's a lot of code for something that would take me ummmmm 5 lines using WebForms" 

and that's true. But consider that

  • the extension methods - which you DON'T have to write are most of the code and these don't count cause they're part of SubSonic ;).
  • you would need to write the data access code anyway
  • I was more verbose than I needed to be with jQuery, and everything I needed to do could be done in one line
  • The HTML to support this is at least HALF of what you'd need to write for a DropDownList/RadioButtonList combo.

I, for one, can't wait until this stuff hit's CTP and I can see what these guys have been up to. Don't get me wrong, this MVC demo code is really solid - but the real stuff, I am sure, will be very fun indeed!

What other kind of SubSonic-y/MVC stuff do you want to see?


dictionary &raquo; How MVC, jQuery, and SubSonic Will Make You Smile - October 25, 2007 - [...] Read the rest of this great post here [...]
Damien Guard - October 25, 2007 - The real problem with treating them as strings is making sure you do proper encoding or you are opening up for HTML injection and XSS attacks. The code you presented here suffers exactly that. I keep seeing this oversight lately including in the view stuff for MonoRail - so much so that their SmartGrid control was affected too. We're all heading for another round of vulnerabilities whilst people keep throwing strings into the page with HTML encoding off as default. [)amien
Rob Conery - October 25, 2007 - @Damien - good thoughts. Can you show me a bit more about what you mean (and others who maybe aren't as up as you on XSS). I agree that this isn't optimal - "it's just a demo". I'm stoked guys like you have commit with SubSonic - this wouldn't have made it to release I'm sure...
Justin - October 25, 2007 - Ugh I'm so jealous of you getting to play with this! I've been wanting to ever since hearing about it. Anyway, another good write up, thanks! I'm no expert on xss by any means but I believe he's pointing out that you are inserting direct strings from your tableList & your .GetTableNameList() into your html. So it most likely depends upon the source of that data and whether any of it is input by users.
Haacked - October 25, 2007 - He's referring to not HtmlEncoding the keys and values of the dictionary since that data might come from user input. Same goes with any TextInput helper. Where's the h(string) function? Phil
Dan - October 25, 2007 - It's nice to see in .net a well architected solution that outputs clean (x)html, and as a consequence clean Javascript.
And great example of where makes sense to apply extension methods, this shows how it's not needed to abstract in System.Web.UI.Control-derived classes the html form elements, a helper method allows you to work with Dictionary in the server side without mixing your logic with html code.

Good job
simplerunner &raquo; How MVC, jQuery, and SubSonic Will Make You Smile - October 25, 2007 - [...] all the details here [...]
DotNetKicks.com - October 25, 2007 - How MVC, jQuery, and SubSonic will make you smile... You've been kicked (a good thing) - Trackback from DotNetKicks.com...
kevin - October 25, 2007 - Rob, looks great! But the one think I'm not a fan of is keeping the HTML in compiled code so I'd offer emulating Django on this one. HTML should just never ever be compiled if possible - my 2 cents. Here something like this could work... ViewData.Providers.RenderTemplate('foo.ascx',ViewData.GetSupplierList(),{}) And then the .ascx file is really just the HTML for the dropdown, table, radio button or whatever you want - and of course the simple logic to loop through and display, etc. In theory the template could be an entire form really - it's just html and markup mvc style, and could pull directly from the view engine that already exists in MVC, I assume. Drop your templates in your /Templates directory and MVC.net will know where to find them, etc. As far as the django reference - here's the docs on the template language: http://www.djangoproject.com/documentation/templates_python/ Just a suggestion. I'm not familiar with MVC.net like some lucky b*stards are ;P
kevin - October 25, 2007 - after thinking about this though, since you're isolating the html to individual elements like dropdown, radio button, etc... then yea, I'm a fan of this. Especially that we can pass in an array {} of attributes. tre cool! But anything further in regards to generating markup is where I get concerned. it's my fear of webform generated html markup complex showing its ugly face.
Dave Savage - October 25, 2007 - I'm really getting giddy over the thought of SubSonic with MVC. It could not get here sooner! =)
Great stuff Rob.
Scott - October 25, 2007 - This isn't a criticism of Rob's coding nor of the ASP.NET MVC framework. "public static string ToHtmlSelect(this Dictionary listItems, string name, object selectedVale, object attribtues)" Who the heck wants to write code like that? Who the heck wants to live like that? There's got to be an easier way to do this, if not in C# 3.0, maybe in C# 3.5 or 4.0? You can deride me all you want for wanting a little more syntactic sugar in my language of choice, but man I'd like to not get CTS from all that typing. :)
Rob Conery - October 25, 2007 - @Scott- this method is in SubSonic, and is meant to be used in the view. In other words - you don't need to write it :). I do agree that Extension methods can be a bit verbose, but at the same time they really can save you a lot in the long run. But I agree - more sugar!
Zack Owens - October 25, 2007 - Hey rob... got an idea.

What if SubSonic had controls that rendered the HTML for you without the postbacks and such. It could help the devs who don't use HTML that often use ASP.NET-like controls without the nasty things they do (viewstate, postbacks, etc).

And maybe a jQuery tie into that too? I shall play arround with that idea.
Shawn Oster - October 25, 2007 - 1. Rob please tell me that you normally enclose your attributes with quotes? That lines like this: "", without proper XML/XHTML quoted attributes, are few and far between. While HTML allows you to get away with this it's bad form.

2.@Scott: I'm not sure I'd be as worried about that method declaration, that's not what you'd be typing over and over, it would be the "Providers.ToHtmlSelect("providerList", "", null)" and that actually reads pretty clean. If you want to save yourself from typing the initial method signature then setup a keyboard macro/template for it.

3. Rob, with all your lovely playing with MVC bits what does this mean for your MVC templates that you released awhile back? I'm assuming they'll probably change or go away entirely as SubSonic gets refactored but I'm curious. Each new project I debate whether to use the stock SS or the MVC templates.

4. The thing I'm *most* excited about the new MVC is now people can start using their id's as selectors again! I've noticed a whole breed of ASP.NET developers that don't even know you can style elements using the #id CSS notation vs. the .class selector because of the funky postback id that gets generated.
Rob Conery - October 25, 2007 - @Shawn: yes I normally do and MAN I hate writing posts after a long day. If anything it shows you how I've come to rely a little too much on the VS designer :). Thanks for catching this... In terms of the MVC templates- I need to refactor those and pull them into the core. They won't go away since WebForms aren't going anywhere :). Yes - it's nice working with clean IDs... @Zack: I've thought about that and I'm sure I'll do something along those lines. The main issue, however, is that you can't do fun things like pass a generic type or work up the anon attribute. @Kevin: good comments and I agree, but HTML is HTML is a String and while you can put it in an ASCX, I'm not sure what you gain. But it's a good point that people don't like to recompile if they change the HTML. At the same time - it's pretty basic HTML and the attribute setting should allow you to do this. I should mention too that it's not an array - it's an Anonymous type.
John S. - October 25, 2007 - Great example Rob!

I would also like an update on the MVC templates as I just ripped out an old custom DAL and used those to replace it.
Denny Ferrassoli - October 25, 2007 - I seriously can't wait for the MVC framework. I work with jQuery and SubSonic daily and my biggest issues have been elegantly using ASP.NET/AJAX and javascript frameworks.

One thing I really do like about ASP.NET AJAX is that it allows you to work with a debug and release version of your JavaScript. I hope to see this implemented in the MVC framework. If not then someone could potentially create it.
Scott - October 25, 2007 - @Shawn Oster I agree. I was just comparing the declaration to Javascript: {object}.prototype.newMethod = function(bar, foo) { ... }; var o = new {object](); o.newMethod("hootie","moto"); or Ruby module thing def newMethod(bar, foo) ... end end class {object} include thing end o = {object].new o.NewMethod("hootie","moto"); In my mind, Javascript wins for the most elegant way to add extensions/mixins to an object. Being able to extend the ASP.NET objects is teh sexxy. Imagine being able to write aspPanel.addSlideEffect();
links for 2007-10-26 | Lazycoder - October 26, 2007 - [...] Rob Conery ยป How MVC, jQuery, and SubSonic Will Make You Smile (tags: mvc .net development lazycoder) [...]
Ed - October 26, 2007 - Rob, Are you thinking of using MVC for your RESTful implementation? You can send the correct HTTP method with Javascript in the absence of PUT, DELETE etc in (X)HTML forms. In the context of a browser, the service can render XHTML and you can serialise an XHTML form with JQuery to name value pairs. Out of the box Ajax controls can GET JSON from your RESTful web service, to add eye candy and save devs from reinventing the wheel. If the client wishes to deal with larger sets then I believe you can serialise to ATOM with .NET 3.5?
Mike - October 29, 2007 - I think that webcontrols will still be used in ASP.NET MVC. So if you want a dropdown you can just use a control and set its datasource, like it is today. I'm afraid that adding extension methods like the one in your demo will lead to a cluttered namespace (kind of like the Sugar thing feels like today, to me anyway). Will there be ToXAMLSelect and GenerateJSONForExtJSDropDOwnControl extension methods? Where will SobSonic end, do you think it can be the complete Rails-like library for ASP.NET or just the Active Record and migrations stuff (so helping us write database enabled applications, be it MVC or WebForms)? Or are you going to add everything and the kitchen sink? (I hope not...)
Rob Conery - October 29, 2007 - @Mike: Yes you can still use a server control and set the data source in the code-behind, but that breaks the whole reason of using MVC. In addition, there is no postback so you don't solve a single problem here - you gain one as you try to guess what your control's ID will be :). The key to your second point is the word "afraid". A "cluttered namespace" is usually a reference to everything be improperly organized under one lid. In our case - with Sugar - we've partitioned everything pretty clearly, and with comments. If you can't find something let me know. I listen heavily to the community RE where SubSonic goes. That said, I think I've managed it pretty well to this point so have some faith :).
Stephen - October 29, 2007 - While I love the MVC paradigm, and the upcoming .net implementation.. I get really put off by these types of functions..

I mean, when I start to write a class and I start to get methods like this, I start to foresee it becoming monolithic.. theres so much interaction that can be done with methods, and then much more by having objects represent a 'builder'.

What would be so wrong with writing a new set of server side controls that are really minimal.. don't use viewstate, but have lots of great features like accessing the object as an element, just as we would in JS on the client side..

I sometimes wonder if some people are moving right back for the sake of it.. while I think webforms has some design flaws from its attempt to abstract state into the web.. I do feel it should be used to teach us something about a MVC implementation..

I know that the MVC will still work with .NET controls, giving that the viewstate functionality is handled by the page, and can be simply 'ignored'..

But it seems to be a lot of these controls are at least considering view state compatible code, meaning their doing that extra bit of work for the sake of something that won't be used.. (hence my idea of writing a new mvc control base and common children).
Joe Chung - October 29, 2007 - Seeing StringBuilder-generated HTML makes me sad for the future of ASP.NET. Object-oriented ASP spaghetti code is still spaghetti code.
Rob Conery - October 29, 2007 - @Joe: does it help that you *can't* see this in the core - kind of like "cover my eyes and it's not there"? Cause, well, that's how it's done isn't it? I mean at some point, walking the control stack, it's all text, and it's concatenated strings. In terms of being sad - here's some tissue my friend, and a pat on the back to remind you WebForms will always be around :). And finally- I wish people would stop talking about "Spaghetti Code". Honestly - think about it. We use C#, not ASP. Slight language difference here people... Either way - FUD it up if you want.
Stephen - October 29, 2007 - Hi Rob,
The term spaghetti code does of course come from mixing server side blocks into blocks of html, regardless of the language.. vbscript might be nothing to c# but I don't think that really matters..

I think its a tad unfair to call it spaghetti code, as ASP days there was a very narrow seperation between major code blocks, such as what will be in code behind now, vs include files back then for example.

But I don't know if I agree with your description of this being all concatenation of strings that is disgustingly hidden away from us..

Surely thats just an ignorant view of the powers of abstraction.. we don't all sit here and write our own OS for each application.. or write our own binary.. or make our own computer..

I wondered what you thought about my concerns with these methods becoming monolithic, having a lot of overloads to achieve something that wasn't originally considered and becoming confusing and non standard..

What is the reason you would rather use these methods? surely we're moving away from a good convience that has invaded into lots of things now, such as xaml, mxml.. etc etc..
Rob Conery - October 29, 2007 - @Stephen: "The term spaghetti code does of course come from mixing server side blocks into blocks of html, regardless of the language" Are you serious? I really hope you meant "does NOT". Spaghetti Code is a very OLD term, predates most languages in use today, and stems from the old days of "top-down' procedural languages like Pascal, Basic, and Fortran. These languages were all about executing in-line, and you needed to pass the flow off to another point in the process - such as GOTO 100, which would send the executing to line 100. This is not only easy, but prevalent in non-OO languages. I didn't say "disgustingly" - what I said was that, at some point, strings get concatenated. HTML is text. To think otherwise, and to think HTML magically appears, is a tad strange. And I can, of course, take your argument the other direction and say that you should not expect a framework to read your mind and make an application for you. You see, arguing my point, to an extreme, is an extremely old debater's trick and is useless in this discussion. All this debate aside - there's nothing wrong with WebForms, per se, and they are a fine and viable toolset. You can leave MVC to all of us Spaghetti Hacks and be on your way... :)
Stephen - October 30, 2007 - While I agree your points about spaghetti code are true, I think you've taken a position of being argumentative for the sake of it..

Spaghetti code has for a long time referred to how PHP and ASP applications view, regardless of if it is an adaption to the original point.. I can now see your argument for saying 'this is c#', but I'm attempting clarify the OP's point that this is related to mixing in server side code blocks with client side html..

I did also state after this that I think thats a little unfair.. as the only server side code included is that which is directly translated to client side html blocks..

I find it very strange that you become very aggressive about your points, defending them without actually adding any reason..


You end your point stating essentially 'Stick to webforms if you can't understand the concept'..

This of course is ignoring my original points again.. my suggestions aren't at all trying to convert an MVC paradigm into some mangled webforms system..

My suggestion is merely to use structure building objects instead of various string creating method calls..

My point was about abstracting a system from being method based.. to becoming object instance based.. as it is with web forms.. to allow the system to be more adaptable and generic..

It doesn't mean at all that these items are webforms.. webforms isn't dictated by the fact we concept this idea of a 'control'.. its only that implicitly, the default control in webforms is state enabled.. which more describes the difference with webforms to an MVC (the idea that we try and abstract state away from the programmer so they can develop in a more winforms like fashion).

I'm also not stating that a new control base IS better, I'm simply asking you why you selected going back away from this concept, since it seems to be a sensible evolution..

You could essentially see my question as:

Why methods and not object instances for building html..
Richard Bushnell - October 30, 2007 - Sorry, I haven't read all the comments, but it looks like this is rather complex to me.

Isn't one of the ideas of using MVC with ASP.Net that we can test the controllers separately? In this case, I don't want to generate HTML in my controllers.

And even if JQuery is cool, it's just another thing that people have to learn. I like learning new stuff, but not everyone is like me.

Can't we make the MVC thing a lot simpler?

I'm working on my own implementation of Model-View-Controller Presenter with SubSonic as the Model which allows me to test the whole app, and still use WinForms. Perhaps there's room for that too.

The extension methods seem really useful though.
Rob Conery - October 30, 2007 - @Stephen: apologies to you - wasn't trying to be aggressive. I try to leave concise comments and they come across that way :). Smilies don't always work either... I'm still not entirely sure the point you're trying to make, and I wasn't dogging you for wanting to stick with WebForms. You took my words and spun them (i.e. the "disgusting" remark) so I thought it fair to make sure people didn't read that. WebForms are perfectly viable - so is MVC and inline scripting. To many people who code "declaratively" - I see your point. It's off-putting to see code on a page written out imperatively rather than declaratively. But it's all the same thing - code. It sort of drives me batty when people say they don't want to see "spaghetti code on a page" when the fact is that Repeaters are still code, and all of their templates and properties can be interpreted as spaghetti by someone who is more familiar with inlining. Same with a GridView. So - in all fairness I think we're closer on this than our discussions portend. I'm not trying to convince people - just challenging them to stop with the unreasonable bias and give it a shot. At the very least, don't look down on it and get all elitest (not saying you were - but a lot do).
Random Blahgging - October 31, 2007 - Use ASP.NET Objects, Not Literal HTML Strings... ...
Stephen - November 1, 2007 - Hi Rob,
Thanks for your reply, and I agree also that its all code, the repeater is also a strange object, it seems more redundant in an MVC pattern..

When I was thinking of my examples, I was thinking more about controls that map almost directly to html, rather than controls that composite html.

For example, given:

ToHtmlSelect(..)

Vs.



foreach(entry in dictionary)
{
ListBoxMyItems.Items.Add(entry);
}

ListBoxMyItems.Items[selectedEntry].selected = true;


I know the initial example is more verbose, but it does let me easily do things like.. say.. add attributes to the list..

If I wanted to get additional functionality out of the Method version, I may end up needing to overload it, and it could become confusing and bloated..

I do see the beauty in the methods binding being extremely transparent..

I actually hate the generic DataSource and DataBind() part of asp.net, while they have their uses in composite controls, it seems a little 'odd' in a lot of situations..
asp.net 2.0 anthology book review - November 1, 2007 - [...] in timing the release of this book - as so much exciting is happening now in the [asp.net] web development space: mvc, jquery, etc.  and the book is [...]
bonder - January 6, 2008 - I'm late to the party, but thought I'd share my comments. Rob's demo is good, but with one real weakness -- the controller code rendering HTML directly. This is so "un-MVC" that it shouldn't appear even in a demo, IMO. Other than that -- great stuff! :) --Bruce
Rob Conery - January 6, 2008 - @bonder (Bruce) - fair point, but remember that the Controller is responsible for rendering the view. While I agree that it's not optimal (especially the way I wrote it here), having a controller send HTML to the Response stream is at the core of what it does. My biggest problem is the timing of this post - I wrote it before there was a toolkit :) so unhinging the HTML from the controller wasn't easy. I have a neat demo coming up that allows for this, and being who I am, I'll make sure to post it so I can get hammered again :) for spaghetti code :):). I wouldn't say this is "Un-MVC" - just not optimal for sure :).
James - March 6, 2008 - Rob,
When are we likely to see some demos (screencasts etc.) for using the latest Subsonic, together with the latest MVC? I have an impending project for which I'm still deciding whether to go Webforms or MVC. It'd be great to see it all in action together?
Gecko