Help with ViewData asp.net mvc - asp.net-mvc

In my master page I have a menu that uses jquery ui accordion.
What is the best way to specify which item should be active?? the only thing I can think of is
$(document).ready(function() {
$("#accordion").accordion({
collapsible: true,
autoHeight: false,
active:<%=ViewData["active"] %>
});
})
But it seems a little repetitive having to set ViewData["active"] everytime a View is called throughout my whole app... what do you think?

Have you considered putting the accordian in a PartialView? Then you place your code within the PartialView and away from the Master page.
Also you can have a base controller which can set the ViewData for you so now it's in one place.
Edit
It's the same as any other base class. Your controller inherits from Controller so your base class will need to do the same;
using System.Web.Mvc;
namespace MyAppControllers
{
public class ControllerBase : Controller
{
protected override void Execute(System.Web.Routing.RequestContext requestContext)
{
ViewData["ApplicationName"] = CacheHelper.Get().Name;
base.Execute(requestContext);
}
}
}
Now set your controller class to inherit from the ControllerBase class;
public class HomeController : ControllerBase
I've also implemented a CacheHelper but you can obviously use your own flavour of storing the current value.

Related

MVC Layout model design pattern

Every page in my site should have some of the same data, similar to how in SO every page displays info about the current user at the top of the page. The way I implemented this was to have a base controller class that all my controllers derive from. In that base controller's constructor I put my model in the ViewBag, and then my layout page uses that.
I'm running into problems with this because my layouts aren't strongly typed. For example, I have to construct new HtmlHelpers in the layout page:
#{var layoutHtml = new HtmlHelper<LayoutModel>(Html.ViewContext, Html.ViewDataContainer); }
#* OK, now we can use the html helper... *#
#layoutHtml.TextAreaFor(model => model.Feedback)
I really don't want to have to have my models subclass from a layout model, since that would force each action to fill out the shared model data individually, but manually creating HtmlHelpers also seems like a bad idea.
Any thoughts?
I might be wrong, but shouldn't you use partial views for this?
Your solution looks way too complicated.
A base controller is a great way to start. I would also introduce a base viewmodel. The base viewmodel would contain user specific information.
public abstract class BaseController : Controller
{
public string CurrentAccountName
{
get
{
return (HttpContext.User == null) ? null : HttpContext.User.Identity.Name;
}
}
public T CreateViewModel<T>() where T : BaseViewModel, new()
{
T viewModel = new T
{
UserName = CurrentAccountName
};
return viewModel;
}
}
public abstract class BaseViewModel
{
public string UserName { get; set; }
}
Then on each request you would populate your viewmodel with user specific information and whatever information is required for the view. MyViewModel is just a viewmodel that inherits from BaseViewModel.
public class MyController : BaseController
{
public ActionResult Index()
{
MyViewModel viewModel = CreateViewModel<MyViewModel>();
return View(viewModel);
}
}
In the master view I would pass in the BaseViewModel and the View I would pass in the inherited MyViewModel.
Now you have access to your user information in your master view and can pass it to a partial or render it directly to the page.
you can define multiple Layouts which you can use in your appropiate views! Just include them like so:
#{
Layout = "~/Views/Shared/_MySubLayout.cshtml";
}
i believe you can use RenderAction to solve this problem. Because information this action will display is common on all pages, you can put it in BaseController and call it from your site master. it will compute its own model and return that model to partial view which can be strongly typed and you don't have to instantiate htmlHelper the way you are doing now.

How do you use Models in a master page with ASP.NET MVC?

public ActionResult Index(){
var dataContext = new DataEvidencijaDataContext();
MembershipUser myObject = Membership.GetUser();
string KorisnickoIme = myObject.UserName.ToString();
var user = from i in dataContext.korisniks
where i.korisnik1 == KorisnickoIme
select i;
ViewData.Add("user", user);
return View(user);
}
In master page i put this
<%= Html.RenderPartial("profPredmeti", ViewData["user"])%>
but this is not work
You can use RenderAction for this to delegate menu rendering to some controller. Another option is to have your controller (or base controller class, or action filter) put the menu object into ViewData, and then your master page will do
<% Html.RenderPartial("MenuRenderView", ViewData["menu"]) %>
where MenuRenderView.aspx partial view consumes the menu object from ViewData["menu"]. What does this object contain depends on your database/code.
The approach that I have taken in the past is to have a base controller class from which all the other controllers inherit. In that base controller class, you can add items into ViewData after the controller is initialized:
protected override void Initialize(System.Web.Routing.RequestContext requestContext)
{
base.Initialize(requestContext);
ViewData.Add("CommonPageData", CommonPageData);
}
The "CommonPageData" in this case is a property or type CommonPageData (a custom class) whose properties are lazy-loaded. One of the properties is a navigation item collection that is consumed on the master page.
User RenderPartial for the Menu, but you're gonna need to pass in the ViewData the datasource for the menu all the time, a solution is to make an abstract BaseController and to put the datasource in the ViewData in the constructor of that base controller
all the controllers will inherit from the base controller
I have this block sitting in my Site.Master page:
<script runat="server" type="text/C#">
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
MasterModel = SiteMasterViewData.Get();
}
protected SiteMasterViewData MasterModel;
</script>
That Get() method is a static factory method tacked on to the View Data class. All of this is embarrassing but here we are.

.NET MVC instantiate controller inside another controller

Is it possible for an ASP.NET MVC controller to create a new instance of a different controller and effectively delegate resonsibility to that?
Let's say for example that I have two controllers in the /Controllers/ directory:
public class HomeController : Controller
{
public ActionResult Index()
{
var otherController = new OtherController();
return otherController.ShowNumberOfThings(100);
}
}
public class OtherController : Controller
{
public ActionResult ShowNumberOfThings(int index)
{
return View(index);
}
}
...and a View called Views/Other/ShowNumberOfThings.aspx:
<%# Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="ViewPage<int>" %>
Number of things: <%= Model.ToString() %>
When I hit the url:
http://localhost/Home/Index
I want to be presented with a page that reads:
"Number of things: 100"
I would like to be able to persist temporary data between controller redirections without being forced to use the session object (TempData[""] uses the session object for cross-controller redirections). My real world case has a complex object which needs passing (not just an int) so using a URL/Cookie is out of the question, and session state is a no-no.
In WebForms at least we could use Server.Transfer and maintain any state in the HttpContext.Items collection. In MVC the only option I can see is to call the controller method directly passing in required arguments.
At the moment it's having trouble trying to resolve the view folder as the "context" is still running under the HomeController.
I guess where I am going with this is trying to cludge ASP.NET MVC into acting like a FrontContoller.
Any ideas?
EDIT
In the end we had to serialise everything into a session and use that. A shame, but I have heard that MVC2 will support serialising objects into a ViewState.
If you want to be presented with "Number of things: 100" when you hit the Index action why not directly render the corresponding view:
public class HomeController : Controller
{
public ActionResult Index()
{
return View("~Views/Other/ShowNumberOfThings.aspx", 100);
}
}
I think it would be preferred to use.
return RedirectToAction("Controller", "Action")
However I'm guessing you want to maintain the Url Home/Index.
If you're looking at the FrontController pattern then you should investigate writing a Custom ControllerFactory which inherits from DefaultControllerFactory then Override the CreateController method.
You can register your factory using the code below.
protected void Application_Start()
{
ControllerBuilder.Current.SetControllerFactory(new MyCustomControllerFactory();
RegisterRoutes(RouteTable.Routes);
}
In the Controller factory you have access to the RequestContext so you can change the RouteData as needed and delegate to the correct controller.
You could of course just set a a Custom route for Home/Index which goes to OtherController.ShowNumberOfThings()
routes.MapRoute("Home", "Home/Index/{id}",
new {controller = "Other", action = "ShowNumberOfThings", id = 100});
a different approach would be the use of partial views
instead of ~Views/Other/ShowNumberOfThings.aspx
you could put your view in ~Views/shared/ShowNumberOfThings.ascx
have both views ~Views/Other/ShowNumberOfThings.aspx and ~Views/Home/Index.aspx implement the partial view
public class HomeController : Controller
{
public ActionResult Index()
{
return View(100);
}
}
public class OtherController : Controller
{
public ActionResult ShowNumberOfThings(int index)
{
return View(index);
}
}
and in both views implement the partial view
<% Html.RenderPartial("~Views/shared/ShowNumberOfThings.ascx", ViewData.Model); %>
you can change the int for any object that will be passed to the model
Another possibility (similar to partial views) is to use Html.RenderAction. This allows for different view model classes and separate controller methods.
<% Html.RenderAction("yourActionName", "yourControllerName", routeValues); %>

How to make ActionFilter on action method take precedence over same ActionFilter on controller

Since asp.net mvc has changed a lot since November, does anyone have a solution to this question:
Resolve FilterAttributes On Controller And Action
Phil said an ActionFilter on a controller is just shorthand for applying the attribute to all action methods of the controller, and it is true, if I put the same ActionFilter attribute on the controller and on an action method, it will run twice. But this doesn't seem like natural behavior since the compiler won't even let you put the same attribute directly on a method multiple times.
A filter can take precedence over another filter by specifing the Order property on each filter. For example...
[MyFilter(Order=2)]
public class MyController : Controller
{
[MyFilter(Order=1)]
public ActionResult MyAction()
{
//...
}
}
In this example the filter on the action method would execute before the filer on the controller.
HTH
I found one way to do it by "cheating" a bit with the ordering, inheritance and the AttributeUsage parameter
First, define your ActionFilter for the controller
[AttributeUsage(AttributeTargets.Class)]
public class FilterController : ActionFilterAttribute
{
public FilterController()
{
this.Order = 2;
}
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
if (!filterContext.HttpContext.Items.Contains("WeAlreadyWentThroughThis"))
{
// do our thing
filterContext.HttpContext.Items.Add("WeAlreadyWentThroughThis", "yep");
base.OnActionExecuted(filterContext);
}
}
}
Then inherit the class for your action attribute
[AttributeUsage(AttributeTargets.Method)]
public class FilterAction : FilterController
{
public FilterAction()
{
this.Order = 1;
}
}
It's far from perfect since you have to rely on HttpContext and two classes (though you could use namespaces to name both classes the same). But you get compiler-enforced check of the attribute scope for class or action and you won't forget an order parameter when typing the code.

How to create a strongly typed master page using a base controller in ASP.NET MVC

Following the NerdDinners example, I am interested in creating a strongly typed Master Page. In order to achieve this, I use a base controller which retrieves the data for the master page. All other controllers inherit this class. Similarly, I have ViewModels for the master page and any other views. The view ViewModel classes inherit from the master page's ViewModel.
Question
How should a child controller ensure that the master page's data is passed to the View without setting the properties of its ViewModel that pertain to the master page itself?
My the master page will display a number of buttons, which are determined in an XML file, hence the Buttons class that I am populating.
MasterPage ViewModel Code Snippet
using System.Collections.Generic;
namespace Site1.Models
{
public class MasterViewModel
{
public List<Button> Buttons{set; get;}
}
}
View ViewModel
namespace Site1.Models
{
public class View1ViewModel : MasterViewModel
{
public SomeDataClass SomeData { get; set; }
}
}
Base Controller
using System.Collections.Generic;
using System.Web.Mvc;
using Site1.Models;
namespace Site1.Controllers
{
public abstract class BaseController : Controller
{
protected MasterViewModel model = new MasterViewModel();
public BaseController()
{
model.Buttons = new List<Button>();
//populate the button classes (doesn't matter how)
PopulateButtons(model.Buttons);
}
}
}
View's controller:
using System.Web.Mvc;
namespace Site1.Controllers
{
public class View1Controller : BaseController
{
public ActionResult Index()
{
Models.View1ViewModel viewModel = new Models.View1ViewModel();
SomeDataClass viewData = new SomeDataClass()
//populate data class (doesn't matter how)
PopulateDataClass(viewData);
viewModel.SomeData = viewData;
//I WANT TO ELIMINATE THE FOLLOWING LINE!
viewModel.Buttons = model.Buttons;
return View("Index", viewModel);
}
}
}
The master page inherits System.Web.Mvc.ViewMasterPage<Site1.Models.MasterViewModel>.
The view inherits System.Web.Mvc.ViewMasterPage<Site1.Models.View1ViewModel>.
You could create an after action executed filter which looks for a model of that type and sets the properties accordingly, perhaps by calling a base controller function. You would then put the filter on the base class, and all actions would see it automatically.
The action filter attribute gets the controller's ViewModel, and passes it to the controller's SetModel function:
using System.Web.Mvc;
using Site1.Controllers;
namespace Site1.Models
{
public class MasterAttribute : ActionFilterAttribute
{
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
base.OnActionExecuted(filterContext);
MasterViewModel viewModel = (MasterViewModel)((ViewResultBase)filterContext.Result).ViewData.Model;
BaseController controller = (BaseController)filterContext.Controller;
controller.SetModel(viewModel);
}
}
}
This function is added to the BaseController:
public void SetModel(MasterViewModel childViewModel)
{
childViewModel.Buttons = model.Buttons;
}
Rather than creating an attribute, why not just override Controller.OnActionExecuted and put the initialization code there? Seems a bit simpler.

Resources