The model item passed into the dictionary is of type
'Web.Models.SearchVm', but this dictionary requires a model item of
type 'Umbraco.Web.Models.RenderModel'.
Declaring that same layout for other templates which don't have custom models (and don't have custom controllers) work just fine. The _Layout.cshtml calls Umbraco macros and makes use of the Umbraco Model.
I am assuming I am getting this error because the _layout.cshtml is expecting RenderModel but it is only getting my custom model? Am I doing something wrong, is there a way around this?
Yes your _layout.cshtml should also have that custom model. I solved this by having a base model and a model which inherits from the base model. Than give your _layout.cshtml the base model (with the properties the master needs) and your own view the model which inherits from the base model.
Yeah, I had figured out that I need to use a custom model on my _layout.cshtml as well. Unfortunately, this means that I have to hijack every single doc type/template on the website. But what I have done is made all my custom models inherit from a single model (called it BaseVm which actually doesn't have anything on it) and had my _layout.cshtml use this BaseVm.
A team mate of mine found an even better way to do this. In your _layout.cshtml, you don't need to inherit from your own model just have it inherit from 'object' like so:
Umbraco.Web.Mvc.UmbracoViewPage< object >
This means you don't need to hijack every route/controller.
Yeah, I am also finding it frustrating. Can you change the inherits on your partial to 'Umbraco.Web.Mvc.UmbracoViewPage<object>' instead of the RenderModel?
Well I'm saying that, but that's mostly because I haven't found a decent way yet to get the current IPublishedNode. You have Node.GetCurrent() but that returns a Node. I could do "new DynamicNode(Node.GetCurrent())", that works but that still doesn't give me an IPublishedNode.
Also, in all fairness, it's a really ugly solution. Sure it works, but damn...
It is not any uglier than everything using the RenderModel. You are just changing the model that you are passing into all of your views from RenderModel to Umbraco.Web.Mvc.UmbracoViewPage<object>.
As for getting to the Dynamic Node, I am not totally sure as I don't ever really use the Dynamic Node because of the loss if intellisense and I have noticed a real performance hit with dynamic node when traversing the content tree.
What if your own model inherits from the Umbraco RenderModel? Your views which need a custom controller can get their own model and the views which inherit from Umbraco.Web.Mvc.UmbracoTemplatePage without controller probably also still work because they get the correct model.
You can now easily override the RenderModel behavior, but now you run into issues with the culture not being set on the PublishedContentRequest. Please go up-vote this issue http://issues.umbraco.org/issue/U4-1333
The underlying issue here is that your View has model of type 'X' and your Layout has model of type 'Y'. This problem will occur for any MVC site where you want a strongly typed layout page, it is not Umbraco specific. In normal circumstances your Layout will be defined as:
@model dynamic
which means that your layout will work when any type of model is defined in your view. That said, I realize that in many circumstances you'd want a strongly typed layout page as well. There's a few ways to handle this but here's a typical example...
First, you'll normally be sharing a master layout page for everything that does not actually use the model for anything, for that layout page just use @model dynamic. Then for specific layout pages that require a model, just nest this one to your master layout and then create a custom base model. For example, we'll create a base model:
public class BasePageModel {
public string PageTitle {get;set;}
}
I'm running in to the same problem, has anyone found a good way around this that would let me use my custom model and access the current umbraco content (Model.Content etc..)
I'm not sure what the problem people are having is ? If you have a layout page that has a specific model, then your view page that is using that layout page must have the same model or a sub class of that model. My notes above with the BasePageModel example should explain it, no ?
If you want your own custom model AND have access to the current render model, then your custom model would need to expose the render model. You can do this by hijacking the route for the document type in which you want to render views for. If you take the above example with a BasePageModel, you could just add a property to it of type RenderModel which would expose the wrapped umbraco model as well.
It sounds reasonable enough to add a RenderModel property to my custom model if I need to access that. But to do this, you say that my master Layout view will no longer use UmbracoTemplatePage as its model, but a dynamic instead. So yes, this allows my custom controllers to pass whatever I want to my custom views, but the main Layout page now needs to change in a way that no longer works on "normal" umbraco pages which are still passing RenderModel as the model.
I think what I need to do is either hope for a CustomModel<T> property on the RenderModel that I can shove my custom model into, or else shove it into the ViewBag. Then I won't need to mess with the master layout view. Or, I could bite the bullet and truly create a separate "CustomerMaster.cshtml" like you showed in your example, which will duplicate the exact same markup as my regular _Layout.cshtml used by the rest of the site, but using slightly different notation for getting at the common things like the navigation and footer. (This duplication of master layouts is what I have been trying to avoid. Although perhaps I am misunderstanding things, which is quite likely.)
Just remember this is not an Umbraco issue, this is just how ASP.Net MVC works, you will have the same design restrictions with a normal MVC website. I think as best practice, if possible you'd not use a model in your layouts and they'd normally just be layouts which can expose sections so you can use your view's model inside of those layout sections. Or if you have modules in your layout you could use ChildActions to lookup a different model.
In 6.1 we have the ability to set a default/custom RenderMvcController, the docs are here:
If you did that, it means you could potentially have a base class for your layouts and you'd ensure that all of your views would be at least a sub class of this base class. Then in your custom controller you'd ensure that the model returned is this base class, then if you wanted specific sub classes for differnet views you could hijack those routes.
I think that the 'issue' is that (by default) Umbraco exposes all content through Model.Content. That then quickly causes one to think that 'their' model needs to be based on RenderModel because 'how can we otherwise get to Model.Content'.
It took me a bit of decompiling (ok, resharper did that ;) to find out that the Model.Content property is simply based off @Umbraco.AssignedContentItem.
I'm not sure (haven't looked in a while) if the help files mention @Umbraco.AssignedContentItem, but that does allow someone to just use strongly-typed models without issues.
So using: @inherits Umbraco.Web.Mvc.UmbracoViewPage<object>
Solves this issue with the knowledge that you can access content through @Umbraco.AssignedContentItem .
AssignedContentItem is not always the current page though so keep that in mind, it is contextual. It is the currently assigned IPublishedContent to the current UmbracoHelper.
The 'purist' way to do this is as I mention, implement your own controllers and assign your own models from there.
IPublishedContent is really the underlying model for the page so you can easily add that as a property to your base model for all of your views and ensure you return an instance of that base model from your controllers.
Well if I decompile UmbracoHelper and look at the constructor, it says this: this._currentPage = this._umbracoContext.PublishedContentRequest.PublishedContent;
If I also look at 'CurrentPage' property of RenderMvcController, it says this: return this.PublishedContentRequest.PublishedContent;
Either I'm wrong, or they both look at the same source.
I'm just trying to understand in what scenario the AssignedContentItem wouldn't be the current page, so I can prepare for possible issues in the future :)
If you create your own UmbracoHelper and pass it an instance of IPublishedContent, it is contextual to the content you've passed it. I'm just saying if that the AssignedContentItem doesn't mean "Current Page" it means it's the assigned item to that specific UmbracoHelper. There's 2 overloaded ctors.
A good example of this is when you have a partial view that inherits from UmbracoTemplatePage and you pass the partial view a custom IPublishedContent model like
The AssignedContentItem inside of that partial view will *not* be the current page being rendered it will be the first child of the current page being rendered.
I feel like i'm so close to getting what I want working, but not quite there yet. I hope what I describe makes sense and is possible to do:
Imagine that I have just a regular controller with full custom routing (does not inherit from RenderMvcController). There is thus no doctype to hijack, and I have my path(s) in umbracoReservedPaths so that Umbraco lets it through. I really want to be able to use the same master layout as the rest of the site, so I realize it's easiest if I return a RenderModel. (Like I said yesterday, I'm fine now with my custom model going into the ViewBag.)
But the thing I'm wondering about is how to construct my own RenderModel if one isn't handed to me already? I am fine with using the site homepage as the current page to base it on, since this is what the master layout would need for generating the navigation, sidebar, and footer, etc.
Is this a bad idea to try this? Are there any other ways I can do this? Apologies if I am failing to grasp the concept...
UmbracoHelper UH = new UmbracoHelper(UmbracoContext.Current);
IPublishedContent C = UH.TypedContent(1234);
RenderModel RM = new RenderModel(C); // the ctor throws NullReferenceException, probably because UmbracoContext.Current.PublishedContentRequest is null
I believe what you want is a custom ContentFinder. Here's one I'm using where I just pass in the root node as a dummy RenderModel for some templates that I want to render as raw html.
However I'm still a bit confused. Where would I get a PublishedContentRequest to pass into your method you just showed? The problem I have still seems to be that with my custom routing, and not inheriting my controllers from RenderMvcController, is that I don't have one of these, nor can I construct one. (The ctor is marked internal.) I did check for UmbracoContext.Current.PublishedContentRequest but is null inside my controller's action method. Likely because my custom routing, as I learned in another thread today, means that all of the normal "umbraco-stuff" that normally populates these things gets bypassed.
public class HomeModel : Umbraco.Web.Models.RenderModel
{
public HomeModel()
: base(UmbracoContext.Current.PublishedContentRequest.PublishedContent) {
}
}
That way your model has a parameterless constructor which is easier to use :-).
The error makes sense, but I think it's going to trip up a lot of people when they write their first RenderMvcController. I have created a pull request to update the custom controller documentation to warn people about this potential issue.
Using RenderMvcController
Umbraco 4.11.4
I have followed this reference: https://github.com/Shandem/Umbraco4Docs/blob/4.8.0/Documentation/Reference/Mvc/custom-controllers.md
I have a controller which extends Umbraco.Web.Mvc.RenderMvcController.
This controller returns a custom model for me:
Everything works great until I declare a layout for my template. So this doesn't work:
However, this does:
It gives me this error:
The model item passed into the dictionary is of type 'Web.Models.SearchVm', but this dictionary requires a model item of type 'Umbraco.Web.Models.RenderModel'.
Also seen on this thread post: http://our.umbraco.org/forum/developers/api-questions/36602-Pass-strongly-type-model-in-razor-view
Declaring that same layout for other templates which don't have custom models (and don't have custom controllers) work just fine. The _Layout.cshtml calls Umbraco macros and makes use of the Umbraco Model.
I am assuming I am getting this error because the _layout.cshtml is expecting RenderModel but it is only getting my custom model? Am I doing something wrong, is there a way around this?
Hi, are these partial views? and if so make sure in your controller you are passing back a partial view. Hope this helps. Charlie
Yes your _layout.cshtml should also have that custom model. I solved this by having a base model and a model which inherits from the base model. Than give your _layout.cshtml the base model (with the properties the master needs) and your own view the model which inherits from the base model.
Jeroen
Yeah, I had figured out that I need to use a custom model on my _layout.cshtml as well. Unfortunately, this means that I have to hijack every single doc type/template on the website. But what I have done is made all my custom models inherit from a single model (called it BaseVm which actually doesn't have anything on it) and had my _layout.cshtml use this BaseVm.
A team mate of mine found an even better way to do this. In your _layout.cshtml, you don't need to inherit from your own model just have it inherit from 'object' like so:
Umbraco.Web.Mvc.UmbracoViewPage< object >
This means you don't need to hijack every route/controller.
I was about to ask a question about this very same subject.
In our _layout.cshtml we do this:
@Html.Partial("Partials/HeaderMenu", Model)
Which works great for 'normal pages', but if I hijack one of my pages to return a custom model, the above line doesn't work anymore.
I get 'why' it doesn't work, I just don't like it :)
The "Umbraco.Web.Mvc.UmbracoViewPage<object>" still isn't a good solution, since the partial still expects a RenderModel.
A shame really, we're currently disliking this part so much that we're rendering all 'custom models' as SurfaceControllers.
Yeah, I am also finding it frustrating. Can you change the inherits on your partial to 'Umbraco.Web.Mvc.UmbracoViewPage<object>' instead of the RenderModel?
I can't.
Well I'm saying that, but that's mostly because I haven't found a decent way yet to get the current IPublishedNode. You have Node.GetCurrent() but that returns a Node. I could do "new DynamicNode(Node.GetCurrent())", that works but that still doesn't give me an IPublishedNode.
Also, in all fairness, it's a really ugly solution. Sure it works, but damn...
It is not any uglier than everything using the RenderModel. You are just changing the model that you are passing into all of your views from RenderModel to Umbraco.Web.Mvc.UmbracoViewPage<object>.
As for getting to the Dynamic Node, I am not totally sure as I don't ever really use the Dynamic Node because of the loss if intellisense and I have noticed a real performance hit with dynamic node when traversing the content tree.
What if your own model inherits from the Umbraco RenderModel? Your views which need a custom controller can get their own model and the views which inherit from Umbraco.Web.Mvc.UmbracoTemplatePage without controller probably also still work because they get the correct model.
Jeroen
Got the same problem, RenderModel needs a constructor with no arguments.. so i can't inherit from it.
This is a really strange that Umbraco doesn't saw this coming, all the examples are with viewmodels.
You can now easily override the RenderModel behavior, but now you run into issues with the culture not being set on the PublishedContentRequest. Please go up-vote this issue http://issues.umbraco.org/issue/U4-1333
Hi All,
The underlying issue here is that your View has model of type 'X' and your Layout has model of type 'Y'. This problem will occur for any MVC site where you want a strongly typed layout page, it is not Umbraco specific. In normal circumstances your Layout will be defined as:
@model dynamic
which means that your layout will work when any type of model is defined in your view. That said, I realize that in many circumstances you'd want a strongly typed layout page as well. There's a few ways to handle this but here's a typical example...
First, you'll normally be sharing a master layout page for everything that does not actually use the model for anything, for that layout page just use @model dynamic. Then for specific layout pages that require a model, just nest this one to your master layout and then create a custom base model. For example, we'll create a base model:
So your layout pages might now look like:
CustomMaster.cshtml
CustomLayout.cshtml
Then you will want a custom model for your normal view. This can just inherit from your base model like:
So then your view could look like this:
Shannon, thanks for chiming in. I think we'd gotten to that point, but getting here just exposed another issue .
yeah, looking in to that now, though doesn't seem that related to this issue IMO.
Hi
I'm running in to the same problem, has anyone found a good way around this that would let me use my custom model and access the current umbraco content (Model.Content etc..)
Regards
Hi Matt,
Did you ever figure this out? This would really be helpful to be able to do.
Thanks!
Hi
I think I just ended up using @Html.Action to render a strongly typed view within the master layout or a partial view.
I can't remember what I was trying to do now, but I can do everything I need to with that combination.
Cheers
Matt
I'm not sure what the problem people are having is ? If you have a layout page that has a specific model, then your view page that is using that layout page must have the same model or a sub class of that model. My notes above with the BasePageModel example should explain it, no ?
If you want your own custom model AND have access to the current render model, then your custom model would need to expose the render model. You can do this by hijacking the route for the document type in which you want to render views for. If you take the above example with a BasePageModel, you could just add a property to it of type RenderModel which would expose the wrapped umbraco model as well.
Hi Shannon, thank you for the reply.
It sounds reasonable enough to add a RenderModel property to my custom model if I need to access that. But to do this, you say that my master Layout view will no longer use UmbracoTemplatePage as its model, but a dynamic instead. So yes, this allows my custom controllers to pass whatever I want to my custom views, but the main Layout page now needs to change in a way that no longer works on "normal" umbraco pages which are still passing RenderModel as the model.
I think what I need to do is either hope for a CustomModel<T> property on the RenderModel that I can shove my custom model into, or else shove it into the ViewBag. Then I won't need to mess with the master layout view. Or, I could bite the bullet and truly create a separate "CustomerMaster.cshtml" like you showed in your example, which will duplicate the exact same markup as my regular _Layout.cshtml used by the rest of the site, but using slightly different notation for getting at the common things like the navigation and footer. (This duplication of master layouts is what I have been trying to avoid. Although perhaps I am misunderstanding things, which is quite likely.)
Thank you for any continued advice and guidance!
Just remember this is not an Umbraco issue, this is just how ASP.Net MVC works, you will have the same design restrictions with a normal MVC website. I think as best practice, if possible you'd not use a model in your layouts and they'd normally just be layouts which can expose sections so you can use your view's model inside of those layout sections. Or if you have modules in your layout you could use ChildActions to lookup a different model.
In 6.1 we have the ability to set a default/custom RenderMvcController, the docs are here:
http://our.umbraco.org/documentation/Reference/Mvc/custom-controllers#Changethedefaultcontroller
If you did that, it means you could potentially have a base class for your layouts and you'd ensure that all of your views would be at least a sub class of this base class. Then in your custom controller you'd ensure that the model returned is this base class, then if you wanted specific sub classes for differnet views you could hijack those routes.
Hi Shannon,
I think that the 'issue' is that (by default) Umbraco exposes all content through Model.Content. That then quickly causes one to think that 'their' model needs to be based on RenderModel because 'how can we otherwise get to Model.Content'.
It took me a bit of decompiling (ok, resharper did that ;) to find out that the Model.Content property is simply based off @Umbraco.AssignedContentItem.
I'm not sure (haven't looked in a while) if the help files mention @Umbraco.AssignedContentItem, but that does allow someone to just use strongly-typed models without issues.
So using:
@inherits Umbraco.Web.Mvc.UmbracoViewPage<object>
Solves this issue with the knowledge that you can access content through @Umbraco.AssignedContentItem .
Regards, J.
AssignedContentItem is not always the current page though so keep that in mind, it is contextual. It is the currently assigned IPublishedContent to the current UmbracoHelper.
The 'purist' way to do this is as I mention, implement your own controllers and assign your own models from there.
IPublishedContent is really the underlying model for the page so you can easily add that as a property to your base model for all of your views and ensure you return an instance of that base model from your controllers.
Well if I decompile UmbracoHelper and look at the constructor, it says this:
this._currentPage = this._umbracoContext.PublishedContentRequest.PublishedContent;
If I also look at 'CurrentPage' property of RenderMvcController, it says this:
return this.PublishedContentRequest.PublishedContent;
Either I'm wrong, or they both look at the same source.
I'm just trying to understand in what scenario the AssignedContentItem wouldn't be the current page, so I can prepare for possible issues in the future :)
If you create your own UmbracoHelper and pass it an instance of IPublishedContent, it is contextual to the content you've passed it. I'm just saying if that the AssignedContentItem doesn't mean "Current Page" it means it's the assigned item to that specific UmbracoHelper. There's 2 overloaded ctors.
A good example of this is when you have a partial view that inherits from UmbracoTemplatePage and you pass the partial view a custom IPublishedContent model like
@Partial("MyPartial", Model.Content.Children.First())
The AssignedContentItem inside of that partial view will *not* be the current page being rendered it will be the first child of the current page being rendered.
On the first page I suggested to let your own model inhertit from the Umbraco RenderModel: http://our.umbraco.org/forum/templating/templates-and-document-types/38311-Using-RenderMvcController?p=0#comment140290. That is still an option and you can also inherit from it like this:
You can create you own model like this in the Controller:
And your view can look like this:
And even if you masterpage inherits like this it will still work.
This is just an example, but I think that what Shannon is suggestion here is better: http://our.umbraco.org/forum/templating/templates-and-document-types/38311-Using-RenderMvcController?p=1#comment152959. You could also combine it and have custom models on all pages which inherit from the RenderModel.
Jeroen
I feel like i'm so close to getting what I want working, but not quite there yet. I hope what I describe makes sense and is possible to do:
Imagine that I have just a regular controller with full custom routing (does not inherit from RenderMvcController). There is thus no doctype to hijack, and I have my path(s) in umbracoReservedPaths so that Umbraco lets it through. I really want to be able to use the same master layout as the rest of the site, so I realize it's easiest if I return a RenderModel. (Like I said yesterday, I'm fine now with my custom model going into the ViewBag.)
But the thing I'm wondering about is how to construct my own RenderModel if one isn't handed to me already? I am fine with using the site homepage as the current page to base it on, since this is what the master layout would need for generating the navigation, sidebar, and footer, etc.
Is this a bad idea to try this? Are there any other ways I can do this? Apologies if I am failing to grasp the concept...
Thank you!
I believe what you want is a custom ContentFinder. Here's one I'm using where I just pass in the root node as a dummy RenderModel for some templates that I want to render as raw html.
public bool TryFindContent(PublishedContentRequest docRequest) {
IPublishedContent node = null;
Regex externalPattern = new Regex("external/(.*)");
if (externalPattern.IsMatch(HttpContext.Current.Request.Path) ) {
string templateName = externalPattern.Match(HttpContext.Current.Request.Path).Groups[1].ToString();
node = docRequest.RoutingContext.UmbracoContext.ContentCache.GetByRoute("/");
docRequest.PublishedContent = node;
docRequest.TrySetTemplate(templateName);
} return node != null;
}
Hi Randy, thank you for this.
However I'm still a bit confused. Where would I get a PublishedContentRequest to pass into your method you just showed? The problem I have still seems to be that with my custom routing, and not inheriting my controllers from RenderMvcController, is that I don't have one of these, nor can I construct one. (The ctor is marked internal.) I did check for UmbracoContext.Current.PublishedContentRequest but is null inside my controller's action method. Likely because my custom routing, as I learned in another thread today, means that all of the normal "umbraco-stuff" that normally populates these things gets bypassed.
Thank you!
Some more info about this post.
You can also create your HomeModel like this:
That way your model has a parameterless constructor which is easier to use :-).
Jeroen
The error makes sense, but I think it's going to trip up a lot of people when they write their first RenderMvcController. I have created a pull request to update the custom controller documentation to warn people about this potential issue.
One solution I found was to use an empty RenderModel class as the model, and include my actual data model as a different property in the ViewBag.
Weird, and it does have some reprocussions down the line, but they're not insubmountable, and it works.
Regards
Duncan
is working on a reply...