- Posted by Jakob Andersen on March 24, 2008
I have had some discussions with a few people about why i would use MonoRail as it (for these people) seem like a step back from WebForms. First of all its important to understand that WebForms provide a statefull abstraction over something that is in fact stateless. In my opinion this makes something that in most cases is simple overly complicated. And secondly it often results in "funky" errors that is hard to debug because of the many steps involved before actually rendering a web-page to the user(i.e. the page lifecycle).
So to show of a single feature in MonoRail that makes a common scenario very easy i have decided to show you how to handle maintaining a many-to-many relation in a webpage. Assume that we have set up ActiveRecord classes for a Person class and this has a many-to-many relation to an Hobby class. That is one person can have multiple hobbies and different persons can have the same hobbies. To show this when editing a person on a web-page i would like to have a list of all the hobbies and the ability to check/uncheck which hobbies the person im editing has.
Using MonoRail and utilizing the available FormHelper i can get this list of checkboxes populated quite easy. Lets take a look at the Controller first:
public class PersonAdminController : ARSmartDispatcherController{
public void List(){
PropertyBag["Persons"] = Person.FindAll();
}
public void Edit([ARFetch("id")] Person person){
PropertyBag["Person"] = person;
PropertyBag["AllHobbies"] = Hobby.FindAll();
}
public void Save([ARDataBind("Person", AutoLoadBehaviour.NewInstanceIfInvalidKey)] Person person){
person.Save();
Flash["Message"] = "Person " + person.Name + " saved";
RedirectToAction("List");
}
public void Delete([ARFetch("id")] Person person){
person.Delete();
Flash["Message"] = "Person " + person.Name + " deleted";
RedirectToAction("List");
}
public void Create(){
RenderView("Edit");
}
}
So this is all needed to List, Edit, Create and Delete our persons. The List view is pretty straight forward but lets include it anyway for completeness:
${UrlHelper.Link("Create new person", {"action" : "Create"})}<br />
<table>
<% for Person in Persons: %>
<tr>
<td>${Person.Name}</td>
<td>
${UrlHelper.Link( "Edit", {"action" : "Edit", "querystring" : "id=" + Person.ID })}
</td>
<td>
${UrlHelper.Link( "Delete", {"action" : "Delete", "querystring" : "id=" + Person.ID })}
</td>
</tr>
<% end %>
</table>
So this lists the persons with their name in a tabular form and exposes our Create, Edit and Delete actions. Now lets get to te interesting part that is the Edit view:
<form action="Save.castle" method="post">
Name:<br />
${FormHelper.TextField("Post.Title", {"class" : "AdminPostTitle"})}<br />
<-- Other fields -->
Hobbies:<br />
<%
Items = FormHelper.CreateCheckboxList("Person.Hobbies", AllHobbies, {"value" : "ID", "text" : "Name"})
for Item in Items:
output Items.Item()
output Item.Name
end
%>
<-- Submit button etc -->
</form>
This is the interesting part, by naming our CheckboxList "Person.Hobbies" which corresponds to the name of the collection of hobbies on our person MonoRail will automatically check the hobbies associated with this person. And when posting the form the selected hobbies will be bound to the Person parameter in our Save action. So with the above code we have editing of many to many relations.
Using WebForms the above would probably involve manually adding each item to a checkboxlist while comparing with the existing values. Or iterating over the items in the checkboxlist after databinding to set if they should be checked or unchecked. And the code for handling this when saving in WebForms isn't trivial as well.
If you would like to prove me wrong i welcome you to provide your WebForms code for handling the above scenario.