chum of chance
chum of chance

Reputation: 6290

Better way to generate global ViewData?

I have a section of my site that requires global data for a navigation drop down, right now I'm doing the following inside an attribute:

ViewData["projects"] = new[]
{
    new ProjectNav { Id = 1, Name = "Big project in New York" },
    new ProjectNav { Id = 2, Name = "Small project in New Jersey" },
    new ProjectNav { Id = 3, Name = "Big project in Florida" },
}

I then markup my controller methods like this:

[ProjectNav]
public ActionResult Index() 
{
     // strongly typed view returned here
}

And in my view I would do something like this:

<% foreach (ProjectNav project in (IEnumerable<ProjectNav>)ViewData["projects"]) 
{ %>
// Enumerate here
<% } %>

This works, but is there anyway to do this in a more strongly typed way? The only thing I can think of is to create a Dto with the ProjectNav stuff as a member, but then you're creating a separate Dto for each of the controller methods and this is definitely not DRY. Is there a better way to go about this that I'm just missing?

Upvotes: 3

Views: 396

Answers (5)

Darin Dimitrov
Darin Dimitrov

Reputation: 1038820

Using child actions is a far better solution. So you would start by defining a controller:

public class ProjectsController: Controller
{
    public ActionResult Index()
    {
        var projects = new[]
        {
            new ProjectNav { Id = 1, Name = "Big project in New York" },
            new ProjectNav { Id = 2, Name = "Small project in New Jersey" },
            new ProjectNav { Id = 3, Name = "Big project in Florida" },
        };
        return View(projects);
    }
}

And then have a strongly typed partial which will render them (~/Views/Projects/Index.ascx) and finally include this in your main view:

<%= Html.Action("index", "projects") %>

This way you don't need to decorate all your controller actions with attributes. Just include the action you need using the Html.Action helper.

Upvotes: 0

Roger Lipscombe
Roger Lipscombe

Reputation: 91835

Have a NavigationController, and use RenderAction or RenderPartial in each view (or in the master page).

Upvotes: 2

Ian Mercer
Ian Mercer

Reputation: 39277

Create a base ViewModel class with a Projects property on it.

Create an OnResultExecuted method in your ActionFilter. In the OnResultExecuted method access the model (filterContext.Controller.ViewData.Model). Check that it derives from your base ViewModel class. If it does, cast it and add your Projects data to it.

Now you have a strongly typed Projects property on your view model and it's set using an ActionFilter.

Alternatively, you could also use an interface instead of requiring a single base ViewModel class ... if (model is IDisplayProjects) ....

Upvotes: 1

FruitDealer
FruitDealer

Reputation: 91

I would put the model into Session and initalise that session attribute when the user first logs in. Or if there is no login then initialise it on the actual page. If your global data mean every page then put it in master page.

So something like this in the masterpage (where GetProjects is a static function):

<% if(Session["projects"] == null)
{
   Session["projects"] = DataManager.GetProjects();
}%>

<% foreach (ProjectNav project in (IEnumerable<ProjectNav>)Session["projects"]) 
{ %>
     // Enumerate here
<% } %>

Not the most strongly typed method of doing it, but very simple and efficient. So with this way you do not have to worry about it at the controller level.

Upvotes: 0

Tee Wu
Tee Wu

Reputation: 579

Global data for navigation drop down which's mean it's part of header or footer right? My approach is prefer to declare it and load it in the master page or, more code, put in the Constructor of each Controller with a specific function.

Upvotes: 0

Related Questions