MVC Templating - jQuery Style!

I've messed around with a variety of ways to makeMVC templating better but I've never really been satisfied with any of my solutions until now.

One of the things that I really like about jQuery is that it allows for better separation of markup from code since you can use CSS selectors to find and change elements on the page. ASP.NET MVC requires server side blocks of code to be mixed into the markup to display content.

The other day I started working on a new project I've named Cobalt - A jQuery style, context aware templating framework for ASP.NET MVC. I'm not quite ready to release the code but I wanted to share the idea in advance.

"jQuery Style" means that you can use method chaining and CSS selectors to update content within the view.

Let's pretend we have the following markup

<!-- Index.aspx (Markup) -->

<%@ Page ... %>
<html>
    <head>
        <title>Untitled</title>
    </head>
    <body>
        <h1>Title</h1>
        <div class="content">
            <p></p>
            <ul></ul>
        </div>
        <a id="back" >Previous</a> | <a id="next" >Next</a>
    </body>
</html>

Using Cobalt, we can use jQuery style code to select the correct elements and update content in server side code at the top of the view.

//Index.aspx (Server Code)

//select tags by their names
this.Find("h1")
    .Text("My Page");

//select tags by name
this.Find("a")
    .Attr("href", this.Url.Action(link.Text())
    .Css(new { color = "#f00" });

//find by ID
this.Find("#back")
    .Remove();

//use other selectors
this.Find(".content > p")
    .Html(this.Model.PostContent);

//select classses and children
this.Find(".content > ul")
    .Remove();

//or perform sub queries
this.Find(".content", content =>
    content.Find("strong")
        .Text(bold.Text().ToUpper())
            .AppendTo(content)
            );

Keep in mind, this code is part of the view and not in a code behind file.

Of course, this view could probably be just as easily updated using a Model and assigning values using server side blocks of code, but consider some of the advantages of keeping our code separated.

  1. Markup stays free of server side code blocks so it is easier to pull the content from external resources (for example a database)
  2. If the markup is constantly being changed by another team member (for example a designer) we can avoid tinkering with the new HTML and instead make changes to our selectors (if even needed).
  3. Many web developers are familiar with CSS selectors - The template could be understood by other team members and developers regardless of programming language choice.

Being Context Aware

The context of a selector is another important part of Cobalt. If you're working in a UserControl you might changes to stay within the context of the control. At the same time, you might want some of your commands to change other parts of the page.

<!-- SomeControl.ascx (Markup) -->

<@ Control ... %>

<h3>Latest Tweets</h3>
<ul class="tweet-list" ></ul>
<a href="http://www.twitter.com/hugoware" >Follow Me!</a>
<% =this.Html.ActionLink("Rendered Link", "Index") %>

When you use the this.Find(...) command, the actions are queued up and executed only for the calling control. That said, you can use the this.Page or the this.Parent properties to change the context (and scope) of a command.

//SomeControl.ascx (Server Code)

//context stays inside of the UserControl
this.Find("ul.tweet-list")
    .Html(this.Model.Tweets);

//context stays inside of the UserControl
this.Find("h3")
    .Text("Follow Me!");

//finds both links (static and rendered)
this.Find("a")
    .Attr("href", this.Url.Action("Home"))
    .Text("changed!");

//context changes to the page level
this.Page.Find("head")
    .Append(this.Model.Stylesheet);

//updates the title in the page
this.Page.Find("title")
    .Text("Changed the title!!");

//never finds the title (context is in the control)
this.Find("title")
    .Text("Title not changed!");

Again, this might be a little extra code in some cases, but we are also able to access elements all over the page without needing to apply special WebControls or wire up to the Page life cycle.

It is worth pointing out that both links will be discovered by the a selector since the actions aren't actually invoked until after the page is rendered. This means you can use MVC like you normally would and still use Cobalt to find and change elements. In fact, as far as I can tell this should work with WebForms as well as MVC, but I'd have to test it before I could say how well it works.

Work In Progress

This project is only a few days old right now but is already coming along very well. I'll be posting some code soon but till then feel free to share any ideas you have.

May 17, 2010

MVC Templating - jQuery Style!

Post titled "MVC Templating - jQuery Style!"