Does ASP.NET MVC have a way to generate unique ClientIDs? - asp.net-mvc

Is there an out-of-the-box way to create unique "id" tags in ASP.NET MVC?
(Similar to the dreaded but sometimes useful ClientIDs in WebForms?)
This would be useful when rendering a partial view many times on a page.
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %>
<%-- Example Partial View --%>
<div id="<%=GenerateAUniqueIDHere()%>">
Content
</div>
<script type="text/javascript">
$("#<%=GenerateAUniqueIDHere%>").hide().fadein().css("font-size", "500%");
</script>
If not, it is easy enough to roll my own.
Thanks much,
Jon

Use a GUID?
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %>
<%-- Example Partial View --%>
<%
string ID = Guid.NewGuid().ToString();
%>
<div id="<%=ID%>">
Content
</div>
<script type="text/javascript">
$("#<%=ID%>").hide().fadein().css("font-size", "500%");
</script>
I would also pass the GUID in as a viewdata object during the renderpartial method call to keep your ViewUserControl tidy

As far as I know, MVC does not have this. I have used ASP.NET MVC for over a year, and never had to use this. When I have several controls, with the same name, I almost always want to be able to query these controls later, so I need to know the Id, and use a counter, so I know the names. If you don't need to know their ids, why even give them a id?

From MSDN:
The chance that the value of the new Guid will be all zeros or equal
to any other Guid is very low.
So there seems to be no way to make sure that an ID is absolutely unique. Using Guid seems to be a better solution than generating a random number on your own.
You could try generating the ID from the data or its description.

Related

Can I duplicate this classic ASP pattern in ASP.NET MVC?

Virtually every ASP app I've written (hundreds) follows the exact same pattern. A "single page app" with a header and footer, and a dynamically updated content area that changes depending upon what going on/in the url. Something like the following (very simplified, but demonstrates the principle):
<% select case lcase(request("action") %>
<% case "home" %>
<div class='class_home'>
Home Screen HTML/Script/ASP
</div>
<% case "enroll" %>
<div class='class_enroll'>
Enroll Screen HTML/Script/ASP
</div>
<% case "checkout" %>
<div class='class_checkout'>
<!-- if it's gonna be lengthy, will often do this instead: -->
<!--
#include file=checkout.inc.asp
-->
</div>
<% end select %>
This pattern may even be nested several layers deep with additional request("subaction") subarea/subforms involved. Every form submits to itself ([form action="" method=POST]), and asp script at the top catches the form and processes it, then continues.
So, the question is, is this pattern still done inside MVC? Or do I have to duplicate the common areas over and over again in each separate page that I create?
Is this even a good idea to WANT to do this? Or is there a better way to accomplish the same goal of a "single page app"?
Thanks!
Even in classic ASP you could achieve this without all the craziness that is going on in that select statement.
In MVC, you use partials and layout pages to avoid repeating code. Here is a nice rundown http://weblogs.asp.net/scottgu/archive/2010/12/30/asp-net-mvc-3-layouts-and-sections-with-razor.aspx
This is still the same in MVC. If you are using Razor, look for the _Layout.cshtml file in /Views/Shared. If you are using the old ASP.Net engine, it will in the same location but called MasterPage.
Aditionally, there is a file called _ViewStart.cshtml. This is invoked automatically by the framework, and this is what points to the _Layout.cshtml file.
I'll add a little more to the suggestions of using _ViewStart.cshtml and _Layout.cshtml. Make sure to use strongly typed view for all your Views and have each View Model extend from a base View Model class that has all the "common" data such as the menu state, logged in status, etc.
You would just do this using ineritance:
public class MyBaseViewModel
{
public string UserName { get; set; }
//other properties
}
public class MySampleViewModel : MyBaseViewModel
{
//additional properties for this View only
}

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

Showing an Image on a masterpage based on the logged in user

So I have a masterpage that has an image whos source is a controller action returning a filestream.
The image will be different for each logged in user.
right now in my masterpage view i have this code wich relies on viewdata.
<img id="dealerlogo" src='/Files/DealerImage/GetDealerLogo/<%=Html.Encode(ViewData["dealerid"]) %>' alt="" />
obviously the problem with this is that I will need to provide the viewdata containing the ID
on everycontroller action returning a view that uses this master page, which is pretty much all of them.
Is there a better way to do this? Like is there a way to get the username right in the view?
Thanks!
You can easily encapsulate this logic in a [ChildActionOnly] Action that returns a partial view and then use new MVC 2 approach
<% Html.RenderAction("GetUserPhoto", "User"); %>
to have it everywhere in your view pages without passing ViewData in all actions.
Here's the solution:
[ChildActionOnly]
public ActionResult GetUserPhoto()
{
ViewData["UserId"] = Page.User.Identity;
return PartialView();
}
And in your view you use the same logic you used to show user image. Also you can directly send a FileResult to partial view to render image for you. In this approach you don't need to repeat ViewData["XXXX"] in all views and you need just to Render the new Partial View in your main Views.
You can use the Page.User.Identity.Name just like in the default logonusercontrol.aspx that is created for you when you create a new asp.net MVC site.
Welcome <b><%= Html.Encode(Page.User.Identity.Name ) %></b>
So for you, you would want something like this:
<img id="dealerlogo" src='/Files/DealerImage/GetDealerLogo/<%=Html.Encode(Page.User.Identity.Name) %>' alt="" />
I assume your GetDealerLogo action method has a parameter of dealerid. It's better to write something like:
<img src="<%= Url.Action("GetDealerLogo", "DealerImage", new { dealerid = ViewData["dealerid"] }) %>" />
Nothing wrong with passing it in via ViewData. You might also consider a strongly-typed View or your own ViewPage base class which exposes a DealerId property.
To make it even cleaner, I really like T4MVC. It would allow you to write:
<img src="<%= Url.Action(MVC.DealerImage.GetDealerLogo(dealerid)) %>" />
And, even further, you might create an Html helper, so you can write:
<%= Html.DealerLogo(dealerid) %>

Working with partial views

I'm trying to create a page that contains a grid and searching. The issue is that I want to have a partial view for the grid and one for the searching.
If doing a search, this should render the grid partial view with the new information.
At the moment I need information, such as what column I'm sorting by and so on, from the grid (currently stored in viewdata), in order to do the search as I want to keep those settings. This information is only available in the grid partial though.
What's the best approach of this to make it neat and nice in the code, but not a mess to work with?
Where can I store information that I need in the other partial view?
Partial View 1;
<table>
<%= Html.CreateGrid(Model, "Grid", "Grid", (int)ViewData["SortColumn"], (bool)ViewData["SortedASC"])%>
</table>
Partial View 2;
<div class="searchControl">
<input type="text" class="SearchBox" href="<%= Url.Action("Grid", "Grid", new {page = 1, columnToSortBy=/* would like to access viewdata from partial view 1 here. */, sortASC = /* would like to access viewdata from partial view 1 here. */ } ) %>" />
<input type="submit" value="Search" class="SearchButton" />
</div>
I know I might take the completely wrong approach on this, so feel free to point me in the right one!
Thanks!
ViewData is a good place to store data that is accessed in Views and Partials.
Even better if you use strongly typed views. Then you could access the data for sorting an filtering via a typed model.
I would have the model-classes implement an interface IGridFeatures that has properties for SortedASC, SortColumn, Page.
Its often a good idea to have these optional properties not in the route but in a querystring.
I think you'll be better of controlling your link through javascript, since all you really want is to control the UI.

asp.net mvc partialview

It seems that my understanding of the use of partial views is not quite right.
I am trying to add a partial view which builds from a database and use the partial view within a master page.
Using the NerdDinner project (wich is great for mvc) I have added the following:
Views\Shared\dinners.ascx" :
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<IEnumerable<NerdDinner.Models.Dinner>>" %>
<ul>
<% foreach (var dinner in Model) { %>
<li>
<%= Html.Encode(dinner.Title) %>
</li>
<% } %>
</ul>
Within "Views\Shared\Site.Master" :
<%# Master Language="C#" Inherits="System.Web.Mvc.ViewMasterPage" %>
...
<% Html.RenderPartial("dinners"); %>
The above will only currently function on the pages where the dinners object is currently availabe as a list (eg site/Dinners). Is there a way to do this in an elegant and mvc way or is this something that requires a clever compromise?
Why do you have it in your master? I would add another place holder to your master and then add it where it makes sense. I feel that master pages should be as agnostic to specifics. If you want it be on all dinner pages, just make nested master of the original one.
This is, IMO, one of the biggest limitations of ASP.NET MVC - managing shared data across multiple views (next to rendering partial ascx views to strings!). If you google or search on stackoverflow for something like 'managing shared view data asp.net mvc' you'll get a ton of results with various options, none of which are really perfect. The MVC team at Microsoft have acknowledged this as a problem and will hopefully include a standard solution in a future release.
Depending on how you manage data access, the easiest way may be to create a base Controller class and retrieve the data you need for the partial either inside the constructor or inside OnActionExecuting().
The option that I have chosen is to use the Html.RenderAction() helper method inside the MvcContrib project. It basically enables you to call an action method from your view and render the response. This isn't great because it requires your view to have yet more knowledge about controllers, but it gives an easy short-term solution that doesn't require hooking up any extra code on your part.
This tutorial on stephan Walther's site deal with this issue. If you use an abstract base class where the dinners object is populated and inherit from that, it will always be available, but you'll have to be aware that it's there always even when you don't need it ;).
One method I use is to create a helper method and use it in your Master Page.
public static void RenderDinners(this HtmlHelper helper)
{
helper.RenderAction<DinnersController>(c => c.Dinners());
}
<%# Master Language="C#" Inherits="System.Web.Mvc.ViewMasterPage" %>
...
<% Html.RenderDinners(); %>
As you can see the helper calls the Dinners Action method on the DinnersController.
public ActionResult Dinners()
{
...get dinners and put in the View
return Dinners(view);
}
partialview seems to me to be inherently flawed. It creates module coupling and breaks cohesion intentionally by definition.
I agree with Daniel, even if your control does not shows on every page, it shows on some of them, you should create your master as template only

Resources