A workaround for missing ViewData in Sitecore MVC
Passing data between Sitecore renderings can get tricky.
Sending messages between sibling renderings can lead us to worry about the order in which they render, and you may end up with renderings tightly coupled to other renderings. Jeremy Davis discusses ways to switch the order of rendering execution on his blog here: https://jermdavis.wordpress.com/2016/04/04/getting-mvc-components-to-communicate/
#
Pass data down, not acrossMy preferred approach is for renderings to be as isolated as possible and not need to talk to siblings. In a regular MVC site, we would instantiate a ViewModel
, and pass it down to any child (or partial) views as needed. If a child view doesn’t change this ViewModel
at all, we don’t have to worry about order of execution or changes of state.
In Sitecore, we can achieve this by wrapping child renderings in a parent Controller Rendering. This Controller Rendering creates and prepares the ViewModel
, and then passes it down to one or more child renderings.
#
Let’s recap on the main points here:- Our parent Controller Rendering creates and prepares a
ViewModel
. This parent specifies a view, which contains one or more placeholders. - This
ViewModel
is passed along to any child renderings currently attached to the placeholders. - During execution, child renderings do not modify the
ViewModel
. We may even consider theViewModel
immutable while rendering takes place.
Sitecore has a peculiarity here which makes our job difficult. Each rendering gets a new instance of ViewData – explained by Kern Herskind Nightingale here: http://stackoverflow.com/a/35210022/638064.
This puts a stop to us using ViewData
to pass our ViewModel
down from the parent rendering to child renderings.
#
The WorkaroundThere’s a way you can ensure that ViewData
is correctly passed down from parent to child renderings. Let’s go through how this is possible.
- In your top level controller, create a
ViewModel
, which will be passed down to all child renderings.
- Add it to the
ViewData
collection in the currentViewContext
- In each child rendering, fetch the
ViewModel
and add it to the localViewData
for the current rendering (which will be empty at this point). View Renderings will do this step for you, so you don’t need to do anything special there
Et voila! You now have access to the same ViewModel
for each of your child renderings.
#
Making it betterMVC offers us even better tools to remove code duplication. If you have a lot of child renderings needing access to your shared ViewModel
, adding the code in step 3 will happen a lot. Let’s refactor that to an filter attribute.
Now, we just need to add this attribute to any Action Methods who may want to access shared ViewData
from higher up in the stack
There we go. I’m sure Sitecore will amend their implementation at some point, but until then, we have an immutable, single direction ViewData
flow.