#Functions inheritance from parent Razor layout - asp.net-mvc

Is it possible to declare a function or helper in a Razor layout view and have a child Razor view use that function? My structure is :
_layout.cshtml -> index.cshtml
Where index.cshtml uses _layout.cshtml as its layout. This is the default structure.
I want to be able to place common functions/helpers in _layout.cshtml and refer to them in index.cshtml and all other views that use _layout.cshtml. It doesn't work out of the box unless I've missed something.
I know. I should be using compiled AppHelpers or HtmlHelpers for this, but I can foresee a great convenience in being able to tweak select functions/helpers directly in a _layout.cshtml file.
Thanks

No, that's not possible. #functions are visible only inside the current view.
You could use #helper which could be placed inside the App_Code folder and be reused from all views.
For example you define ~/App_Code/MyHelpers.cshtml:
#helper Foo()
{
...
}
and then in some view:
#MyHelpers.Foo()
But I would recommend compiled helpers. They are unit test friendly and view engine agnostic and when tomorrow Microsoft reveal their brand new Blade view engine you will have 0 work to do in order to use them, whereas if you code against Razor specific things like #functions and #helper you will have to port them. Probably not a concern for you but worth mentioning.

Related

What is the correct pattern to use when your PartialView wants to include scripts?

I have a bunch of partial views in my MVC 5 application.
They are used from a bunch of different pages. They have dependencies on certain script files.
Putting scripts in partial views appears to be a big no-no, so I've been putting the scripts in the parent pages.
I haven't been able to figure out how to keep my page rendering logic from being dispersed throughout several source files, and it is annoying to be forgetting references on different pages all the time when I add an existing partial view to them.
It seems like some sort of #include directive would be useful in razor partial views. Has someone bolted this on to MVC in a user contrib?
I like to use forloop htmlhelpers...
In global.asax...
Forloop.HtmlHelpers.ScriptContext.ScriptPathResolver = System.Web.Optimization.Scripts.Render;
In Layout.cshtml...
#Html.RenderScripts()
In Partial View...
#using (var ctx = Html.BeginScriptContext())
{
ctx.AddScriptFile("~/bundles/whatever");
ctx.AddScriptBlock(
#<script>
</script>
);
}
It'd be nice if this package handled css files also...usually a partial view is using some javascript that also requires some css.

Finding Razor partial views in the area EditorTemplates folder

I am starting to use EditorFor helper method to render my razor partial views, but I couldn't get the partials in the Areas folder to work.
Here is the path to the partial:
~\Areas\Products\Views\Shared\EditorTemplates\_Edit.cshtml
The partial is really simple with only one "div" tag to do the testing.
Try to use in my page view (~\Areas\Products\Views\EditPage.cshtml) as
#Html.EditorFor(m => m.ProductEditModel, "_Edit")
Visual studio tells me that "Cannot resolve template '_Edit'".
Now, if I move the partial to the root view folder:
~\Views\Shared\EditorTemplates\_Edit.cshtml
It works, Visual studio has no problems to resolve the template, and the div is renderred correctly in my browser.
I also tried to customize the RazorViewEngine, did not work either
namespace MySite.Web
{
public class RazorViewEngine : System.Web.Mvc.RazorViewEngine
{
public RazorViewEngine()
: this(null)
{
}
public RazorViewEngine(IViewPageActivator viewPageActivator)
: base(viewPageActivator)
{
AreaPartialViewLocationFormats = new[]
{
"~/Areas/{2}/Views/Shared/EditorTemplates/{0}.cshtml"
}.Union(AreaPartialViewLocationFormats).ToArray();
}
}
}
Just wondering what did I do wrong? BTW, I am using MVC3 at the moment, can't upgrate to MVC4 due to some old components.
When calling a partial view or view from a different area in MVC, specify the full path of the partial view or view. Since MVC is based on convention, by convention it will look in the same area the calling code in the view (or controller) resides for any partial views or views referenced, unless a specific path is used. Try using the full path to reference the partial view when it is located in the products area:
#Html.EditorFor(m => m.ProductEditModel, "~/Areas/Products/Views/Shared/EditorTemplates/_Edit.cshtml")
Since the view referenced is a shared view it doesn't matter if you specify the full path if you are in the same area. However, if you are trying to access a view within a different directory than the view trying to reference it and this directory is not named shared you will need to specify the full path regardless of area. It is similar when the controller calls the view; if a controller from the same area as the referenced view specifies the short name for the view and this view is from a parent directory named different than its own (ignoring the "controller" suffix) the view engine will not find your view. Unless of course the parent directory for the view is in the shared folder.
Whether it's in the controller or a view you can't use the "short name" across areas because the view engine has a convention for where to look when the path isn't used. Areas are meant to do this to keep your code separated, or decoupled if you will, at a high level by default. So any decision to "cross the barrier" should be thought about mindfully, but certainly not discouraged. It's all about convention.
I am answering my own question now.. My page view path was not correct. Since my area is Products, controller is ProductController, my page view should be placed in ~\Areas\Products\Views\Product\EditPage.cshtml, that way, it matches what the view engine expects, and the partial will be corrected resolved.

MVC 4 solution structure Partial View

Well since most of the tutorials and demos chooses to put the partial view in shared folder im asking for a better way to do this.
My problem:
I want to use partial views in order to create a dynamic interface with reusable views. As i can with usercontrols. Since there might be a lot of partial views i want to put them in a seperate folder than the shared folder.
example:
How it looks today:
[View]
[View.Home]
index.cshtml
[View.Shared]
_layout.cshtml
A better way
[View]
[View.Home]
index.cshtml
[View.Shared]
_layout.cshtml
[View.Shared.Partial]
partial1.cshtml
etc
Or should i rethink my approach enirely? if so how? if not what should i think about?
Here is an example of something I do,
Application Wide used partial views - I put them in my Shared directory.
Partial Views for a specific Controller - I put them in the Views/[ControllerName] Directory.

Is it possible to simplify the meta data at the top of a MVC Razor file?

This doesn't really look so clean. Can I simplify this by putting all in just one curly brace?
#model Test.WebUx.Areas.Administration.ViewModels
#using Test.Shared.ExtensionMethods;
#{ Layout = "~/Areas/Administration/Views/_locs.cshtml"; }
The #model directive is required and has to be on it's own I believe.
You can however move "Text.Shared.ExtensionMethods" into the namespaces element in the web.config file in your Views folder and it will be imported on all of your views. This is especially useful if these functions are used in multiple views.
I don't think you can do so. It's just like an HTML tagging system, but without any closing tag. To use Razor view engine, one uses # to start the Razor code and fetch values that are being passed by controller.

Is there an equivalent to Monorail view components for the ASP.Net MVC Framework?

I make heavy use of View Components in some of the larger applications I've built in Monorail - What is the equivalent approach in ASP.Net MVC for a view component, that can support sections etc.?
Actually you have several options to create the equivalent of a ViewComponent in ASP.NET MVC, depending in the complexity of your component. I use these two approaches which are the more mvc-ish of the options I am aware of.
1:
The simplest thing is to create a ViewUserControl and display it using Html.RenderPartial with the helper. The ViewUserControl is a simple piece of markup with no backing controller (I think you can put a codebehind file if you want).
Optionally, you can pass a model object or the entire ViewData dictionary to the view when calling RenderPartial, like this:
<% Html.RenderPartial("TopBar", model); %>
"TopBar" is an ascx page. This works anywhere, in master pages and in normal views.
2:
If you want your component to have more complicated logic or to access datasources, IoC, etc, then you can use Html.RenderAction which is an extension method found in the Microsoft.Web.Mvc assembly. I am using this out of the mvccontrib distribution. It works like this, you need to create a normal controller with all the logic you need, then create some views and all of these things become your component, for example:
public class AboutComponentController : Controller {
public IRepository Repository{ get; set; }
public ActionResult Detail() {
var lastEvent = Repository.FindAll<Auditoria>().FirstOrDefault();
return View(lastEvent);
}
}
Notice how I have a reference to an IRepository which is going to be injected with IoC (Windsor in my case) and I can do anything a normal controller would do.
Now, in any page (master or normal) where you want to use your component, import Microsoft.Web.Mvc and call Html.RenderAction with the appropriate parameters. This will create a mini mvc pipeline that creates the controller, resolves the view, etc., just like a Monorail ViewComponent. I prefer to use the lambda based variation of the method, like this:
<% Html.RenderAction<AboutComponentController>(x => x.Detail("a message"));%>
Unfortunately, the only way to pass parameters is to use the method call itself, which in turn must be unique in the controller. Still needs some work to resemble a ViewComponent.
I don't use masterpages or layouts in the views of my components since they are composition elements themselves.
Remember that when using the Webforms view engine, you can have strongly typed views if you like to have intellisense when using the Model variable in code blocks.
The beauty of this is that you can mix view engines with these approaches, I usually create the components in nvelocity and display them in aspx pages, etc.
I now there can be issues with caching of the partial views but I haven't run into any so far. I am sure there are other options (like subcontrollers in mvccontrib) but this is usually enough for simple cases. Of course you can use normal ASP.net components in your aspx view pages but that would be cheating right? hehe. I hope it helps.
Phil Haack blogged about creating areas to group controllers into sub-folders/sections similar to MonoRails.

Resources