Scott Bellware recently blogged about wanting those of us in the "MonoRail Mafia" (sorry, couldn't resist) to show more examples and documentation of this thing called MonoRail. So in an effort to try and inform folks about some of the cool things in MonoRail, here's another installment of creating and using view components in MonoRail.
On my current ASP.NET project which I recently ported over to MonoRail, I'm really liking how easy it is to create reusable UI components using the MonoRail framework. I've been going crazy porting over all of my previous custom "server controls" and "user controls" from the standard WebForms model to utilizing view components and shared sub views in MonoRail. Here's a couple examples...
One of the standard controls I've been using is a styled LinkButton using a hyperlink instead of an input text tag, so I can better control the look and feel in CSS. Creating this as a reusable MonoRail view component was very easy.
So I wanted to be able to do something like this in my views...
${Form.FormTag({ 'id':'someForm', 'action':'index' })}
<?brail component LinkSubmitButton, { "linkText":"Submit", "formToSubmit":"someForm" } ?>
${Form.EndFormTag()}
Then the code for implementing this is pretty simple...
public class LinkSubmitButtonComponent : ViewComponent
{
public const string FormSubmissionJavascriptFormat = "javascript: document.getElementById(\"{0}\").submit()"
private string linkText;
private string formToSubmit;
public override void Initialize()
{
base.Initialize();
linkText = ComponentParams["linkText"] as string ?? "Submit";
formToSubmit = ComponentParams["formToSubmit"] as string ?? "form1";
}
public override void Render()
{
base.Render();
string javaScriptToRender = string.Format(FormSubmissionJavascriptFormat, formToSubmit);
string html = string.Format("<a href='{0}' class='linkButton'>{1}</a>", javaScriptToRender, linkText);
RenderText(html);
}
}
Pretty simple stuff. It's amazing how less stressful it is to create this kinds of components when you don't have to worry about the noise of page lifecycles, control hierarchies and viewstate.
Taking it a step further, I noticed I have a lot of pages that need to use the same simple search form, which just so happened to use the link button component shown above. So I could either create a shared sub view for it or create a view component. Using a shared view is probably best in this situation, but I'm going to show an example of it as a view component because I want to show how you can create composite view components all in code. (Something I haven't seen much about in the docs...)
Anywhere I needed a simple search form, I wanted to be able to just write something like this...
<?brail component SearchForm, { "actionToFire":"index.rails" } ?>
And this could be implemented as a view component like this...
public class SearchFormComponent : ViewComponent
{
private const string searchFormName = "searchForm";
private const string searchCriteriaKey = "searchCriteria";
private string actionToFire;
private string searchCriteria;
public override void Initialize()
{
base.Initialize();
actionToFire = ComponentParams["actionToFire"] as string ?? "index";
searchCriteria = PropertyBag[searchCriteriaKey] as string ?? string.Empty;
}
public override void Render()
{
base.Render();
ShowFormStart();
ShowSearchCriteriaTextBox();
ShowSearchButton();
ShowFormEnd();
}
private void ShowFormStart()
{
RenderText(new HtmlHelper().Form(actionToFire, searchFormName, "post"));
}
private void ShowSearchCriteriaTextBox()
{
RenderText(
string.Format("<input type='text' id='{0}' name='{0}' value='{1}' />", searchCriteriaKey, searchCriteria));
}
private void ShowSearchButton()
{
LinkSubmitButtonComponent linkButton = new LinkSubmitButtonComponent();
linkButton.Init(RailsContext, Context);
linkButton.Context.ComponentParameters.Add("linkText", "Search");
linkButton.Context.ComponentParameters.Add("formToSubmit", searchFormName);
linkButton.Initialize();
linkButton.Render();
string html = string.Empty;
linkButton.Context.Writer.Write(html);
RenderText(html);
}
private void ShowFormEnd()
{
RenderText(new HtmlHelper().EndForm());
}
}
Notice I'm constructing the link button component in code, initializing it using the current IRailsEngineContext and IViewComponentContext, rendering it, grabbing the rendered html via its TextWriter and rendering it out to the view. I'm sure there are better ways to achieve this that I haven't yet discovered, but this is one way you can do it.
You can access the source code here. (Along with the rest of my emerging sample code library)