Create reuseable UI componets with MVC - asp.net-mvc

I am in the phase of building a rendering framework for rendering my models in different formats.
My idea is the following:
public class ResidenceRendererShort : IRender<Residence> {
public string Format() {
return "short";
}
public string Render(Residence content) {
return content.Name; // Could return a whole lot of HTML
}
}
I can have multiple of those with different formats, and they are all injected using Ninject DI into my RenderingService, where I got methods for finding the correct render, using methods like e.g. FindRendererFor(Type type, string format)
Now my question is, how can I create a tag in razor which will use the rendering service and applying the correct render? I have been looking into HtmlHelpers, but they are static methods and I can not inject my RenderingService into this.
I thought I could create something like:
#Model my.namespace.Residence
#Html.RenderObject(Model, "short");
Am I missing something or someone got an idea on how to accomplish this?

You're killing yourself. Just use Display/Editor Templates. If you have a view in ~/Views/Shared/DisplayTemplates or ~/Views/Shared/EditorTemplates named after your class, Residence.cshtml in this case, then Razor will use this view to render your class whenever it's passed to Html.DisplayFor or Html.EditorFor.

Related

In ASP.Net MVC, how can you manage dependencies between views and layout pages?

We're working with ASP.Net MVC and Google Publisher Tags (GPT).
GPT requires that you create javascript "slots" in the <head> and some html+script that goes into the <body>. The key dependency here is that in both places is an id that must match. So the head will contain some Javascript that includes something like:
<head>
<script>
...
DefineSlot('div-gpt-ad-123456789-0', 'foo', 'bar')
...
</script></head>
and
<body>
...
<div id='div-gpt-ad-123456789-0'>
<script>
...
Display('div-gpt-ad-123456789-0')
...
</script>
</div>
...
How can we manage the dependencies between these 2 pieces code? The critical piece is that the id of both parts must match.
We want to use MVC to create these pieces of code dynamically. So in any view, partial or layout, I will be able to add a helper call that might look like:
#Html.CreateAd(size, "foo", "bar")
#Html.CreateAd can be called anywhere in a view, partial view, layout, or nested layout.
How do you use ASP.Net MVC to program the code that goes into <head>?
Any suggestions are appreciated. I'm just looking for direction, not a full blown solution.
Many Thanks.
You have a few different ways to do this.
You can add the id's to the ViewData or a base viewmodel.
Then OnActionExecuting or OnActionExecuted in a base controller or via actionfilters, you can add your data to whichever place you prefer to. If you need examples for this, please leave a comment on this answer.
Then, your helpers (one for each section) you can read from one of the 2 sources you decided on. I have gone both routes. If all of your pages are going to have an ad, then I would lean towards the base ViewModel. If it's more of a rare occurrence ViewData would be more appropriate.
To access the viewdata within an htmlHelper Extension method:
public static class HtmlExtension
{
public static MvcHtmlString RenderAdHead(this HtmlHelper h)
{
h.ViewContext.ViewData.Model // a test and cast here
h.ViewContext.ViewData["AdIdentifier"] // test for null and cast here
string tags = String.Empty;
//build up string to resemble your script/html tags using either of the
//sources above, so long as either source is not empty.
return new HtmlMvcString(tags);
}
}
And some code for a filter:
public class AdvertisingFilter : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
List<String> idList = null; // list of string might not be adequate. depends on your implementation
//read advertising ids from your datastore or wherever you have.
filterContext.Controller.ViewData["advertisingFilter"] = idList;
}
}
The basecontroller is pretty much the same, instead you have the controllercontext directly. You just have to be sure all your controllers inherit from them.

ASP MVC Razor view extension methods, how to create 'global' view methods?

I am using Razor view with asp mvc preview 3
I am trying to create some methods which I would like available directly in the views. These are not really Html helper methods so I don't think extending HtmlHelper makes sense?
my goal, be able to call methods in the view i.e.
#HelloWorld(); vs #Html.HelloWorld()
I can get Html.HelloWorld to work by creating an extension method on HtmlHelper
public static class HtmlExtensions
{
public static string HelloWorld(this HtmlHelper helper)
{
return "Hello";
}
}
I would like to do the same thing but for the view; my problem - what type of object is the view?
Note: I was able to get this to work by defining the methods in the .cshtml page
#functions
{
public string HelloWorld()
{
return "Hello";
}
}
#HelloWorld() #* now this works *#
then I tried to put this code my _viewstart.cshtml file thinking it would be available in all views but it was not
if I knew which type the view was I think it could be easily extended, any help appreciated
As remarked by others, Razor Views all ultimately inherit from WebViewPage:
public abstract class WebViewPage<TModel> : WebViewPage
You can therefore simply write your extension methods for WebViewPage without creating a new base class or changing the config files, which has been suggested by other answers. For example:
public static class WebViewPageExtensions
{
public static string HollowWorld(this WebViewPage wvp)
{
return "Memento mori";
}
}
Add a using statement for that namespace to your View and then:
<p>#this.HollowWorld()</p>
it turns out, the asp runtime is going to define the Execute method at runtime, so the custom view base class must also be abstract
using System;
using System.Web.Mvc;
namespace MyMvcWebApp.Extensions
{
public abstract class ViewBase<TModel>
: System.Web.Mvc.WebViewPage<TModel> where TModel : class
{
// now this will be available in any view #HelloWorld()
public string HelloWorld()
{
return "Hello from the ViewBase class";
}
}
}
this should work with strongly typed views, it looks like with razor all views are strongly typed, when you do not define the type 'dynamic' is used and that is the strong type
also, as Clicktricity stated you then update the web.config (the one under the Views directory)
<pages pageBaseType="MyMvcWebApp.Extensions.ViewBase">
The default base class for Razor views is specified in the Web.config located in the views directory. Usually it is:
<pages pageBaseType="System.Web.Mvc.WebViewPage">
I've not tried it, but I would suggest inheriting from this base class and adding your own functionality, then adjust the web.config accordingly.
The best way to call a method with arguments using razor engine is to use helpers.
Example: let you have a helper #MakeNote(string content)
Then in cshtml page you just need to call #MakeNote("Hi") and you should be fine.
I was going crazy when I was having problem then google sent me to this page but it did not help. I was trying to load content in html <select> with L2E using razor.
The secret is to create a helper in app_code then use with cshtml.

Reusable Content Box Data In ASP.NET MVC?

If I create a PartialView for a box that holds a header, image and content what is the best way to store the content without using a database?
Example: TurboTax
I doubt the content for the boxes in the sidebar are stored in a database but to make reusable code it would be beneficial to create the structure in a PartialView and populate the content areas. I can create a PartialView and pass a Model from the parent Controller to the PartialView but then I would be stuck copying and pasting that same content if I wanted to use the same box on another page.
For fixed content you might want to think about using XML+XSLT or even HTML snippets in the file system and simply rendering them. An HtmlHelper method might make more sense for this than a partial view - Html.RenderXml() or Html.Include(). The only real difference between these and partial views is that the view engine isn't invoked since there aren't any substitutions. I do this sort of thing with my privacy policy and terms and conditions. I'd certainly consider keeping these cached.
If these really are templates and you are just substituting content, then I think the partial view works well and I would consider putting the data in a database, again, maybe using caching if I found that performance suffered. You could use this in combination with the former -- say keep your images/xml in the file system and a pointer to them in the database so you know which ones to pick in the partial.
Passing data to partial view that is used in many places can be done in many ways:
Create base model class for all your models. In base class define PartialModel property which will be holding model for partial view (there may be many of them if use have many partial views). Now you can populate the PartialModel property in controller action, but to make code more reusable you can create your own Action Filter which will insert the partial view data just after the action method is executed (but before the model is passed to the view)
public class PartialViewModelAttribute : ActionFilterAttribute
{
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
BaseViewModel model;
if (filterContext.Controller.ViewData.Model == null)
{
model = new BaseViewModel();
filterContext.Controller.ViewData.Model = model;
}
else
{
model = filterContext.Controller.ViewData.Model as BaseViewModel;
}
model.PartialModel = new PartialModel(...) // Partial model initialization
base.OnActionExecuted(filterContext);
}
}
Then you can use it like:
[PartialViewModel]
public ActionResult Index()
{
//...
}
Another option: you can create BaseController class for all your controllers and create PartialModel on base controller initialization. Then PartialModel can be stored in ViewData[] dictionary. Because using ViewData dictionary in views is bad, create extension method on HtmlHelper like:
public static PartialModel GetPartialModel(this HtmlHelper helper)
{
return helper.viewContext.ViewData["PartialModel"] as PartialModel
}
So you could obtaint the model this way:
<% Html.RenderPartial("MyPartial", Html.GetPartialModel()); %>

ASP.NET StrongTyped Controller-Action View<TView,TModel>(TModel Data)

I'm using Asp.net MVC 1 and I would really like my controller actions to use StronglyTyped View(data) calls that enforce type checking at compile time and still let me use aspx pages under the default view engine. The ViewPages I call are strongly typed, but errors in the action's call to View(data) can't be caught at compile time because the built in Controller View(data) method isn't strongly typed and doesn't even check to see if the page exists at compile time.
I've implemented a partial solution (code below) using this post but (1) I can't get the generic View function to recognize the Type of strong view pages unless I create a code behind for the strongly typed view, and (2) Intellisense and refactoring don't work properly with this method which makes me doubt the reliability of the method I'm using.
Question:
Is there a better way to get type enforcement when calling Views from actions?
Alternative: Is there an alternative method where my action method can create an instance of a viewpage, set some properties directly and then render out its HTML to the action response?
Code:
Here's the base Class all my Controllers Inherit from to achieve what I have so far:
public class StrongController : Controller
{
protected ActionResult View<TView, TModel>(TModel model)
where TView : ViewPage<TModel>
where TModel : class
{
return View(typeof(TView).Name, model);
}
}
And here's an example Controller in use:
namespace ExampleMVCApp.Controllers
{
public class HomeController : StrongController
{
public ActionResult Index()
{
return View<ExampleMVCApp.Views.Home.Index, ExampleData>(new ExampleData());
}
}
}
ViewPage Code Behind Required for Type Recognition... Aspx header didn't work
namespace ExampleMVCApp.Views.Home
{
public class Issue : System.Web.Mvc.ViewPage<ExampleData>
{
}
}
I think you should give the T4MVC helpers a spin (one of the original announcements here). This would at least enable you to get rid of the code you already have, since these templates generate the code based on the Views you already have and you employ these "fake" method calls to address your views.
For having your calls to View to be strongly typed for the specific model declared by your view, I am not exactly sure if these helpers help you with that (though I suspect they do). However, if they don't you can still hack the T4MVC code to do so yourself or get in touch with the original author, David Ebbo, to suggest the feature for addition.

Does ASP.NET MVC Has Anything Equivalent To WPF's DataTemplate Feature?

In my ASP.NET MVC project, I have a polymorphic collection that I wish to render - say, an IEnumerable<ISomething> where the individual items may be a mix of different implementations of ISomething.
I'd like that list rendered, where each concrete type renders according to its own template (perhaps a strongly typed ViewUserControl).
In WPF, I'd be able to specify DataTemplates that would automatically bind concrete types to specific templates. Can I do something similar in ASP.NET MVC?
Obviously, I can iterate through the list and attempt a cast using the is keyword and then use a lot of if statements to render the desired control, but I was hoping for something more elegant (like WPF).
I ended up with developing a solution myself - I have described it in DataTemplating In ASP.NET MVC.
I'm not sure if I get you fully, but, why don't you implement a method to your ISomething interface like render for example, which by contract will be implemented to all of your other concrete entities, and then iterate through each item in the polymorphic collection and call it.
I had a similar issue and never found a "simple" answer. I had the benefit of knowing that all items in the list would render the same way so I created a decorator for ISomething, converted the list to IEnumerable using some code from the Umbrella project (http://umbrella.codeplex.com), and then extracted out the relevant pieces. Kinda like the following:
public interface ISomethingDecorator
{
string Description { get; }
string[] Actions { get; }
}
public class BigSomethingDecorator : ISomethingDecorator { /* ... */ }
public class SmallSomethingDecorator : ISomethingDecorator { /* ... */ }
Then, as I said, I use the Umbrella project to convert from ISomething to ISomethingDecorator and returned IEnumerable to the View.
Don't know if it'll help you with what you're trying to do -- especially being a month late -- but I thought I'd let you know how I handled it. If you're displaying completely different formats, it probably won't do the trick but maybe it can get you a starting point.

Resources