Using delegates to render html blocks in asp.net - c#

I thought of a neat hack this morning, probably not original, but I haven't seen it before. I don't consider it good practice, but can help when you need to render a block of code repeatedly around your page, and don't want to touch other code or create other files (partial views or components).
In your .aspx file create an anonymous delegate like so:
<%
Action<DataType> renderMe = data => {
%> Some html text That can contain quotes, etc.
And other <%= data.something %> stuff...
<%
};
%>
Then you can simply use it anywhere you want: (myvar1 and myvar2 are of type DataType)
This is some html and I want the block here: <% renderMe(myvar1); %> ...
or maybe here <% renderMe(myvar2); %>
I know it's not a great idea, but can anyone see any problems with doing this?

This ain't bad per se. Quite similar to Spark's macros (that looks better imho). Rashud`s script manager (for advanced js initialization) uses the same technique. MvcContrib's grid renderer does the same too.
Thing is - niche when this is suitable is really narrow. Only when desired snippet of html should be passed to server side or when you want to re-use it twice or more in context of one specific view but don't want to create separate partial view.

Related

Evaluate c# code as string in a aspx file

I have this problem: From a database, held up a string, which contains HTML mixed with C# code. I wish I could run correctly both codes on my page .aspx.
e.g.
in my .aspx:
<div><%= Model.repo.getCode() %></div>
and the getCode() method give me this:
<div id="secondDiv"><p><%= Model.Person.Name %></p></div>
so I want the final html file look like:
<div><div id="secondDiv"><p>Jhon</p></div></div>
any suggestion?
There may be direct way to bind such value,
But if you could store String.Formatable into database then it would be easy to bind the data needed.
Using String.Format you achieve like,
returned string from Model.repo.getCode() (see curly braces)
"<div id="secondDiv"><p>{0}</p></div>";
And in ASP code,
<div><%= string.format(Model.repo.getCode(),Model.Person.Name) %></div>
Take a look at this project as it helped me with a similar problem: https://github.com/formosatek/dotliquid Basically you can bind whatever objects to a template and that template can call properties of you objects and even use conditional logic and loops.

MVC3 Layout Page, View, RenderPartial and getting script files into the Header (from the partial view)

So I have a Layout page
<head>
#RenderSection("HeaderLast", required: false)
</head>
A view
#section HeaderLast
{
<script src="#Url.Content("~/Scripts/knockout-1.2.0.js")"
type="text/javascript"></script>
}
<div id="profile-tab">
#{ Html.RenderPartial("_userProfile"); }
</div>
And a Partial view
#section HeaderLast
{
<script type="text/javascript">
alert('test');
</script>
}
<div......
I figured it couldn't be that simple. Is there a proper way to do this out of box or will this always require some kind of mediator and passing stuff around ViewData to manually make the content bubble up to the layout page?
Bounty started: The bounty will be rewarded to the best solution provided for this short coming. Should no answers be provided I will award it to #SLaks for originally answering this question.
You cannot define sections in partial views.
Instead, you can put the Javascript in ViewBag, then emit any Javascript found in ViewBag in the layout page.
#JasCav: If a partial needs its own CSS, it has no good way to get it rendered.
If that's the reason for its use, it could very well be by design.
You don't want to have a separate CSS file x partial/helper. Remember, each separate CSS file means a separate request to get it from the server, thus an additional round-trip that affects time to render your page.
Also you don't want to emit direct CSS to the HTML from the partial/helper. Instead you want it to have appropriate hooks you can use to define all the look in your site's CSS file.
You can use the same hooks you have available for CSS to activate custom JavaScript behaviors for the elements involved When JavaScript is enabled.
Finally it may be the case what you need is not a Partial View, but an extra Layout you use for some pages. With that approach you would have:
A master Layout that gets set automatically on _ViewStart like you probably has now. This defines the sections like in your sample.
A children Layout page. Here you have both the extra html, css, js you need to have for these views. This uses both #RenderBody() and #section SomeSection { } to structure your common extra layout.
Some views that point to the children layout, and others that use the default master layout.
How to get extra data to the children Layout is out of the scope of the question, but you have several options. Like having a common base for your entities; using ViewBag or calling Html.RenderAction to get that shared logic related to shared dynamic elements in the layout.
It looks like there was a similar question on SO - How to render JavaScript into MasterLayout section from partial view?.
Unfortunately, there is no possibility of declaring sections inside Partial Views. That is because RenderPartial ends up rendering totally separate view page. There is a workaround to this, though a bit ugly. But it can look better if using strongly-typed model instead of ViewData.
Basically, you need to keep track of the reference to the view which called RenderPartial and use the DefineSection method on the object passed to push data to that view.
UPDATE: There is also a blog post about dealing with RenderSection you may find useful.
Here is another approach using helper methods and templated delegate
http://blogs.msdn.com/b/marcinon/archive/2010/12/15/razor-nested-layouts-and-redefined-sections.aspx
As a follow up to my question, the JavaScript/CSS combiner/minifier tool Cassette supports this functionality to allow you to compartmentalize your JavaScript and other assets that are required for partials.
I purchased a site license and use this in all of my MVC applications now.

Better way to architect the way I'm doing user permissions on view fields

So currently in our code, we have a bunch of classes around accessing roles and permissions for specific users logged into the web application. This is built fine, and works well. However, a certain requirement that has come up now is not only the ability to restrict certain pages (whole views) to certain permissions, but also specific fields on those views. So you might have the ability to see a view, but you might only have a permission that lets you see certain fields on that page.
We generate most of our fields using HTML Helpers with in ASP.NET/C#. For example here is one that we need to block/show depending on a permission:
<%= Html.CheckBoxFor(m => m.Current, new { #class = "economicTextBox", propertyName = "Current", onchange = "UseCurrent();UpdateField(this);" })%>
My first thought was just to play the check for permissions in similar embedded C# code right before that and put the HTML Helper in an "if" statement, so it would only render if the user had the permission. I second guessed myself when I saw the front end getting very bloated with all of these statements in my mind, and thought there might be a better way to do it.
Can any of you guys recommend anything? Thanks.
Have the user permissions as a separate module of the model. Create partial views which contain the blocks of code that you want to show/hide. Pass the module to the partial view, and have the view hold the logic for what to show.
This should mean you can create one block of logic in your partial view, with the decision being some basic ifs or switches and the mark up for displaying it more distinct and manageable.
So for example, let's say you make a permissions model that all your page models inherit, thus ensuring you can pass around the same permissions to all pages. You can then call a view and pass it the model like this:
<%Html.RenderPartial("Widgets/NavigationUserControl"); %>
This will hold the mark up and logic for user navigation. Inside this partial view you'd have it inherit your permissions object:
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<WebApp.Web.Models.Permissions>" %>
And then you can do your more basic logic in the markup, or have a html helper do the grunt work and return some enums/properties for more basic code to do decision on. For example;
<%
if (Model.User.IsAdmin)
{
%>
...html...
<%
}
else
{
%>
...html...
<%
}
%>
My first thought was just to play the
check for permissions in similar
embedded C# code right before that and
put the HTML Helper in an "if"
statement, so it would only render if
the user had the permission. I second
guessed myself when I saw the front
end getting very bloated with all of
these statements in my mind, and
thought there might be a better way to
do it.
Put the if inside your Html Helper and pass to the helper function what data you need to perform the conditional check.
View:
<body>
<p>text</p>
<%: Html.PermissionControlledFieldOne(someUserData, otherData) %>
Helper:
public HtmlString PermissionControlledFieldOne(....) {
if (performSecurityCheck(someUserData, otherData)) {
// .. render partial or build html or whatever you need
}

Struggling with ASP.NET MVC auto-scaffolder template

I'm trying to write an auto-scaffolder template for Index views. I'd like to be able to pass in a collection of models or view-models (e.g., IQueryable<MyViewModel>) and get back an HTML table that uses the DisplayName attribute for the headings (th elements) and Html.Display(propertyName) for the cells (td elements). Each row should correspond to one item in the collection.
Here's what I have so far:
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %>
<%
var items = (IQueryable<TestProj.ViewModels.TestViewModel>)Model;
// How do I make this generic?
var properties = items.First().GetMetadata().Properties
.Where(pm => pm.ShowForDisplay && !ViewData.TemplateInfo.Visited(pm));
%>
<table>
<tr>
<%
foreach(var property in properties)
{
%>
<th>
<%= property.DisplayName %>
</th>
<%
}
%>
</tr>
<%
foreach(var item in items)
{
HtmlHelper itemHtml = ????;
// What should I put in place of "????"?
%>
<tr>
<%
foreach(var property in properties)
{
%>
<td>
<%= itemHtml.Display(property.DisplayName) %>
</td>
<%
}
%>
</tr>
<%
}
%>
</table>
Two problems with this:
I'd like it to be generic. So, I'd like to replace var items = (IQueryable<TestProj.ViewModels.TestViewModel>)Model; with var items = (IQueryable<T>)Model; or something to that effect.
A property Html is automatically created for me when the view is created, but this HtmlHelper applies to the whole collection. I need to somehow create an itemHtml object that applies just to the current item in the foreach loop. I'm not sure how to do this, however, because the constructors for HtmlHelper don't take a Model object.
How do I solve these two problems?
Phil Haack to the rescue!
http://haacked.com/archive/2010/05/05/asp-net-mvc-tabular-display-template.aspx
for the geniric IQueryable part, why don't you simply cast it to an IQueryable...
var items = (IQueryable<TestProj.ViewModels.TestViewModel>)Model;
and What is your template for, isn't it almost the same as the default list template when you add a view... If you want to customize it, i think you'd better look for the List.tt template. You could modify that to use Html.Display instead of Html.Encode.
Or you could try using
Html.DisplayFor(o => property.GetValue(item))
I'm not sure if this would work...
Sadly you can't do:
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<List<T>>" %>
or even
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<T>" %>
ASP.NET MVC implements a Html.DisplayForModel() as Brad Wilson shows on his blog which does scaffolding for you out of the box. (which I'm sure you know about, mentioning for completeness).
If you look at the MVC 2 RTM Source Code source code and in particular the DefaultDisplayTemplates.cs it's all basically contained in an HTMLHelper as suppose to a View. Perhaps instead of fighting with the auto generated class for the view (as much as I love asp.net mvc the default view rendering engine is a total pain) go with an HTMLHelper extension modelled after how ASP.NET MVC RTM implements scaffolding. I guess this would open the possibility for code reuse some more too (though almost everything is internal).
It would be nice if the ASP.NET MVC scaffolding would allow for a custom template to be passed in to change the html if you're not a fan of the default <div class=\"display-field\"> etcetera. To me this is one of the weak spots of ASP.net MVC (the 'hardcoded' error handling/ scaffolding HTML).
If your not a fan of string building your HTML, your extension could call out to more granular partial views (like ScaffoldCaption.ascx) when your scaffolding HTMLHelper extension has gathered enough information to sufficiently type the granular view.
Have you looked into the MVCContrib Grid? Certainly someone has written an extension method to show all properties by default. If not it shouldn't be difficult.
edit2- I guess don't trust my answers on Sunday. I misread the question before and didn't realize Html.Display() was the MVC2 templated input. (I'm doing some radically different input builders at my shop so I didn't pick up on the name. I just thought Display showed the value.) Anyway, there are two options I see here.
1) Pull the MVC2 source or break out Reflector and write your own method that is not extending HtmlHelper.
2) You can use reflection here to build a new HtmlHelper instance but it's not pretty. (I didn't test this but is should work.)
var modelType = Html.GetType().GetGenericParameters()[0];
var itemHtmlType = typeof(HtmlHelper<>).MakeGenericType(modelType);
var itemHtmlCtor = itemHtmlType.GetConstructor(typeof(ViewContext), typeof(IViewDatacontainer), typeof(RouteCollection));
var itemHtml = itemHtmlCtor.Invoke(Html.ViewContext, Html.ViewDataContainer, Html.RouteCollection);
You'll get an object out of this though so you have to use reflection to invoke itemHtml.Dipslay. I'd recommend going a different route.

ASP.NET MVC how to create a model which pulls in data from 2 tables, to display in 1 view

I think this may be a very simple question, but I am only just starting out with .net and c# at all, and only really just finally getting my head around OO stuff.
I have built the NerdDinner application, and I am building on top of it currently with my own project.
What I need to do, in the context of nerd dinner, is display the details of a dinner, but also show all the assoicated RSVP's on the same page.
The url could be the same as normal
dinners/details/2
but the 2 would be used to bring back all rsvp's related to that, and display them in a list on the same page.
I have spend some time trying to do this in the dinnerRepository.cs file, but I'm getting a bit stuck, and not sure the best way to do this.
I would then like to be able to add more rsvp's from that same page (I understand that dosent work in this example, but you seen that I am trying to Add more of rows to tableB, related to a single row in tableA)
Please only answer the first question if you feel the second part should be asked elsewhere.
Thanks so much for any help.
You do not need to change the dinnerRepository.cs file at all. The dinner model already has a RSVP collection. You will just need to modify the Details view as such:
<p>
<% foreach(NerdDinner.Models.RSVP rsvp in this.Model.RSVPs)
{ %>
<%= Html.Encode(rsvp.AttendeeName) %>
<br />
<%} %>
</p>
Your done.
IIRC, the details view is passed an instance of Dinner. Simply render out the RSVPs for that dinner on the details page.
I like using RenderAction for this kind of thing. RenderAction allows you to target a specific subcontroller method to render a subview in the page. Since it is a modular technique, it can be used in more than one page without changes.
RenderAction is part of the Futures assembly: Microsoft.Web.Mvc.

Categories

Resources