nik codes

Archive for the month “March, 2015”

Something Razor Should Learn From JSX

Alright, here’s the thing: I often find the interaction model between Razor Views and ViewModels in ASP.NET MVC to be clumsy and annoying. Recently I’ve searched for ways to make it better, and there don’t seem to be any. This post highlights how I came to the fore mentioned conclusion, a listing of what I’ve tried to make things better, and a proposal for how to improve Razor.

Champion readers of nikcodes.com, please take a gander at what I’m thinking, I’d love to hear your thoughts on all this.

Declare & Proceed

As I’ve learned more about React, Facebook’s JavaScript templating library, my thoughts on coupling Views and ViewModels has been challenged. (For the unfamiliar, React solves many of the same problems as Knockout, but is more similar in style to Razor than Knockout.)

react-opti

To create Views, React (optionally) uses a somewhat contentious super-set of JavaScript they call JSX. JSX mixes declarative XML and HTML literals directly into procedural JavaScript code. Take this simple JSX “Hello World” example for instance:

var HelloMessage = React.createClass({
    render: function() {
        return <div>Hello {this.props.name}</div>;

    }
});
 
React.render(<HelloMessage name="Nik" />, mountNode);

Notice the <div> and <HelloMessage> tags sprinkled into that JavaScript? The argument is that it’s easier to compose Views in a declarative language like XML, so why not provide that power in the same place as the UI logic? Of course, JSX isn’t something a browser natively understands so this all gets “compiled” into something “native”, in this case, standard, run-of-the-mill JavaScript:

var HelloMessage = React.createClass({
    displayName: "HelloMessage",
    render: function () {
        return React.createElement("div"null"Hello "this.props.name);
    }
});
 
React.render(React.createElement(HelloMessage, { name: "Nik" }), mountNode);

The ability to mix declarative and imperative statements together in a single language is nothing new. VB.NET has a very similar feature called XML Literals as well as LINQ (for querying data structures, which C# also has). Heck, the next version of JavaScript, ES6, has support for Template Literals baked right in as well!

What About the Server Side?

React, and front-end JavaScript developers in general, aren’t the only web developers that leverage Views, ViewModels and an MV* architecture though. ASP.NET MVC developers have been using Razor to mix HTML with C# for years now. To be fair, the balance in Razor Views tends to lean more towards the declarative with little “nuggets” of C# logic sprinkled throughout. For example, see this little HelloMessage.cshtml, Razor template:

@model Person
 
<div>Hello @Model.Name</div>

Like the JSX file, this Razor template gets “compiled” into something “native” – C# in this (abridged) case:

[PageVirtualPathAttribute("~/Views/Home/HelloMessage.cshtml")]
public partial class HelloMessage : WebViewPage<Person>
{
    public override void Execute()
    {
        WriteLiteral("<div>Hello ");
        Write(Model.Name);
        WriteLiteral("</div>");
 
    }
}

So let’s stop for a moment and compare JSX to Razor:

JSX Razor
Front-End (Mostly) Back-End (Mostly)
Imperative code (JavaScript) with declarative XML/HTML mixed in. Declarative code (HTML) with imperative C# or VB mixed in.
UI composition, logic and ViewModel contained in same file. UI composition and logic contained in same file.
ViewModel defined in separate class file.
Compiles to native format: JavaScript Compiles to native format: C# or VB

You’ll note that for the most part, JSX is pretty similar to Razor, with a glaring difference about where the ViewModel is defined. I never perceived this to be an issue, until I saw JSX do it a better way.

Pulling Everything Together

Managing ViewModels in Razor and ASP.NET MVC isn’t necessarily difficult, but it certainly isn’t as easy as it is in the React/JSX world. Over time, ASP.NET MVC developers have established well trodden best practices to help manage this disconnect, including:

  1. ViewModels exist as separate classes (as opposed to using the ViewData dictionary).
  2. Each View gets its own explicit ViewModel.
  3. Naming conventions are used to help locate a ViewModel for any given View.

Following these practices mean that, for the most part, ViewModels and Views basically have a one to one relationship and are logically paired together. I tend to believe that if two things go together logically, then it sure would be nice if they go together physically as well.

Unfortunately, Razor does not allow us to declare a nested class inside the View to use as the ViewModel. In fact, it doesn’t allow us to declare a class at all. I know this because this weekend I tried several different ways to make it work. I even tried to leverage the powerful Razor Generator to get something that would pass as co-located Views and ViewModels.

If any reader can come up with a better way to couple the physical location of a View and its ViewModel together, I’d love to hear it in the comments. To me, the nicest solution would be if ViewModel’s could be declared inline with Razor’s @model syntax. Here’s HelloMessage.cshtml again, updated with my proposal:

@model 
{
    public string Name { getset; }
    public int OtherProperty { getset; }
}

<div>Hello @Model.Name</div>

Note, I’m no longer using the @model syntax as it was intended. Typically @model is followed with a type, like “@model Person“. In this proposal, I go directly into declaring what might be thought of as an anonymous class. I could then use that class inside my controller like this:

public ActionResult HelloMessage() 
{
    var model = new HelloMessage.Model { Name = "Nik" };
    return View(model);
}

I think there would be a lot less ceremony in Razor and ASP.NET MVC if it could physically couple Views and ViewModels together, at least in the common use case where that makes sense. If you prefer to keep them separate, or you have a reusable ViewModel, that’s fine too; just with with the familiar “@model {Type}” syntax.

Do you agree? Would you like to see something like this added to Razor? Do you see a way to improve this design, or are there any problems with it? Please let me know!

Post Navigation