views:

141

answers:

4

I'm enjoying Asp.Net MVC and am looking to use it in an upcoming project. Part of the project, however, is an emphasis on being able to expose the project Views to designers for things like theming and so on. One problem I'm anticipating is that Asp.Net MVC views are rather developer-centric. I really don't want to have to educate designers on the intracies of <% vs. <%= let alone something like <% foreach ...

Take a typical MVC menu structure, for example.

<div id="menu">
 <ul>
      <li><%= Html.ActionLink("Home", "Index", "Main")%></li>
      <li><%= Html.ActionLink("About", "About", "Main")%></li>
      <li><% Html.RenderPartial("LogOnUserControl"); %></li>
  </ul>
</div>

I'd much rather be able to tell designers to go with something like

<div id="menu">
 <ul>
      <li>{ActionLink "Home", "Index", "Main"}</li>
      <li>{ActionLink "About", "About", "Main"}</li>
      <li>{Partial "LogOnUserControl"}</li>
  </ul>
</div>

Or

<div id="menu">
 <ul>
      <li><my:ActionLink text="Home" action="Index" controller="Main" /></li>
      <li><my:ActionLink text="About" action="About" controller="Main" /></li>
      <li><my:Partial name="LogOnUserControl" /></li>
  </ul>
</div>

Yes, that last looks suspiciously like a raft of UserControls. Personally, I'm not a fan of actually using UserControls to do this if only because the rendering of those controls happens after pretty much everything else (as I understand it) and I'd prefer something that fits more in line with the MVC lifecycle. All I really need is a set of placeholders and a way to replace them with the relevant rendering.

So where's the best place to do so and what kind of trade-offs am I looking at here. I can imagine a couple of angles to come at this:

  • A custom ViewPage class where I can override something relevant. ViewPage.RenderView or ViewPage.FrameworkInitialize, maybe, but how you get at the text from there I don't know.
  • Create a custom TextWriter and override ViewPage.CreateHtmlTextWriter that I can then intercept the text output for replacing stuff. This is pretty late in the cycle, though, and will mess with other custom filtering if I'm not careful.
  • Create my own IView and ViewEngine classes. I didn't get far down this path before wondering if I was headed to a very bad place.
  • Custom UserControls that can mimic the functionality needed.

Opinions? Other options? Is my own ViewEngine my best option? My own ViewPage? Or are UserControl objects going to be adequate (please say no)?

+5  A: 

Take a look at Spark. It's syntax is similar to your example.

Chuck Conway
+1 for Spark, but your designers still cannot be clueless.
mxmissile
spark looks like an odd mangling of HTML and back end logic.
DA
I don't want clueless designers. I just don't want to force them to learn developing while they're at it.
Jacob Proffitt
You're right, Charles, Spark looks like it can provide what I want. It's a little intrusive, but that may simply be a consequence of what I want...
Jacob Proffitt
@Jacob, I've never used Spark extensively, but I've heard good things. Most say it's how web development should be.
Chuck Conway
Your designers should know HTML so Spark should be great.If they didn't know HTML it honestly would not matter what you did...
Min
@Min said but true!
Chuck Conway
Absolutely. Html/CSS etc. is what designers *do*. What I don't want them to have to do is learn code APIs.
Jacob Proffitt
+1  A: 

As Charles Conway said, Spark is definitely way to go. Look at example. First, you create partial view in _ActionLink.spark with code:

<viewdata text="String" action="String" controller="String">
${ Html.ActionLink(controller, action, text) }

Then you use it like that in other views:

<ActionLink text="Home" action="Index" controller="Main" />

You just have to prepare partial views for your designers and then they an create view in prefered style. Isn't it easy?

EDIT:

Sorry, that was wrong. <ActionLink text="Home" action="Index" controller="Main" /> will not work, because Home, Index and Main are treates as variables. It may not be that easy to do it as you wish.

LukLed
<ActionLink text="'Home'" action="'Index'" controller="'Main'" /> will do. They're not variables, they're C# code.
queen3
I wanted to find solution not requiring double quotes usage, because it is not too intuitive.
LukLed
A: 

While answered and accepted, I don't see the example of this: why don't you just use:

<li><a href='<%= Url.Action("Home", "Index")%>'>Main</a></li>

Similar in Spark. I prefer this to Html.ActionLink, Html.BeginForm, etc, whenever possible (almost always except for form controls where it's easier to have automatic name encoding with Html helpers).

And your designers do see the link.

queen3
The difference between Url.Action and Html.ActionLink is one of taste, I think. Either one will require designers learning a programming API and neither addresses the added pain of Html.RenderPartial and learning the difference between <% and <%= (and bugs where you add or leave off the ;). For my purposes, this is functionally equivalent to what I want to avoid.
Jacob Proffitt
+1  A: 

I have a similar requirement for a current project, except my 'designers' are more or less end users (people that know their way around html, but it's not their job) which leads to some additional challenges. My implementation is probably too specific for your needs but I'll quickly explain it lest it be useful to anyone else.

I wanted to totally avoid any kind of code in the views for the pages they would be designing so I decided to go with a templating system that does something similar to your point #2, but not at runtime. I am also using spark, although it's not for the benefit of the users as they won't be touching anything that looks like code.

Basically the users create a full html-only template that has placeholder tags for user controls and partial views. The users then upload the template through an interface that parses it and turns it into a spark view.

eg. For a picture gallery, <gallery /> is parsed into something like !{Html.Gallery(Model)} or <use file="Gallery"/>. For a description field, <desc name="Kitchen" /> is parsed into !{Html.Description(Model, x => x.Descriptions.Kitchen)}. It also checks that "Kitchen" is in fact a property for the object/page that is being templated, or that the Model being passed in for a gallery actually contains a collection of images to avoid runtime errors.

Further properties can be specified in the tag to pass additional parameters to the parsed controls. If the control specified requires Javascript, then it is also included in the view. If there are any problems parsing, output is return specifying which placeholder is invalid and why.

Jay
Not a bad idea. I suppose you could put your interpreter in the runtime page lifecycle, but since it's a one-way, one-time process it's probably best to have the extra step to deployment.
Jacob Proffitt