Best practice for "graphic" if-statments in MVC 3 - asp.net-mvc

Just started converting to MVC from classic ASP and wondering about best practice for if-statements that is used to decide if a HTML-element should be visible or deciding if that or that HTML-element should be visible.
Example:
if (type = 1)
{
<img src="/Content/Images/gray.jpg" />
}
else
{
<img src="/Content/Images/blue.jpg" />
}
I see several options here.
1. Use if-statements in my views (prefer not to)
2. Use a HTML-helper (will be a lot of helpers = Hard to work with the code)
3. Create HTML-snippets in my controller and pass into my view
4. Work with JavaScript to show/hide element on document load.
The example above if of course a simplification of my problem. Many thanks for the help!
/Mike
EDIT:
Took a bad example, here comes a new:
if (type = 1)
{
<span class="class1">Something something</span>
}
else
{
<div id="id1">
text
<img src="/Content/Images/gray.jpg" />
</div>
}

Personally I'm starting to use a separate layer for loading up by viewmodels with html specific information.
public class ViewModel
{
public string ImageName { get; set; }
}
Keeps the views clean:
<img src="/Content/Images/<%= Model.ImageName %>.jpg" />

I personally prefer to use HtmlHelper extensions or declarative HTML helpers for all but most trivial conditional expressions. However in this case I would probably let my laziness win, and express the conditional directly in the view like so:
<img src="/Content/Images/#(i == 1 ? "gray" : "blue").jpg" />
Edit
For more complex scenarios, definitely HTML helpers. If you're using the Razor syntax, you can create declarative helpers by placing them into your ~/Views/Helpers directory. They're very clean to write and use.
But the idea behind creating a good helper is that it encapsulates an intention. This is easy to test by trying to give your helper a name. If the name is IfOneThenShowSomethingElseGrayImage, you should try to break it into further pieces still.
So let's imagine the intention in your example is to show information about the current user based on the user type. You could create a helper like this:
#helper UserType(int type) {
#if (type == 1) {
<span class="class1">Something something</span>
} else {
<div id="id1">
text
<img src="/Content/Images/gray.jpg" />
</div>
}
}
And then use it in your view like this:
#UserType(Model.UserType)
Even though it's only used on one page, it enhances the readability in the view, and can be reused if necessary.
Just my two cents.

Related

Why can't one Razor helper call another helper?

I'm running the razor code below in a .cshtml file (it's a simplified version of something more complex that I need to achieve), but the renderTestB helper does not seem to execute.
#renderTestA("test string 1", "test string 2");
#helper renderTestA(string input1, string input2)
{
<div>
#renderTestB(input1)
#renderTestB(input2)
</div>
}
#helper renderTestB(string input)
{
<p class="test">#input</p>
}
Why is this? And is there another way of achieving what I'm trying to do?
I realise I could duplicate the paragraph code within the renderTestA helper, but would obviously prefer a re-usable code solution.
What about something like this?
#renderTestA(renderTestB("test string 1"), renderTestB("test string 2"))
#helper renderTestA(string input1, string input2)
{
<div>
#input1
#input2
</div>
}
#helper renderTestB(string input)
{
<p class="test">#input</p>
}
You should consider using Editor / Display templates or custom HTML helpers instead as the #helper functionality was used back before these features became the norm.
As to why you cannot nest them. It introduces a number of problems that are easily avoided by using the syntax I suggested above. Such as... if you have a circular loop of nested helpers, it could easily cause a stack overflow.

On an asp.net-mvc site, Is there a good pattern for dealing with both editable and read only views?

I have an asp.net-mvc website where up until now there have been no entitlements as its been open to everyone. Many of the pages are detailed forms with textboxes, select dropdowns, etc
I now need to change this to make many of the existing pages "entitled" so only certain people have edit capability and everyone else sees a read only page. I am trying to figure out if I should
Create a seperate view for everyone one of my existing views with forms that is just read only html on a page and redirect based on entitlements on the server side, something like
public ActionResult OrderView()
{
var isEntitled = Model.IsEntitled()
if (isEntitled)
{
return("OrderEditableView", GetViewModel());
}
else
{
return("OrderReadOnlyView", GetViewModel());
}
}
or
Reuse the same view and simply disable or hide the "Save" button on the screen.
on my view have
<% if (Model.IsEntitled) { %>
<button id="saveButton">Save Changes</button>
<% } %>
The second option would be much quicker to implement but would be a little weird because it would look like you could edit all of the fields but just dont' see (or see a disabled) Save button on the form.
The first option seems cleaner but I would have to go and create a new view for everyone of my screens and that seems like a lot of work (over 100 views currently)
This seems like a common situation so I wanted to see if there was a best practice on dealing with this situation. Obviously I am looking for a solution given the current situation that I am in but I would also be interested if there were patterns or solution that would be considered best practice or recommended if I was starting from scratch.
I would go for creating a separate partial/full views for both.
Reasons:
easy to change and maintain
validation code only in editable part
clear separation of concerns
cleaner code
may be we can reuse some of the code by abstracting/separating similar code to re usable partial views
easy control over access
Also, in real world we balance among budget,time and effort. so considering that I may use mix of these depending upon the situation.
I'd go for the second options with a single view for both writable and readonly forms.
For the visual side, I'd implement a few things in addition to hiding the Save button:
Set the input fields to readonly. You can easily do this with client side Javascript.
Change the colors (and possibly other attributes) of the input fields to emphasize that they are readonly (e.g. remove the borders). This can be easily achieved by a style sheet and a single additional class name in the form tag.
The authorization on the server side is simple as well. Just annotate the action accordingly.
It could look like this (simplified, using Razor and jQuery):
View:
#using (Html.BeginForm("AddressController", "SaveChanges", FormMethod.Post,
new { #class = Model.IsEntitled ? "regular" : "readonly"}))
{
<p>
#Html.TextboxFor(model => model.Name)
</p>
<p>
#Html.TextboxFor(model => model.Address)
</p>
#if (Model.IsEntitled) {
<p>
<button id="saveButton">Save Changes</button>
</p>
}
}
<script type="text/javascript">
$( document ).ready(function() {
$('form.readonly input').attr("readonly", true);
});
</script>
Controller:
public class AddressController : Controller {
[Authorize(Roles = "xyz")]
[HttpPost]
public ActionResult SaveChanges(Address address) {
...
}
}
CSS:
.readonly input {
border: none;
}
an option is to do the switch in the view..
<% if (Model.IsEntitled)
{
Html.Partial("OrderEditablePartialView", Model)
}
else
{
Html.Partial("OrderReadOnlyPartialView", Model)
}
%>
another option could be to create custom Html helpers to render the elements as editable or readonly, which would give you some flexibily on how you handled each element.
crude example:
public static MvcHtmlString FormTextBox(this HtmlHelper helper, string name, string text, bool editable)
{
return
new MvcHtmlString(
String.Format(
editable
? "<input type='text' id='{0}' name='{0}' value='{1}'>"
: "<label for='{0}'>{1}</label>",
name, text));
}
The quickest solution would be some javascript that disables all input elements on pages where the user isn't entitled. How to implement this is up to you, it depends on how your views and models are structured.
This way you can reuse the edit-views without even having to edit them, but depending on the kind of user you have you can expect "site not working" calls when your forms appear disabled.
patterns or solution that would be considered best practice or recommended if I was starting from scratch.
The proper way to go would be DisplayTemplates and EditorTemplates with proper viewmodels, but from your "create a new view for everyone of my screens and that seems like a lot of work" comment I gather you're not interested in that.
So I'd go with the former. Please make sure you check the permissions in the [HttpPost] action methods, as malicious users can easily re-enable the form and post it anyway.

MVC alternatives to UserControls

We're in the process of transferring a big WebForms app to MVC.
In the WebForms app we have some reusable controls (.ascx). For instance, a TextBox that shows username suggestions as you type. The control adds some JS/CSS to the page, has some server-side logic and custom properties (like SelectedUserID).
What should it be in an MVC app? How do I encapsulate those things into one reusable entity in an MVC app? Maybe a partial view? But you can't add, say, JS/CSS to the page's <head> from a partial view (preferably, with a check that it's not already been added)... Also, how do I return something like the mentioned SelectedUserID in a partial view?
To rephrase the question - how would you implement such a control in an MVC app?
PS. I know you can still use .ascx user-controls in MVC apps but it seem a "hacky/legacy" way. What is the "legitimate" option?
Your question is pretty broad. All my comments/answer are going to be in Razor. In my opinion, if you're going to switch to MVC, you might as well use Razor. There are plenty of good reasons to switch, but I'd say my top pick for anyone who is migrating, the best reason is two fold; first razor allowed me to drop some bad habits about how programming works with webforms and in the same manor it forced me to re-write my code instead of trying to copy and paste and change the original code to work in MVC.
The control adds some JS/CSS to the page, has some server-side logic and custom properties.
What should it be in an MVC app?
This is a pretty religious software question. There are plenty of ways of doing it.
So onto my religous opinion about how to add JS/CSS in MVC. One way to include server side is to create a region in the Layout/Master template.
Views/_ViewStart.cshtml
#{
Layout = "~/Views/Shared/Layout.cshtml";
}
Views/Shared/Layout.cshtml
<!doctype html>
<head>
<link href="/Styles/Global.css" rel="stylesheet" type="text/css" />
#RenderSection("Styles", required: false)
<script src="/Scripts/Global.js" type="text/javascript"></script>
#RenderSection("Scritps", required: false)
This would allow each view to optionally (because required=false) add any Styles or Scripts using the RenderSecion code.
Views/Home/Index.cshtml
#section Styles {
<link href="/Styles/Home/Index.css" rel="stylesheet" type="text/css" />
}
This is pretty simple, and probably a good solution for many simple to moderately complex sites. This wasn't enough for me, as I needed to do what you requested, and only include files if they were truly needed. Additionally, partial views and templates cannot render sections, which I found to be a giant PITA. So I added some HtmlHelper extension methods. (i'm only going to show the code for Javascript as the Stylesheets are nearly Identical code. These methods don't allow duplicate urls.
Domain/HtmlHelperExtensions.cshtml
public static class HtmlHelperExtensions
{
private const string _JSViewDataName = "RenderJavaScript";
private const string _StyleViewDataName = "RenderStyle";
public static void AddJavaScript(this HtmlHelper HtmlHelper, string ScriptURL)
{
List<string> scriptList = HtmlHelper.ViewContext.HttpContext.Items[HtmlHelperExtensions._JSViewDataName] as List<string>;
if (scriptList != null)
{
if (!scriptList.Contains(ScriptURL))
{
scriptList.Add(ScriptURL);
}
}
else
{
scriptList = new List<string>();
scriptList.Add(ScriptURL);
HtmlHelper.ViewContext.HttpContext.Items.Add(HtmlHelperExtensions._JSViewDataName, scriptList);
}
}
public static MvcHtmlString RenderJavaScripts(this HtmlHelper HtmlHelper)
{
StringBuilder result = new StringBuilder();
List<string> scriptList = HtmlHelper.ViewContext.HttpContext.Items[HtmlHelperExtensions._JSViewDataName] as List<string>;
if (scriptList != null)
{
foreach (string script in scriptList)
{
result.AppendLine(string.Format("<script type=\"text/javascript\" src=\"{0}\"></script>", script));
}
}
return MvcHtmlString.Create(result.ToString());
}
}
Updated Views/Shared/Layout.cshtml
<!doctype html>
<head>
<link href="/Styles/Global.css" rel="stylesheet" type="text/css" />
#Html.RenderStyleSheets()
<script src="/Scripts/Global.js" type="text/javascript"></script>
#Html.RenderJavascripts()
Updated Views/Home/Index.cshtml
#Html.AddStyleSheet("/Styles/Home/Index.css")
Onto your next question...
How do I encapsulate those things into one reusable entity in an MVC app? Maybe a partial view?
Razor supports both partial views and templates. Although technically there is large overlap in what each can do, there are limitations of how each were designed to allow a programmer to take advantage of each when needed.
Partial views do not require a Model/Class in order to be rendered. Here is a completely valid partial view:
/Views/Home/Index.cshtml
#Html.Partial("Partial-Menu")
/Views/Shared/Partial-Menu.cshtml
<div id="menu">
Home
About Us
</div>
Templates on the other had, do required a Model in order to be rendered. This is because Templates are a way to render any class or struct as either an Editing Template or a Display Template.
/Models/Person.cs
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
/Controllers/HomeController.cs
public ActionResult Index()
{
Person model = new Person();
model.FirstName = "Jon";
model.LastName = "Doe";
return this.View(model);
}
/Views/Home/Index.cshtml
#model Project1.Models.Person
#Html.DisplayModel()
#Html.EditforModel()
/Views/Shared/DisplayTemplates/Person.cshtml
#model Project1.Models.Person
<div>#Html.LabelFor(x => x.FirstName) #Html.DisplayFor(x => x.FirstName)</div>
<div>#Html.LabelFor(x => x.LastName) #Html.DisplayFor(x => x.LastName)</div>
/Views/Shared/EditorTemplates/Person.cshtml
#model Project1.Models.Person
<div>#Html.LabelFor(x => x.FirstName) #Html.EditorFor(x => x.FirstName)</div>
<div>#Html.LabelFor(x => x.LastName) #Html.EditrorFor(x => x.LastName)</div>
The way I see it, any model that might turn into a Form for data entry should probably be a Template. This is the way I prefer to encapsulate my models. Using the extension methods provided earlier, both my partial views and templates can load includes as needed (currently only one of my models of oodles of them actually needed to use this).
My preference is to have up to three includes (of each Javascript and Styles) per page rendered. I basically have a Global.css/js, a controller Home.css/js, and a page Index.css/js as the possible includes. It's been very seldom that I have a controller include.
The best solution I've found so far is Razor declarative helpers. They fit awesomely.
#helper UserTextBox() {
<input type="text" />
<!--my own helper that registers scripts-->
#Html.AddJS("../js/myscript.js")
}
Related question: Razor: Declarative HTML helpers
Related note: you can't use #Html helper inside a declarative helper but there's a workaround: https://stackoverflow.com/a/5557503/56621
Well, you can't have something that "automatically" includes css/js. That's something the user has to add to the page (either the master/layout or the current page). But in general, one would create an Html Helper. Don't expect these to be complex systems (like the gigantic grids of asp.net days) but you can do a lot with them.
Yes, a partial page may be easier for simple things. Even simpler might be an Editor or Display Template.
In general, however, most "controls" for MVC these days are jQuery based.
A short video is available here: http://www.asp.net/mvc/videos/mvc-2/how-do-i/how-do-i-create-a-custom-html-helper-for-an-mvc-application
Don't be tempted to include your javascript in these mechanisms. While it may work for a single control, if you have multiple controls on a page you will get duplicate css/js.
I would add the js/css to the main layout then write an html helper to render your custom control.
Many controls that uses jquery in aspnet mvc works this way.
Take a look at this controls here
1) for custom js/css you can use sections like this:
//code in view
#section someName
{
...
}
//code in layoutpage
#if (IsSectionDefined("someName"))
{
#RenderSection("someName")
}
2) i'd better do the following:
create model SelectedUser with Id property
create templates for this model
add a SelectedUser object to any model, which needs it
They must be separated from surrounding text by blank lines.
The begin and end tags of the outermost block element must not be indented.
Markdown can't be used within HTML blocks.

MVC Partials vs User Controls?

I'm trying to understand what the equivalent of a user control is within an MVC application? From what I understand, it's a Partial. The problem I'm finding is that when I use a user control, I'm able to encapsulate the logic within that control and re-use it across multiple pages without having to worry about the parent page.
How does this work with MVC? There doesn't seem to be any way to encapsulate logic with a partial view.. in fact it's confusing the hell out of me.
Does this mean that for any page that might use the partial, the controller would always have to return the data for the partial just in case it's required? Doesn't this seem incredibly inefficient? I can't seem to wrap my head around how this works...
You can add logic using razor syntax. For example, you can use conditional statements to manipulate the final output.
#{
if(whatever == true)
{
<p>Whatever is true</p>
}
else
{
<span id="someId">
<label for="enterName">Enter name:</label> <input type="text" id="enterName" />
</span>
}
}
In addition to this you can use #helper syntax like
#helper MakeLink(dynamic id)
{
<div id="actionButtons">
#Html.ActionLink("Some Title", "Some Action", new {id = id}) |
#Html.ActionLink("Another Title", "Another Action", "Some Controller", new {id = id})
</div>
}
The logic in partials is slightly different to what you are used to in Web Forms user controls. The above logic can be placed in a Partial so that you can reuse it later, you just need to place it in /Views/Shared/DisplayTemplates or EditorTemplates.

How to abstract common snippets of markup with ASP.NET MVC

I have a lot of content-heavy views in my ASP.NET MVC 2 site. These contain several re-occurring HTML patterns. When using ASP.NET Webforms, a class derived from WebControl could encapsulate these patterns. I'd like some pointers on the correct approach for this problem with MVC.
Detailed Explanation
Patterns not unlike the following HTML markup keep occurring throughout these views. The markup renders into an isolated a box of content:
<div class="top container">
<div class="header">
<p>The title</p>
<em>(and a small note)</em>
</div>
<div class="simpleBox rounded">
<p>This is content.</p>
<p><strong>Some more content</strong></p>
</div>
</div>
This is a trivial example, but there are more complex recurring patterns. In ASP.NET Webforms I would have abstracted such code into a WebControl (let's say I'd have named it BoxControl), being included on a page like this:
<foo:BoxControl runat="server">
<Header>The title</Header>
<Note>(and a small note)</Note>
<Content>
<p>This is content.</p>
<p><strong>Some more content</strong></p>
</Content>
</foo:BoxControl>
This abstraction makes it easy to adapt the way the box is constructed throughout the site, by just altering the BoxControl source. It also keeps the static HTML content neatly together in the View Page, even when combining several BoxControls on a page. Another benefit is that the HTML used as content is recognized by the IDE, thus providing syntax highlighting/checking.
To my understanding, WebControls are discouraged in ASP.NET MVC. Instead of a WebControl, I could accomplish the abstraction with a partial view. Such a view would then be included in a View Page as follows:
<%= Html.Partial("BoxControl", new {
Header="The Title",
Note="(and a small note)",
Content="<p>This is content.</p><p><strong>Some more content</strong></p>"});
%>
This is not ideal, since the 'Content' parameter could become very long, and the IDE does not treat it as HTML when passed this way.
Considered Solutions
Strongly-Typed ViewModels can be passed to the Html.Partial call instead of the lengthy parameters shown above. But then I'd have to pull the content in from somewhere else (a CMS, or Resource file). I'd like for the content to be contained in the View Page.
I have also considered the solution proposed by Jeffrey Palermo, but that would mean lots of extra files scattered around the project. I'd like the textual content of any view to be restricted to one file only.
Should I not want to abstract the markup away? Or is there maybe an approach, suitable for MVC, that I am overlooking here? What is the drawback to 'sinning' by using a WebControl?
There is a solution to this problem, although the way to get there is a little more clutsy than other frameworks like Ruby on Rails.
I've used this method to create markup for Twitter Bootstrap's control group syntax which looks like this:
<div class="control-group">
<label class="control-label">[Label text here]</label>
<div class="controls">
[Arbitrary markup here]
</div>
</div>
Here's how:
1) Create a model for the common markup snippet. The model should write markup on construction and again on dispose:
using System;
using System.Web.Mvc;
namespace My.Name.Space
{
public class ControlGroup : IDisposable
{
private readonly ViewContext m_viewContext;
private readonly TagBuilder m_controlGroup;
private readonly TagBuilder m_controlsDiv;
public ControlGroup(ViewContext viewContext, string labelText)
{
m_viewContext = viewContext;
/*
* <div class="control-group">
* <label class="control-label">Label</label>
* <div class="controls">
* input(s)
* </div>
* </div>
*/
m_controlGroup = new TagBuilder("div");
m_controlGroup.AddCssClass("control-group");
m_viewContext.Writer.Write(m_controlGroup.ToString(TagRenderMode.StartTag));
if (labelText != null)
{
var label = new TagBuilder("label");
label.AddCssClass("control-label");
label.InnerHtml = labelText;
m_viewContext.Writer.Write(label.ToString());
}
m_controlsDiv = new TagBuilder("div");
m_controlsDiv.AddCssClass("controls");
m_viewContext.Writer.Write(m_controlsDiv.ToString(TagRenderMode.StartTag));
}
public void Dispose()
{
m_viewContext.Writer.Write(m_controlsDiv.ToString(TagRenderMode.EndTag));
m_viewContext.Writer.Write(m_controlGroup.ToString(TagRenderMode.EndTag));
}
}
}
2) Create a nifty Html helper
using System.Collections.Generic;
using System.Linq;
using System.Web.Mvc;
using My.Name.Space
namespace Some.Name.Space
{
public static class FormsHelper
{
public static ControlGroup ControlGroup(this HtmlHelper helper, string labelText)
{
return new ControlGroup(helper.ViewContext, labelText);
}
}
}
3) Use it in the view (Razor code)
#using (Html.ControlGroup("My label"))
{
<input type="text" />
<p>Arbitrary markup</p>
<input type="text" name="moreInputFields" />
}
This is also the way MVC framework renders a form with the Html.BeginForm method
Well you wouldn't render the partial like that, pass it a strongly-typed ViewModel, like this:
<%= Html.RenderPartial("BoxControl", contentModel) %>
contentModel is the ViewModel (just a POCO-like storage mechanism for your views), which the strongly typed partial view would bind to.
So you can do this in your partial view:
<h1><%: Model.Header %></h1>
<p><%: Model.Content %></p>
etc etc
After considering the answers and running an experiment, I'm inclined to adhere to the pure MVC approach and duplicate some presentation code throughout View Pages. I'd like to elaborate on the rationale for that decision.
Partial View
When using a Partial View, The content for the box needs to be passed as a View Model, making the View Page less readable versus declaring the content HTML on the spot. Remember that the content does not come from a CMS, so that would mean filling the View Model with HTML in a controller or setting a local variable in the View Page. Both of these methods fail to take advantage of IDE features for dealing with HTML.
WebControl
On the other hand, a WebControl-derived class is discouraged and also turns out to have some practical issues. The main issue that the declarative, hierarchical style of traditional ASP.NET .aspx pages just does not fit the procedural style of MVC.NET View Pages. You have to choose for either a full blown traditional approach, or go completely MVC.
To illustrate this, the most prominent issue in my experimental implementation was one of variable scope: when iterating a list of products, the MVC-way is to use a foreach loop, but that introduces a local variable which will not be available in the scope of the WebControl. The traditional ASP.NET approach would be to use a Repeater instead of the foreach. It seems to be a slippery slope to use any traditional ASP.NET controls at all, because I suspect you'll soon find yourself needing to combine more and more of them to get the job done.
Plain HTML
Forgoing the abstraction at all, you are left with duplicate presentation code. This is against DRY, but produces very readable code.
It doesnt look like webforms has that much less html to me, it seems more like a lateral move. Using a partial in MVC can make it cleaner but the html markup you needed will still be there, in one place or another. If its mostly the extra html that bothers you, you might take a look at the NHaml view engine or check out haml
the haml website.
I'm by no means a Haml expert but the html does look a lot cleaner...
.top container
.header
%p
The title
%em
(and a small note)
.simpleBox rounded
%p
This is content.
%p
Some more content

Resources