Share one action with two views - asp.net-mvc

I'm working on a website which needs a control panel that is used by multiple user types. I've been wondering is it possible to make different views that have different layouts but they are similar in operation use a single action so that I wouldn't need to re-write them again?
For example, both Admin and Supervisors can modify employee details but the admin views have a different layout than the supervisor. I can write different actions for each view, but the code in both actions would be exactly the same as they do the same operation. So can one action be shared with many views ?

You certainly can. However, the approach depends on how you determine which layout to present and where that information comes from. The most straight-forward approach is to determine the type of users (Role) and based on the user's role you can render a specific view within the same action method...
public ActionResult YOUR_ACTION()
{
//do your processing
Role role = GetCurrentUserRole();
if(role.Name = "Admin")
return View("ViewForAdmins");
else if (role.Name = "Supervisor")
return View("ViewForSupervisors");
else
return View("EveryoneElseView");
}

Maybe you could put employee details in a partial view and call RenderAction from main views.

Related

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.

how to manage logic in the _Layout.cshtml

I am currently working with an mvc site where I have a fairly extensive main layout page. It is dependent on data from the database which in turns includes inherent logic as what to include etc on the layout.
Most of my controller actions are rendered within this layout. I am not sure of how to work this. Being used to master pages in web forms, all the logic resides in the master page. I have a couple mechanisms to achieve the common layout logic but looking for the best practise way of achieving such.
Options are:
Replicate logic in all controller actions (not really an option)
Extract the logic in to another class and call it from the controller actions
All controllers inherit from a base controller which as overrides the OnActionExecuting method and us this to perform logic and return the necessary data in the TempData
Use "RenderAction" in the cshtml to call necessary controller actions and extract the layout in to smaller partial views
Any other options open to me or recommendations?
If I understand you correctly, I would use an action partial
#Html.Action("{ActionName}", "{Controller}", new { roleName= "Admin" })
Action Partials call dedicated controller action methods of type
[ChildActionOnly]
public ActionResult _TopNav(string roleName)
This way you can design recurring logic that will propagate throughout your application without replicating.
Even better, if your _Layout handles privilege based link generating, you can pass role id's and control what the end user sees and what their navigation buttons point to.

How to keep views free of authorization logic in mvc?

I have a view to display a list of items.
The user can edit, delete or create new items, but according to their authorizations they may or may not be allowed to do some of this actions.
I have the requirement to display only the actions which the current user is allowed to do, but I don't want to clutter the views with authorization if-else's
Despise of being a very common requirement, I can't find a real satisfactory way of doing it.
My best approach so far is to provide an overload to the Html.ActionLink extension method that takes the permission to ask for, but there are going to be more complex scenarios, like hiding entire blocks of html or switching a textbox for a label+hidden.
Is there a better way to do this?
One example I can think of would be to invoke Html.RenderAction (link: http://msdn.microsoft.com/en-us/library/ee703490.aspx), and then pass the link you wish to use as a route value to the action method (it will appear as a parameter on your action). Because it's a RenderAction, you get to go back through the controller process and thus you can have it render whatever view or data you want depending on the user's state.
Example:
<% Html.RenderAction("Permissions" /* Controller */, "PermissionLink", new { Url = "Admin.aspx" }); %>
And your controller would have something like:
public ActionResult PermissionsLink (string url)
{
// Do Whatever kind of Authentication you want here, Session is available, etc
if(authenticated)
return View("Link");
else
return View("Blank");
}
I personally don't see anything wrong with this kind of conditional logic within the view. The logic is still all about presentation. You decide whether to show or hide, enable or disable, highlight etc. This is the view's job, not the controller's. As long as the view does not need to actually compute anything to come to its decision. The controller should be agnostic of the view's implementation as much as the other way around.
My approach would be this:
Make the controller do the actual logic of deciding the level of access that the user has
Pass this information to the view using (ie via the ViewModel) but in a way that is neutral to implementation details (ie "user is admin", "user is an author", etc)
Let the view draw itself appropriately using only the pre-compiled data that is provided by the controller.
This also has the benefit that the view itself can be removed and replaced without any effect on the controller.
We had this same issue. We ended up writing a lot of helper methods and in cases where lots of html output was required, we put them in partial views.
Would it not be simpler to create several views with various controls based on what values the user can change, then return the right view based on the user's access rights?
Views should be for presenting information only, there really shouldn't be any conditional logic contained in them, or at least a bare minimum.
I've always found when i get to the point where i'm finding it hard to grok a situation like yours, the best solution is always to go back to the controller and e-assess what i'm passing to the view in the first place.
By the time the View is called to do its job, all of the important 'decisions' should have already been made.
In complicated situations where are many conditions and rules I'm doing this that way:
ViewModel
public class ModelView
{
private IAuthorisationService { get; set; }
public bool CanShow
{
...
}
}
View:
<% if(Model.CanShow) { %>
<html>
<% } %>

Pass data to the view. Always through viewData ?

I’m building an application that monitors other systems. Now I want to implement partial view, a User Control called “status”. This control shall display status information about the application.Like:
user logged in,
How many systems online,
Latest activity.
This partial view shall be rendered in nearly all other views. How shall I pass this information to the view?
I don’t want to write
Wiewdata[“SystemsOnline”] = Helpers.CountSystemsOnline()
Wiewdata[“SystemLatestActivity”] = ………………
in all my actions.
Can I write something like Html.RenderPartial(../Shared/Status) that fist go to an action that adds the viewdata?
Or shall i access the information directly in the view trough the hepler?
I noticed that the defult LogOnUserControl view use Page.User.Identity.Name to directly access that data.
When is it ok to not pass data throug viewdata in the controller?
You have to use the ViewData class for that purpose. I do not recommend to invent tricks instead.
But you do not need to manually pass the same data from every action. Just do it in your base controller:
protected override void OnActionExecuted(ActionExecutedContext context) {
base.OnActionExecuted(context);
if (context.Result is ViewResult) {
// Pass the systems online to all views
ViewData[“SystemsOnline”] = Helpers.CountSystemsOnline();
}
}
Only a couple of lines of code and you make it available everywhere.

Administration Area in Asp.Net MVC

My question may be obvious but I'd like to build a well-designed web application.
As for any administration area, the admin should be able to list/create/delete/modify users, articles, posts, etc...
I'd like to know what is the best way to design the application.
Should I create a controller for each one of those items (/Users/Create/id or /Posts/Delete/id), or create all of the action in my Administration Controller (/Administration/CreateUser/id or /Administration/DeletePost/id) ?
You should write a separate controller for each entity to keep a clean separation of concerns for your controller classes. If you only have one controller, then you will only have one Views directory with dozens of views, and your controller will contain dozens of methods, and that will soon become unmanageable.
The answer depends on how much functionality will be in the controllers. Just start of with one controller and if it gets too much split it into a few.
The great thing about MVC is where you put things in your controllers doesn't have to have any effect on the URLs. you can very easily map /Users/Create to e.g. UserAdminController class.
I would just build a new MVC website that handles the administration.
You have a more flexible solution as long as you've separated the data and the business logic in different assembly's. You could then publish your website to a subdomain, admin.yoursite.com for example. That way you don't have to mess with your routes and you can keep them in separate views which imho is the most elegant solution.
Pro's and Con's would be nice to hear.
I'm working on a project which will need the same administration site but haven't got that far yet so this question interests me a lot.
I'm currently using ASP.NET for a large client.
The approach I've adopted is to put the functionality of the action into another class.
Example
I am writing an administration section also. There will be one Administration controller (our admin section is small, if it was larger I would change the routing to allow more controllers, for now we are using the out of the box configuration). If I create an "EditUser" view. I will also create an "EditUserAction" class. All EditUser code will go into the class. I construct the EditUserAction class in the Administration controller class in the Edit User method. This removes all the action specific code out of the Controller class. This way all the action specific code is either in the action method or in the action class. Otherwise, the controller would quickly become overrun with code from various actions. The controller class would balloon to an unmanageable mess in short order.
Class examples
public class Administration: Controller
{
public ActionResult EditUser(string userId)
{
EditUserAction action = new EditUserAction();
}
}
public class EditUserAction
{
public void Save(User user)
{
//save code here
}
}
I hope this explanation is clear. If it's not let me know and I'll clarify.
To answer your question I am doing it the latter(/Administration/CreateUser/id or /Administration/DeletePost/id).
You could use DynamicData for this. It isn't MVC but it can be used together with it and it is really easy to setup and use.
Here is another way of asking my question.
A part of my Master Page:
<% if (!String.Equals(ViewContext.RequestContext.RouteData.GetRequiredString("controller"), "Administration")) { %>
<div>
<!-- Some Code -->
</div> <% } %>
As you can see, in my master page, I'd like to display some part of the page, depending on the user working on the administration area or not.
It works pretty well with only the Administration Controller (/Administration/CreateUser/id)... but it becomes a big mess when I use different controller as User or Article (/User/DeleteUser/id or /Article/Details/id).
I'd prefer to use one controller per entity, but I can't find a way to bond this approach with multiple controllers.
I suggest to use this solution.
But I changed definition to this:
public ThemedViewEngine()
{
base.MasterLocationFormats = new string[] {
"~/Views/{1}/{0}.master",
"~/Views/Shared/{0}.master",
"~/Themes/{2}/Views/{1}/{0}.master",
"~/Themes/{2}/Views/Shared/{0}.master",
"~/Themes/Default/Views/{1}/{0}.master",
"~/Themes/Default/Views/Shared/{0}.master"
};
base.ViewLocationFormats = new string[] {
"~/Views/{1}/{0}.aspx",
"~/Views/{1}/{0}.ascx",
"~/Views/Shared/{0}.aspx",
"~/Views/Shared/{0}.ascx",
"~/Themes/{2}/Views/{1}/{0}.aspx",
"~/Themes/{2}/Views/{1}/{0}.ascx",
"~/Themes/{2}/Views/Shared/{0}.aspx",
"~/Themes/{2}/Views/Shared/{0}.ascx",
"~/Themes/Default/Views/{1}/{0}.aspx",
"~/Themes/Default/Views/{1}/{0}.ascx",
"~/Themes/Default/Views/Shared/{0}.aspx",
"~/Themes/Default/Views/Shared/{0}.ascx"
};
base.PartialViewLocationFormats = new string[] {
"~/Views/{1}/{0}.aspx",
"~/Views/{1}/{0}.ascx",
"~/Views/Shared/{0}.aspx",
"~/Views/Shared/{0}.ascx",
"~/Themes/{2}/Views/{1}/{0}.aspx",
"~/Themes/{2}/Views/{1}/{0}.ascx",
"~/Themes/{2}/Views/Shared/{0}.aspx",
"~/Themes/{2}/Views/Shared/{0}.ascx",
"~/Themes/Default/Views/{1}/{0}.aspx",
"~/Themes/Default/Views/{1}/{0}.ascx",
"~/Themes/Default/Views/Shared/{0}.aspx",
"~/Themes/Default/Views/Shared/{0}.ascx"
};
}
Default theme is default so it is required to exists.
Structure of directory will be:
Content
Themes
Default
Content
Views
Home
Blog
Whatever should be skinned
OtherTheme
Content
Views
Home
Blog
Whatever should be skinned
Views
Articles
Posts
Users
Settings
Other Administration stuff
It depends on the scale size of your admin area,
I suggest you consider to do the following (or maybe document it a bit)
Consider how many entities you want to manage independently,
Consider how many actions each one will have,
Check that is there any dependency between your application and admin area (user access, for user friendly URLs )
then you can specify which approach can help you, Having one admin controller, admin actions in entity controllers, or defining a new Admin project in case of large functional applications.
*If the project scale is growing fast and soon it needs large scale, I would choose third one - having a new admin mvc project.
I hope it help you decide.

Resources