Setting the layout of ServiceStack Razor views on per-customer basis - asp.net-mvc

I am working on a ServiceStack-based web application that will be used by multiple clients. There is a default layout/design that will be used in the absence of a client-specific one. So I was hoping to take advantage of the support for cascading layout templates available now in ServiceStack Razor but am having no luck making it work.
Here is roughly how I have structured the views in my project:
\
_ViewStart.cshtml
DefaultLayout.cshtml
SomeSharedContentPage.cshtml
\Views
SomeSharedViewPage.cshtml
\ClientA
LayoutA.cshtml
StylesA.css
\ClientB
LayoutB.cshtml
StylesB.css
The logic in _ViewStart.cshtml checks the identity of the logged-in user and sets the appropriate layout kind of like this (in a simplified form):
if (user.Client.ID == CLIENT_A_ID)
Layout = "~/Views/ClientA/LayoutA.cshtml";
else
Layout = "~/Views/ClientB/LayoutB.cshtml";
In turn, client-specific LayoutA and LayoutB both use the shared basic design/layout defined in DefaultLayout.cshtml by including the following at the top:
#{
Layout = "~/DefaultLayout.cshtml";
}
I was hoping to achieve a cascading nested layout effect whereby both SomeSharedViewPage.cshtml and SomeSharedContentPage.cshtml are displayed with the final layout comprising both the default and custom elements.
Unfortunately it doesn't work even when I hard-code one of the layouts in the view, nor when I explicitly specify the path of the layout page (e.g. Layout="~/Views/ClientA/LayoutA.cshtml" instead of Layout="LayoutA").
What am I doing wrong?
UPDATE
I got the top-level shared layout to work by renaming DefaultLayout.cshtml to _Layout.cshtml but client-specific layouts LayoutA and LayoutB are still not not being applied, so obviously SS Razor now simply falls back to _Layout.cshtml by convention.
I know that support for cascading nested layouts was recently added to ServiceStack, so I must be doing something wrong.

Based on the testing I've done, I don't think ServiceStack Razor supports _ViewStart.cshtml. However, you should be able to dynamically change the Layout via code using other methods. For example, you could set it up like this:
Default.cshtml
<h2>Default</h2>
Views\_Layout.cshtml (the default for ServiceStack Razor)
#{
if (user.Client.ID == CLIENT_A_ID)
Layout = "_Layout2";
else
Layout = "_Layout3";
}
Views\_Layout2.cshtml
<h1>Layout2</h1>
#RenderBody()
Views\_Layout3.cshtml
<h1>Layout3</h1>
#RenderBody()
You should also be able to use \Views\ClientA\ALayout.cshtml but you will have to make sure all the layout files use a unique name like ALayout.cshtml and BLayout.cshtml.

Related

Casual layout patern in mvc with razor

I can't find the reel intentions behind MVC-Razor layouts through internet.
In shared folder, there is :
_Layout.cshtml
_LoginPartial.cshtml
Should i use the _Layout for pages that dosen't require to be logged in, and use _LoginPartial for pages that require to be logged in ? Or am i completely lost ?
To make it simple :
If i create a new view that can only be reached when logged in, should it be beginning with
Layout = "~/Views/Shared/_Layout.cshtml";
or
Layout = "~/Views/Shared/_LoginPartial.cshtml";
?
Edit :
Checking tutorials and explanation from everyone (thanks all)
_Layout.cshtml is exactly like a master page in WEB FORM,
So i should always use :
Layout = "~/Views/Shared/_Layout.cshtml";
at the begining of a page i want to have formated like others.
The Login partial can be applied after authentification to alter the layout (disconnect button instead of connect, ect.)
The file _Layout.cshtml represent the layout of each page in the application. While partial view is a custom, reusable component that you can use in each page you need it. For example, we can create a partial view for customer and call it many time in page
<table class="table table-condensed">
#foreach (var student in Model.Students)
{
#Html.Partial("_StudentForm ", student)
}
</table>
So _Layout is meant to be used for all pages and _LoginPartial.cshtml can be used inside the page you need to have a login form in. Check this article about partial view
Tips and Tricks about Razor Partial Views
Layout = "~/Views/Shared/_Layout.cshtml";
in your view start file (_ViewStart.cshtml), may times its the ONLY thing in that file.
#{
Layout = "~/Views/Shared/_Layout.cshtml";
}
the _Layout.cshtml and _Viewstart.cshtml combo is similar to a master page in web applications but these will not have controller actions associated with them. if you set the layout setting in the _ViewStart file you don't need to set it in your actual views, they will inherit it from the viewstart file.
If you look inside the layout file you will see this line somewhere
#RenderBody()
That is where your individual views HTML will end up when your specific view is called.
The Login partial is just a quick start to demonstrate a view that can change display based on if the user is logged in or not.
You can use the same _Layout.cshtml but your controller ActionMethod should change to Authenticate. Use the below link for more info.
Authenticate User in MVC
Its more of a naming convention for layouts.
views will inherit it from the viewstart file. If you look inside the layout file you will see the renderbody method.
#RenderBody()
This is where the HTML code is read and shown in the browser.
The same goes for the _loginPartial.cshtml its just there for looks and to show you what Mvc is capable of.
Visual Studio creates the layout _Layout.cshtml when all but
the Empty project template is used. This layout is applied to all views by default through the /Views/_ViewStart.cshtml file.
If you do not want the default layout applied to views, you can change the settings in _ViewStart.cshtml (or delete the file entirely) to specify another layout in the view, like this:
#{
Layout = "~/Views/Shared/MyLayout.cshtml";
}
Or you can disable any layout for a given view, like this:
#{
Layout = null;
}
Hope this helps.

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.

adding attributes in HTML tag of specific views only?

I am working on a MVC application with razor view engine.
HTML, Head and Body tags are placed in shared view _Layout.cshtml and other views are using this as layout.
While supporting application I am in need to add a custom attribute in some of pages (not in all pages). If I add attribute in layout it will appear in all pages. Can you pleased guide me how adding an attribute can be managed only in desired pages.
Below is my html tag with attribute:
<html ng-app="business_rules">
You can create a Layout where you need all these attributes and just refer this layout for desired pages using
#{
Layout = "Path/To/Layout.cshtml";
}
on the top of those pages.
For rest of the pages, you will use the different layout without those attributes.
You can define the Layout form the controller too. It can be done as below:
public ActionResult Index()
{
MyMOdel model = new MyMOdel ();
//TO DO:
return View("Index", "_AdminLayout", model);
}
I would use jquery. Just add on pages where you need this attribute following code:
<script>
$(document).ready(function(){
$('html').attr("ng-app", "business_rules")
});
</script>
I came across the exact same problem right now and the best solution I came up with was this below (I don't really want to add an entirely separate layout just for an attribute on the body tag). You've got a couple of suggestions on the asp net forums as well
I added an item to the view bag, a (probably overly) generic one called BodyAttributes:
ViewBag.BodyAttributes = "ng-app=\"moduleName\"";
Then I render this in pure htlm on the body tag
<body #Html.Raw(ViewBag.BodyAttributes)>
This keeps your layout non page specific and the angular doesn't need to be bootstrapped on every view. Plus, whether or not the angular is bootstrapped is now controlled from within the view itself where it belongs.

ASP.Net MVC dynamically switching Views

I have an ASP.Net MVC 4 application where the user can choose a theme or a design of for their hosted one-page site (within this application). At first, I thought to do this with the built-in Areas but due to some application restrictions I've decided not to use that method. The way I thought to do it (which so far works) is to send the user to the index action of the controller, there find out which theme they have chosen and then return the appropriate view. this way I don't have the action name on the url which is nice since the url needs to be simple like: abc.com/cb/websiteID. btw, every theme/design has one view in the folder.
For some reason this method does not sit well with me and I think there should be a better way of doing this. Is there a downfall to this? Is this method a bad practice? is there a better way?
If I've left out a detail, please let me know and I'll do my best to address it.
Do you have a limited set of themes, which your users can choose from?
If so, I would consider to have a layout-per-theme instead, have a single view and dynamically switch layout based on params...
//in your controller
public ActionResult(int id) {
string layoutForThemeName = SomeService.GetThemeForUser(id);
ViewBag.LayoutName = layoutForThemeName
}
// in your view Index.cshtml
#{
Layout = ViewBag.LayoutName;
}
Do not forget that Razor let you inherit one layout from another, so you could event create base layout with script references etc. and layout for every of your themes.

ASP.NET MVC 3 Razor Partial View - jQuery Included In Main Layout

A partial view that I'm using requires certain jQuery libraries to be included, and I'm currently trying to think of the best way to nicely add them.
My current setup is as follows:
_Layout.cshtml:
...
#if (ViewBag.jQuery)
{
<script src="#Url.Content("~/Scripts/jquery-1.5.1.min.js")"
type="text/javascript"></script>
}
...
Index.cshtml:
...
#{ Html.RenderPartial("PartialView",
new MVCModel.Models.MyModel(),
new ViewDataDictionary { { "ViewBag", ViewBag } }); }
...
PartialView.cshtml:
...
#{
if (ViewBag.ViewBag != null)
{
var vb = (dynamic)ViewBag.ViewBag;
vb.jQuery = true;
}
}
...
So what I'm trying to do is "turn on" the library in the partial view, and have that boolean propagate up to the master layout. This makes it so that I can enable the library as much as I want wherever I want (many partial views, or one view used multiple times), but it will only get included once. On top of that, I get control over the ordering of the includes, so I can make sure a files dependencies are included first.
I'm just wondering if there is a nicer way of pulling this off.
Right now my two biggest issues are:
The ViewBag is not strongly typed, so intellisense won't tell me which libraries are available to me.
Passing the ViewBag to the partial view, just so I can re-use it.
Here is a simple way to accomplish your objective.
There is a function in MVC Razor views called RenderSection. It has a syntax like
#RenderSection ("occasionalScripts", false)
(occasionalScripts is the name of the section and false means the section is optional and may not appear in every page.)
You would want to include this in your _Layout.cshtml which is Views\Shared. This will allow your main script template to display a section with your script definitions if you have it defined for a particular view.
Generally, you want to put this at the bottom of your page just before the closing </body> tag. (This is a best practice for performance.) I would list all of my other scripts (the ones which load on every page) just above it. The reason is because the load order of jQuery scripts is very important.
In each of your views where you have special scripts, add the following:
#section occasionalScripts {
... put script references here
}
If you have a view which requires no special scripts, then you don't need to include this section there. The false on the #RenderSection tag will accommodate any view where the #section tag is missing.
On each page where this functionality is implemented, you can select different scripts.
Optionally, you could have multiple sections defined for different categories of files.
I usually have a different approach, and consider that ALL libraries used on the web site should be referenced on every page. It does make the first load a bit slower, but then the loading of all the following pages is faster, as it uses the browser-cache.
To improve it even better, you can make all your libraries available in only one file.
I used SquishIt for that at some point. I integrates rather well in ASP.NET MVC.

Resources