nik codes

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!

Single Post Navigation

23 thoughts on “Something Razor Should Learn From JSX

  1. Isn’t the problem that you’re getting to here that your Controller Action explicitly knows about the View, that you’re removing the separating between them? Now sure, the Action doesn’t know the layout part of the View but it knows specifically that that View will be used for this Model in this Action. Where I see this falling down is if you start using content negotiation to request different kinds of content for the same Action (although, true, less common in MVC and more common in WebAPI, but looking at ASPNet5 this separation is gone).

    To draw a comparison back to your React example would that not be like having a Service which goes to some backing store (localStorage, indexedDB, REST API), creating the State for the component and then passing the State to the component to display? Again this sounds like a SRP problem, Service should get data and expose what it _thinks_ it should expose. Component consumes Service, consumes exposed data and sets up the State which the UI uses.

    • nikmd23 on said:

      > Isn’t the problem that you’re getting to here that your Controller Action explicitly knows about the View, that you’re removing the separating between them?

      The reality is, that’s already the case. It isn’t *always* the case, but it is a lot if you already are following the best practices I mentioned for ViewModels.

      Before my proposal, in your controller you’d new up a VM with a name like HelloMessageViewModel – which is a one off view – intrinsically tying your action to the view.

      If you use the same model with different representations (HTML, JSON, etc), than I agree with you, it’s a reusable model and as such it should not be defined directly in the view. My experience, however, has shown me that a majority of VM’s don’t fit into this category.

      • +1, and for the few cases where you may start with a regular view, but then later decide to return JSON, it’s a pretty easy refactor to just cut out the contents and create a separate class. If I’m making just a webapp, though, and it gets pretty big, then I’d much rather just have the viewmodels in with the view instead of having a massive model folder to track models.

    • Even when doing conneg in Web APIs, my actions always know the media types that they are capable of rendering . That’s one reason I like the new Produces attribute in MVC6. The idea that any arbitrary resource can return any format that is configured globally seems a tad Utopian.

      • nikmd23 on said:

        I tend to agree.

        The few times I’ve done conneg, I’ve had to change the model before serializing it anyways. For example, the JSON representation always seems to need extra properties that the HTML version does not (especially with hypermedia scenarios).

  2. Pingback: The Morning Brew - Chris Alcock » The Morning Brew #1821

  3. mirhagk on said:

    One downside is that it makes the views a compile time resource. Normally they views are dynamically bound, allowing you to change the views without recompiling. This would change that, by having to create a new type in the assembly

  4. This could really simplify RAD-style development with MVC. If you can easily add a model type to your view and then immediately use it from your controller, it takes out the step of creating a new file. For action methods that always return View(), and view models that aren’t immediately reusable (YAGNI applies here), this would be a welcome change. The closest thing today would be “@model dynamic” with an anonymous model object, which would actually be closer to the feel of React’s props/model, but the strongly-typed approach here would be welcome and comfortable for C# devs.

    • I create a folder and put my viewmodel, view and controller in the same folder. That way they are physically connected.

      • nikmd23 on said:

        Really!? I assume you update the path that the View Engine uses to resolve Views then as well? Or do you always reference the View by path in your action?

      • I don’t know how I feel about the View and Controller being in the same folder, but that does bring up a good point… there’s nothing stopping you today from declaring your ViewModel type in the same *file* as your Controller, or even as a nested type right above or below your action method. That might actually feel more natural than the Razor approach here, and would address a number of concerns here in the comments, and wouldn’t require the step of creating a separate ViewModel file, although at the expense of bloated controller code. This might be a case where I’m okay with lots of nested types. i.e. “@model MyApp.Controllers.HomeController.IndexViewModel”

  5. So if I read the post correctly, you want the view and viewmodel to be one in the same. How does that differ from WebForms? (not trying to troll, honest question)

    Let’s go down the list of what webforms could do:

    1. Declare properties directly on your page (Aspx)
    2. Create components that can be reused declaratively
    3. Combined page logic with view logic
    4. Rendering model very similar to React rendering (just server-side)

    Do we just need a WebForms revival in ASP.NET vNext (DNX) while considering the good and bad of the technology?

    • nikmd23 on said:

      There is a difference between logical and physical coupling.

      All your examples logically couple everything together. My proposal still keeps everything logically separated, but allows for physical co-location, if and when desired, to reduce friction.

      In WebForms there is no (easy) way to separate things after the fact. It’s also only has one model to follow, where this provides choice.

  6. You can put your view models inside the Views folder alongside the corresponding view, and then nest the model beneath the view. That would put them right next to each other without complicating or changing Razor views.

    That said, I always create a separate class library to hold all the code, leaving the MVC project with only static files, scripts and views. To me, this is a cleaner approach and a model is only ever a ctrl-click away.

  7. ViewModels should be in separate files, we fought hard to get to this point, you can still put VMs in the same folder with Views as other’s pointed out but putting them right within the View makes no sense and it’s a step backwards in terms of SRP, loose coupling etc. If you architect your web app correctly you can achieve a high degree of reusability of VMs. VMs also include DataAnnotations and other attributes. Also I don’t see how binding would work with VMs inside View files. All these stands when using MVC although I believe you should transition to Web API ASAP (if possible) and get rid of server-side rendering completely.

    As for React, when I saw their frontpage samples I thought “What the heck is that?” and turned away immediately. I’d never use such a hideous syntax. Typical Facebook stuff.

  8. I like this idea. A lot.

    While the idealist in me totally gets the arguments against something like this, and to some extent wants to fight against the idea, we often don’t have the luxury of adhering strictly to “the right way”. At the end of the day, the pragmatist in me wins out 10 times out of 10 on this one for the time and confusion it would save. Why? Because this exact thing has been a major pain point for me in the past and continues to be one today.

    Some of the line of business applications I work on are big. We’re talking 10 different areas with an order of magnitude more controllers, each one with a large number of actions and views. Not the biggest MVC apps out there, I’m sure, but plenty big enough. And you know what? It’s really, really easy to get lost. Jumping from action to view to model gets overwhelming just for the sheer number of tabs that get opened when dealing with two or three. And guess what, while I *might* at some point need more complicated view models, or want to share a view model, or…etc., it rarely happens. Most of the time the view model is just hanging out there with like four or five properties needed for display. And in those cases where you actually *do* need to split it out, this approach allows for that too.

    This does get tricky when thinking about implementation though. Because a strongly-typed view page inherits from the generic WebViewPage, and the generic type parameter is important for passing on down to the strongly-typed HtmlHelper and then the various extensions that use lambda expressions to get at model properties, any approach would have to respect this architecture. Though I like the simplicity, I don’t think an inner class would cut it in this case (or maybe it would?). If the model class goes outside the view class, then there’s the question of what namespace to put it in and how to name it. Plus, that feels a little icky, having the implementation for a class be removed in the compilation from the declaration in the code. Perhaps something could be done with T4 templates, though then there’s the issue of generating extra code file(s), which is kind of what you were trying to avoid in the first place. Also, the results of T4 generation tend to be confusing when using code analysis tools, debugging, etc.

    TL;DR: I love the idea but think it will require buy-in and implementation at the ASP.NET MVC level to get right.

    • nikmd23 on said:

      This specific proposal would certainly require buy in from the ASP.NET team, which I don’t see happening. (And that’s okay.)

      The larger point made, that these things would be a lot easier to maintain if they were closer together seems to have struck a cord. Hopefully there will be something in the future, even if it isn’t this proposal, that will reduce the distance.

  9. Pingback: Is Separating HTML and JavaScript Harmful? | BitNative by Cory House

  10. I’m all for the idea. When working in MVC, having two tabs open instead of three is a nice win. I nearly always have all the corresponding model, view, and controller open at the same time. And your proposal (or something similar) would feasibly still allow for declaration outside if desirable.

    React has opened my eyes to my blind love for separation of concerns. React shows that composing your HTML in your JS has some clear benefits: better errors, strong intellisense, easier refactoring, and lower cognitive load (since you don’t have to mentally tie two interconnected files together). The fact is: Separating HTML & JS is an arbitrary separation of technologies. They’re inherently intertwined concerns. And since they’re intertwined with no explicit interface, I believe they belong together in the same file. More on why separating JS and HTML is potentially harmful here: http://www.bitnative.com/2015/03/18/is-separating-html-and-javascript-harmful/

    Okay, back on the topic of Razor. Sure, there’s a minority of cases where inlining of the Viewmodel doesn’t fly (as discussed above), but as long as those cases can continue to use the existing mechanism, offering a lower friction option like this strikes me as a nice win.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: