Proper way to handle _Layout needing access to scope in .NET MVC - asp.net-mvc

I am using angular with an existing .NET MVC application. All of our .cshtml views are based include a partial _Layout.cshtml. We have a left sidebar generated in this _Layout. Depending on the web route the content in that left bar will change.
Problem is at one point I need get and put information into the sidebar in _Layout from $scope. The only way I know to do this is to go into the layout and wrap it with a ng-app. Something like so:
_Layout:
#(ViewBag.HotList != null ? " ng-app=hotListApp" : "")
if (ViewBag.Hotlist != null)
{
<ul ng-controller="someCtrl" id="hotlist" class="hotlist">
</ul>
}
#RenderBody()
But this is messy I have tons of pages. I would have to do this ng-app decision for most my pages. Is there a better way to separate this? How have others conquered this issue?

We put our ng-app at the body tag level:
<body id="ng-app" ng-app="app">
This way you can interact with any of your UI elements wherever they may live. You may want to consider making your HotList into an Angular Directive. You could then put attributes on your directive that you can interact with in your controller for the sidebar. You directive could look like:
<div ng-controller="sidebarController"
<HotList data="myData" />
</div>
The sidebarController would be responsible for retrieving the data and assigning it to the $scope variable "myData". The logic inside your HotList controller would know how to render that data in an unordered list.

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 do I encapsulate calls to ViewBag or ViewData?

I want to be able to able to register script blocks in the ViewData or ViewBag and then unload them on my layout page at the correct position.
I tried writing a #function {} in my _Layout but this cannot be called from my Views.
So how do I write a class that allows me to do something similar to
#Something.registerscript("myscript.js")
And then on the Layout page
#Something.RenderScripts()
I saw an implementation using the singleton pattern here...
Add CSS or JavaScript files to layout head from views or partial views
But im afraid that will cause problems as this should be dynamic not static!
Is this for a generic solution or do you just want to include scripts files from within your views? For the latter, you can always create a section called "Head" or whatever which is created within the <head>-element.
For example:
_layout.cshtml
<html>
<head>
#RenderSection("Head", false)
</head>
...
</html>
View.cshtml
#section Head
{
<script type="text/javascript">...</script>
}
I wouldn't propose this as an answer, since there is the possiblity that you do want to actually do more than this (as far as I know, this doesn't work with EditorTemplates etc.). But in case you were thinking to complicated, this works very easy.
Why would you use ViewBag for this ? If you want to create something that has request-wide scope, use HttpContext.Current.Items - in your case implement one storing helper method, and one "render everything stored" method.
Viewbag or it's close relative viewdata I'd say are the best collections to use for this, as your context appears to be within the view. Where would you register the script & where would you render it?
You could, I think, write helper extensions to wrap up the register & render side of things. I believe helper methods can access viewdata. You'd get the added bonus of the abstraction away from the actual store you used, as you'd only reference it from these helper methods.

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.

Can a controller influence the _layout.cshtml file?

I'm stuck! I'm under the impression that the _layout.cshtml file is used for MasterPage-like content. Everything there is rendered on every page. Naturally, I want to write the code for rendering my sidebar menu in that file.
I want to dynamically display a list of Categories from my DB, but I'm having a problem with passing the actual model of categories to Layout.cshtml since it seems no controller actually touches it.
Any suggestions?
Otherwise please tell me how to approach this problem. I've been wracking my brain for the past three days and still no elegant solution.
I need to:
Dynamically fetch a list of Categories from the DB.
Display this list of Categories on every single view. (Hence the use of _layout.cshtml)
Elegantly handle each different categories click.
I'm at my wits end. :P How would you solve this?
_layout.cshtml
#if(isSectionDefined("Categories"))
{
<div id="sidebar">
#RenderSection("Categories", required: false )
</div>
}
index.cshtml
#section Categories {
<ul>
<li>Category One</li>
<li>Category Two</li>
<li>Category Three</li>
</ul>
}
see this : http://weblogs.asp.net/scottgu/archive/2010/12/30/asp-net-mvc-3-layouts-and-sections-with-razor.aspx
Any viewmodel that you pass to your view is automatically available within your master page. If you do not use RenderAction/Action which is the best approach, then you must create the necessary master page data in every action and add it to viewdata - either by having a common base class for your strongly typed viewmodel that contains all master page data or by using the viewdata dictionary.
I would strongly recommend that you go down the html.action approach though. In this way, you have a totally separate controller action for dealing with your list of categories. This action can retrieve the neccesary category data and return the categorylist usercontrol as a partialview and you will not have to worry about polluting all your other actions with this data.
As I see it, ViewData (and its relatives like ViewBag, Model, etc.) is meant for the specific current view. Your _Layout.cshtml is not specific to the current view; and it would be awkward if EVERY controller would have to pass the categories data in addition to whatever else data it needs to pass for the view.
Instead, what I do, is provide a static method in one of my helper classes that retrieves the categories from the DB. I also do some caching there, so that I do not have to hit the DB on every single request. The _Layout.cshtml then simply calls this static method. Simple and elegant.
If you wish, you can bring this out to a partial view, make it a helper method, whatever.
One note of caution though - my custom error view also uses the same _Layout.cshtml, and if the DB goes down, you get an exception trying to display the exception. ASP.NET MVC is smart enough to detect this and abort processing, but you're left with a nondescript default error page. What I did was to place try...catch statements around these dangerous calls, which quietly ignore the exception if the current page is the error view.
I've achieved something similar by having my ViewModels implement an Interface which has members that contain the menu data. In my action method I set that data. Then in my view I check to see if my view-model implements that inteface, pull the menu data out and render the menu (in a partial view actually)

Can I use a Controller to give information to _Layout.cshtml?

Basically, my _Layout.cshtml file is my MasterPage type View.
In it I'm designing the common look that will be shared across all Views.
I'm trying to load a left sidebar with a listing of all Categories in my database. For this purpose I've created a ViewModel called SidebarNavigation.cs
In my _Layout.cshtml I'd like to do something like:
<div id="leftnavigationbar">
#Html.Partial("_SideBarMenu", model)
</div>
But it seems that _Layout.cshtml doesn't actually use a Model up top in the page.
Any suggestions?
Just like in the aspx view engine the layout page "receives" the same model as the content page. For additional view data like this you should add it to the ViewData dictionary

Resources