I tend to write titles using additions and equals... Hmm... Guess I like simple math... Who knows?
Anyway, I want to stress out today how cool I find Monorail right now! I always thought it was a cool framework, but after I wired my controllers through Windsor (which allowed me to use my repositories, services and factories in the controllers), got Restful APIs in my controllers (yes, you read that right, and I'm telling you how) and got ExtJs wired up, I've been thrilled with it!
So, without further ado, let's check it out!
Introduction
ExtJs is an amazing JavaScript framework. If you haven't tried it, stop reading now and go check it out. It is all you ever wanted in web usability. It's Web 2.0 done proper.
I specially love the grid control, which gives you all sorts of built-in functionality, like paging, ajax update, templating of rows, row selection, sorting per column, column hiding and more.
One of the most amazing features is that it takes an URL as data source, as long as the url renders XML Content-Type. This goes hand in hand with the ability of MonoRail of invoking controller actions using an URL. So all you need to do is create an action that renders XML and change the Content-Type of the Request to "text/xml" (or something like that), right?
Or you could...
Restful Controllers
One of the things that motivate me to keep doing Open-Source work is that sometimes you just think: "Thank god someone took the time to do that!". This just happened yesterday when I stumbled on the Monorail Rest Support. This is a contrib project in the MonoRail trunk. This very nice project will allow you to use C# 3.5 to generate different responses to an action. What does that mean? Let's see an example:
1: RespondTo(format =>
2: { 3: format.Xml(xml => xml.Serialize(entities));
4: format.Html(html => html.DefaultResponse());
5: });
What that means is that if my action is accessed using a Content-Type of XML MonoRail returns an XML Serialization of the entities I passed in (a simple IList<WhateverType>), which can be consumed by whoever needs it, in my case the ExtJs grid. Now, if someone requests my action using the regular content type of text/html, the regular flow of MonoRail takes place, rendering whatever view is associated with my controller. Now, is this neat, or WHAT?
My point about Open-Source is that I don't know that much about MonoRail (not yet at least) to be able to craft that Restful Controller API. Don't get me wrong, I always wanted that in MonoRail, I do get a Restful API, and I'd probably would be able to create it if I wanted to(the code is surprisingly straightforward, I advise you to check it out). The GREAT thing is that I don't have to, since there's an active community of very bright people around Castle Project. That's what I dream happens with Stormwind Project (which is largely inspired and motivated by Castle Project).
Anyway, enough of that and let's get to the code. I'll show code that uses a repository to retrieve some Tenents (repository code is off the scope of this post, but I trust you know how to get some entities out of your data store, right?), then uses that collection to respond to an ExtJs grid.
The Controller
Let's start with the controller. Let's create a simple controller called TenentManagementController:
1: public class TenentManagementController : RestfulController
2: { 3: private readonly ITenentRepository tenentRepository;
4:
5: public TenentManagementController(ITenentRepository tenentRepository)
6: { 7: this.tenentRepository = tenentRepository;
8: }
9:
10: public void Index()
11: { 12:
13: }
14:
15: public void GetListOfTenents()
16: { 17: RespondTo(format =>
18: { 19: format.Xml(xml => xml.Serialize(tenentRepository.FindAll()));
20: });
21: CancelView();
22: }
23: }
What does this controller do? First it has an action called Index, which actually renders our grid. Then there's the action the grid calls to data bind itself to: GetListOfTenents.
This controller inherits from RestfulController. You can use this controller if you add a reference to Castle.MonoRail.RestSupport.dll to your project. You can find that project in here. It's an amazing piece of work, and if you can contribute to it, please do. I know that as soon as I need something that is not there (and I understand the code any better) I'm contributing. It's very easy to submit a patch to Castle. Just give it a try, you might even like it!
The View
Then, let's check the Index.vm view.
1: <script>
2: Ext.onReady(function(){ 3:
4: // create the Data Store
5: var store = new Ext.data.Store({ 6: // load using HTTP
7: url: '$siteRoot/TenentManagement/GetListOfTenents.ashx',
8:
9: // the return will be XML, so lets set up a reader
10: reader: new Ext.data.XmlReader({ 11: // records will have a "Tenent" tag
12: record: 'Tenent',
13: id: 'Id'
14: }, [
15: // set up the fields mapping into the xml doc
16: // The first needs mapping, the others are very basic
17: {name: 'Id', mapping: 'Id'}, 18: 'Name',
19: 'CreatedOn'
20: ])
21: });
22:
23: // create the grid
24: var grid = new Ext.grid.GridPanel({ 25: store: store,
26: columns: [
27: {header: "Name", width: 120, dataIndex: 'Name', sortable: true}, 28: {header: "CreatedOn", width: 180, dataIndex: 'CreatedOn', sortable: true} 29: ],
30: sm: new Ext.grid.RowSelectionModel({singleSelect: true}), 31: viewConfig: { 32: forceFit: true
33: },
34: height:210,
35: split: true,
36: renderTo: 'results'
37: });
38:
39: grid.getSelectionModel().on('rowselect', function(sm, rowIdx, r) { 40: alert(r.data.Id);
41: });
42:
43: store.load();
44: });
45: </script>
46:
47: <div id="results">
48: </div>
If you ignore all the noise, let's concentrate in the very interesting points here:
- The Ext.onReady function. This is a function that's invoked after the DOM has finished loading. That's perfect for us, since we want our Grid to be initialized as soon as possible.
- The Ext.data.Store class. This class is responsible for data binding your XML data to the grid, by translating the XML to a column-based data structure understood by Ext.grid. I'm sure you can check on the comments or on Ext documentation (which is one of the best I've ever seen) to understand how to do it.
- The Ext.grid.GridPanel class, which is the one that actually renders the grid. Again, checking Ext documentation is your best bet, since I can hardly explain it as well as they do. This one basically sets the look & feel of your grid. Notice the renderTo attribute. That tells that this grid should be rendered inside the element with id of 'results' (our div).
- The grid.getSelectionModel().on('rowselect') event. This happens whenever the users click on a row. You get back the data entity (the record) that was used to bind that row (EAT THAT, GRIDVIEW!). This way you're able to retrieve any sensible data you need in order to load details in another window, save something or whatever you need to do with that data.
- The SINGLE most important line of them all: store.load();. This is the code that actually calls on the data store to update itself using whatever XML is returned by the url specified. The amazing thing here is that you can call store.load() as many times as you like, so if you update some data that's in the grid, just call store.load() and you get fresh data.
Conclusion
Now there's only a few more steps involved...Oh...No...Wait... It's done! God damn it! I wanted one of those huge posts that everyone go WOW because they won't actually take the time to read, but MonoRail and ExtJs are too simple to give me enough horsepower for that.
So if you just execute TenentManagement/index.ashx (in my case, since I don't use the .castle extension), you will see a very nice 2-column grid with the data returned by your controller action. Is that cool or what? Did you know you could implement KILLER-Looking, FEATURE-INTENSIVE Grids in like 5 minutes? Knowing the right tools makes a lot of difference, now doesn't it?
For the nitpickers of the day who are saying now "I won't do that because ExtJs is a paid framework, so I would have to pay for the license".
First of all, you do build software, don't you? You do like to get paid to do that, right? Why do you think they should just give it to you for free, if you don't do it yourself? How about donating your salary to charity?
Second, ExtJs is WAY cheaper than writing all that stuff yourself. It almost feels like you are ripping them off, and not the other way around as you might be thinking.
So if you are feeling that way, stop being a cheap developer and understand that the huge amount of time this framework will save you just pays the prices of the licenses itself.
I hope that's helpful for someone out there. If you need anything, just shout here in the comments, but I encourage you to try both Castle Project and ExtJs communities. Both very active and helpful.
#144