Has anyone here structured your views, controller and actions in such a way that the page titles are set from the database. How do you identify one view from another ? In which phase of the lifecycle do you retrieve the page title and set it for the view/action method
a lot of posts are scattered for asp.net, php but then a efficient way for mvc I am yet to find
You could use action filters:
public class ControllerAndActionNameAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
filterContext.Controller.ViewBag.ControllerName = filterContext.RequestContext.RouteData.Values["controller"].ToString();
filterContext.Controller.ViewBag.ActionName = filterContext.RequestContext.RouteData.Values["action"].ToString();
base.OnActionExecuting(filterContext);
}
}
But instead of putting the action- or controllername into the viewbag, you could load stuff from your database based on action and/or controller.
What if you have service or helper method that retrieves all the titles from the database and stores them to some sort of cache or static variable think Dictionary. Depending on how you plan to update with the site running determines when or how.
Then expose a static helper method that the views can call to retrieve their title.
You could probably even incorporate a T4 template to set a property for each view to call the method with the correct retrieval key
Related
I guess I have a pretty standard problem as I want to pass certain data from an asp.net MVC 5 controller to a shared view, in my case navigation.
I have a template that shows username and userpicture in the navigation shared view, so I need to pass the respective data to it.
My layout structure:
Shared\Layout
Shared\Header
Shared\Navigation
%Body%
Shared\Footer
Where the Layout ist the master view and of course I also have my other views like Home\Index.
As you can imagine, I have to show the username and his userpicture in every view except the login/registration or any error views.
These views do not use the layout at all, so everytime a view is rendered using the layout structure, the user is already logged in.
So I was researching about ways to pass data from my controller to the navigation view, although my controller returns the Index view and would appreciate some clarification on their disadvantages and valid choices in my use case:
Use case:
My project has a pretty dumb MVC application that the user can access. Once he logs into the MVC app authenticates the user against the same webapi where it get's it's data from and stores the access token as well as other user details for further requests. I'm not yet sure where to store that data. As far as I understand it, the options would be Cookies, Session and local storage.
As I am pretty new to asp.net, MVC and C# in general, I didn't yet figure out how to make the [Authorize] Attribute work inside the MVC app so it can mark the user as authenticated :/
I guess the key problem is that the MVC app does not have access to the database and therefore cannot check the login and populate the User Identity.
How to transfer data from controller to view:
ViewBag: The easiest way of passing data to the view. It is not strongly typed and can be accessed in all views. I was told it is kind of a bad practise to use it and was advised to use viewModels.
ViewData: seems to be kind of the same thing as viewdata.
ViewModel: A strongly typed model that is passed to the view and needs to be declared in any view that uses it. So if I want to use it in my navigation view, I'd need to declare it there. The big disadvantage of this approach is that every viewmodel needs to have kind of a baseViewModel so they have a common structure which appearently can cause problems later down the road and also prevents me from inheriting other models to populate my viewModelStructure.
Cookies: Obviously I can store data in cookies during login and then access them in the view, but the cookies HAVE to be there so I would not be able to save this information in the session or local storage
Session: I can also store data in the session, but the session expires when the user closes the browser tab.
LocalStorage: This is pretty new to me so I can't judge it.
User Identity: I just discovered that I can also access the user's identity from Context.User.Identity.
Global Filter like the [Authorize] attribute or a custom one: If I understand it correctly, with a global filter I can populate needed data automatically in every controller action and exclude the ones that dont need it like Login/register etc. I'm not yet sure how to apply this way because of my project structure (see above).
RenderAction: I could also call another controller method via the RenderAction helper method to always render that section of the page.
How do you guys solve this problem?
If you need more clarification, please do ask.
Thanks :)
Use a base ViewModel. You can still use inheritance to build up functionality in your view models, they'll all just share a common base view model, as you said.
I'm not sure what problems you envisage with this approach, but I would suggest they're outweighed by the benefit of strongly typed, maintainable view models, that can be used by all your views, including partial views and _Layout.
A suggestion to get you started based on your layout structure:
public abstract class ViewModelBase {
public HeaderViewModel Header {get;}
public NavigationViewModel Navigation {get;}
public FooterViewModel Footer {get;}
public ViewModelBase(HeaderViewModel header, NavigationViewModel navigation, FooterViewModel footer) {
Header = header;
Navigation = navigation;
Footer = footer;
}
}
public class HeaderViewModel {
// properties
public HeaderViewModel(...) {
}
}
public class NavigationViewModel {
// properties
public NavigationViewModel(...) {
}
}
public class FooterViewModel {
// properties
public FooterViewModel(...) {
}
}
I'm working on a Web Application project using C# and MVC that will take in two URLs in a form and use them to create an instance of a class I have created called "ImageSwap." This model has some data (a username of the person performing the swap; two variables which hold the URLs of two images to be swapped; two variables which save the actual names of these files without all of the rest of the URL information; and two arrays which represent the file locations to check for these files). Right now, I have it so that the initial index view creates the instance of the class and passes it to the same view with the information put in through the form, submitted via POST, like so:
public ActionResult Index()
{
Ops.Operations.Models.ImageSwapModel newImageSwap = new Models.ImageSwapModel();
return View(newImageSwap);
}
[HttpPost]
public ActionResult Index(ImageSwapModel imageSwap)
{
var oldFileFound = false;
var newFileFound = false;
if (ModelState.IsValid)
{
//Perform data manipulation and set needed values
}
}
It then performs some functions on the data, such as parsing out the filename at the end of the URL, and a directory number (which is the first part of this filename, i.e. directory#_fileName.jpg). All of this works fine.
My problem is that I would like to pass this model to another view once it has data populated in all of its fields by this initial ActionResult so that I can have a verification view, which would allow the user to preview the two files side by side so that they can ensure they are swapping the appropriate images. They should then be able to hit another submit button which will initiate the actual moving/replacing of the images and be taken to a page confirming.
Is there a way to pass data from this controller to a different view? My confusion arises because I cannot create another version of an ActionResult of Index with the same input, but I do not want to have the actual swapping of the images occur without a preview and a prompt. Should I re-write my Index view so that it utilizes partial views in order to accomplish this? What is the easiest way to have data persist through multiple steps and views?
What is the easiest way to have data persist through multiple steps
and views?
Your question sounds like you're trying to achieve what you can easily do with sessions. The session object allows you to persist data between requests simply by adding it to the Session object on the HttpContext that exists within the base class that your controller extends, like so:
(Note the Serializable attribute. This allows your object to be serialized into the session object).
[Serializable]
public class ImageSwapModel {
// Your class's properties
}
Then in your controller you can do the following:
[HttpPost]
public ActionResult Index(ImageSwapModel imageSwap)
{
var oldFileFound = false;
var newFileFound = false;
if (ModelState.IsValid)
{
this.HttpContext.Session["ImageSwap"] = imageSwap;
}
}
When you want to retrieve the model you can grab it from the session like so:
var imageSwap = (ImageSwapModel)this.HttpContext.Session["ImageSwap"];
Taking it one step further:
Whilst the above will work fine, generally it's not a good practice to reference the HttpContext object directly in your code as it creates unnecessary coupling to the HttpContext object that can easily be avoided. Instead you should opt to inject an instance of the session object via Dependency Injection. Here is a similar answer that provides a basic idea as to how you can do this.
You can return different views with Models being passed to them in your one Index action like
if(some condition)
{
Return View("ViewVersion1", MyModelVersion1);
}
else
{
Return View("ViewVersion2", MyModelVersion2);
}
On the site I'm working on, there are users with different permissions on the site.
Given the schedule ID and employee ID that we're currently looking at, we can get their role-specific permissions.
Right now, our BaseModel has a property that properly accesses the DB and grabs this info.
For all views that pass a model to the view, everything runs fine.
The problem lies in Controller Methods where no model is passed. In a few views, all they're supplied is a few ViewBag entries, and work fine.
However, I /need/ the CurrentPermissions property in those pages nonetheless, for the layout. Whether or not the permissions have one boolean value set true/false, something may/may not be displayed/populated.
So, my option seem to be:
Somehow throw my CurrentPermissions into a ViewBag entry for all views, and access them through that instead of the base model.
I'm not sure how to do this. I've seen people using OnActionExecuting, but that fails since my connection to TransactionManager is not yet set up at that point.
Somehow throw just the BaseModel into those views that don't currently pass a model. I'm refraining from this as much as possible. I'm not sure how I would go about doing such, but it seems like that would over-complicate the situation.
How can I go about pushing this CurrentPermissions object (generated from a call to my TransactionManager) to every view (specifically, the Layouts!)
Your approach is what we use in out projects... and we use this approach to systematically remove the use of ViewBag changing it to ViewModels.
Other approach we have used (for UserPreferences in my case) is adding an ActionFilter that ends including the preference in the ViewBag. You decorate the actions needing it with [IncludePreferences] in my case (that is the name of my filter attribute.
EDIT ActionFilter:
public class IncludePreferencesAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var controller = filterContext.Controller as BaseController;
// IController is not necessarily a Controller
if (controller != null)
{
//I have my preferences in the BaseController
//and cached but here you can query the DB
controller.ViewBag.MyPreferences = controller.TenantPreferences;
}
}
}
In you action you decorate it using [IncludePreferences]
As a temporary solution, I'm doing the following at the top of my Layout:
#{ OurModel.SupervisorRestriction CurrentSupervisorRestrictions = ViewBag.CurrentSupervisorRestrictions ?? Model.CurrentSupervisorRestrictions; }
This way, if we're passing in an object then it works just fine. Otherwise, I'll directly pass in a ViewBag.CurrentSupervisorRestrictions from the controller. There are only a few cases, so it's not that bad.
Better suggestions would be great, though.
I’m using authentication with Active Directory within an ASP .NET MVC3 web application.
I want to control what components are rendered in the view. The option I’m considering is passing in the groups the logged in user is part of from the controller, and then using a helper object to check whether the user is part of the required group. Is this the best method to achieve this? If it is, then how would I achieve the controller part?
Thanks.
Well this could be achieved if you use a correct model linked to the page (view-model, not a class from your domain), if you follow this approach, the view model should contain all the information required by the view and nothing more, since your view requires to know which controls should be visible based on the current user roles, I would create a property for each control in the view model like this:
public class MyViewModel
{
public bool ShowMycontrol1 { get; set; }
}
in your controller populate this "flags" using the current user roles, and finally in the view check for these properties in order to show or hide the required controls
This is just one way to accomplish this, I'm sure there many more
You can write an extension method. Let's say you want to check whether user is admin or not. So, you do that using extension method like this;
public static class AuthHelper
{
public static bool IsUserAdmin(this HtmlHelper helper)
{
return helper.ViewContext.HttpContext.User.IsInRole("Administrator");
}
}
i hope this helps.
I'm building a site where i need to be able to notify a user on each page. The function is going to look alot like what StackOverflow is using, the yellow(orange?) border at the top. I dont want to reproduce this logic inside all of my actions. DRY.
I'm thinking about using a action filter, but since i need to query my data layer it does not seem to be the best way to solve it.
So, for example, what would be the best way to implement a feauture like "You have a question with bounty ending in X days" on each page?
In ASP.NET MVC 2 there's the Html.RenderAction. For ASP.NET MVC 1.0 if I recall correctly there's the same functionality in MvcContrib.
When I have logic that needs to go on every action, that needs to initialise view data, I do this in my OnResultExecuting method (called after action runs, before view rendered) of my base controller (from which all my controllers inherit), which sets data on my base view data model (from which all my view data models inherit):
public class BaseViewData
{
public string UserMessage { get; set; }
}
public class BaseController : Controller
{
protected override void OnResultExecuting(ResultExecutingContext filterContext)
{
ViewResult viewResult = filterContext.ActionResult as ViewResult;
//Only continue if action returned an ActionResult of type ViewResult,
//and that ViewResults ViewData is of type BaseViewData
if(viewResult != null && viewResult.ViewData is BaseViewData)
{
((BaseViewData)viewResult.ViewData).UserMessage = userService.GetUserMessage();
}
}
}
I would use TempData to store the message, add a helper to render the message (if there is one), and then use that in your master page. This is basically how rails does it, and I have always felt the lack of a notification system (like flash) was an odd gap in ASP.net MVC, just because it is so easy to do.