0

I had previously been using TempData to set things like a "body css class", which then pages and partials could override.

I've now moved over to ViewData after realising that TempData uses sessions, however setting a ViewDataDictionary's value inside the partial basically gets ignored when it gets back up to the page - and never propagates up the layout hierarchy.

I've tried calling "RenderPartial" from inside my Page, and using the override which allows me to specify the ViewData to pass over:

Layout:

Page:

@{
    var cssClass = (ViewData["something"] != null) ? ViewData["something"].ToString() : "";
}

<body class="@cssClass">

Page:

@{
    ViewData["something"] = "blah";
    Html.RenderPartial("MyPartial", ViewData)
}

Partial:

@{
    ViewData["something"] += " blah";
}

When I debug inside my Layout, I can see that ViewData["something"] is "blah" - the partial didn't set it correctly.

When I was using TempData this would work ok. I don't really want to go back to using TempData because of ASP session locking and its effect on concurrent requests.

Has anybody got this to work before? Am I overlooking something?

Thanks

Steven Sproat
  • 4,398
  • 4
  • 27
  • 40
  • If the issue you're having is something to do with the partial view not rendering properly, [read this](http://stackoverflow.com/questions/5248183/html-partial-vs-html-renderpartial-html-action-vs-html-renderaction). Otherwise we may need more specific information about the issue you're facing. Also, you shouldn't be using ViewData or TempData to persist view specific information. You should be using view models. – Inspector Squirrel Mar 03 '15 at 17:47
  • Everything renders ok. I'm using this from inside a View, to be shown by the View. e.g. setting particular CSS classes for particular pages. The issue is ViewData being set my the partial doesn't propagate up – Steven Sproat Mar 03 '15 at 18:02
  • You can still use HttpContext.Current.Items["something"] – rism Mar 04 '15 at 06:17

2 Answers2

3

Each view has its own ViewData. By default, Razor fills views further down the hierarchy with the ViewData of their parents, but this is one-way. For example, if you do something like:

SomeView.cshmtl

@{ ViewData["foo"] = "bar"; }
@Html.Partial("_SomePartial")

SomePartial.cshtml

@ViewData["foo"]

The result of the partial will be "bar" as you'd expect. But, if you did something like:

SomeView.cshtml

@Html.Partial("_SomePartial")
@ViewData["foo"]

_SomePartial.cshtml

@{ ViewData["foo"] = "bar"; }

Nothing would be printed, as ViewData["foo"] doesn't exist in the view data for the parent view, only in the view data for the partial.

Chris Pratt
  • 232,153
  • 36
  • 385
  • 444
3

So this is a one way propagation as you've discovered if you want to set data in partial and return to parent view you can use the HttpContext which is not very cool but it works:

Parent:

@{
    HttpContext.Current.Items["Something"] = "blah";
    Html.RenderPartial("_Partial");
}
@HttpContext.Current.Items["Something"];

Partial:

@{
    HttpContext.Current.Items["Something"] = "somethingelse";
}

Outputs "somethingelse" in the parent.

Alternatively and the way it's typically done, if you're bypassing TempData, is via the parent model or a temp model:

Model:

public class MyTempModel
{
    public string Something { get; set; }
}

Parent:

@{
    var tmod = new MyTemModel()
    {
       Something = "blah"
    };
    Html.RenderPartial("_Partial", tmod);
}
@tmod.Something;

Partial:

@model MyTempModel
@{
    tMod.Something = "somethingelse";
}

Outputs "somethingelse" in the parent.

rism
  • 11,932
  • 16
  • 76
  • 116
  • Brilliant - many thanks for the poke to use HttpContext.Items - we use this for "per request" caching currently, but I didn't think of using it to solve this problem! This worked a charm - cheers. – Steven Sproat Mar 04 '15 at 10:04