Parent layout wants a RenderModel, but I want to use a custom model...
Ok, so I think I finally get how to create custom controllers and use route hijacking enough to tell someone else how to get a basic one going. ;)
I've created a minimal model, controller, and partial view. I have my document type and template, and it all generally seems to work nicely together. My model gets set up in the hijacked Index method in my controller and passed to my view, and I can display the properties I set. I was so excited to finally get that much working.
Where I start to have trouble now is when I use another layout in my template. On our site, we have a "Master" layout, a "Submaster" layout, and then most of the other templates use Submaster.
When my custom model/controller (what is the right terminology? haha) has Layout = null, it's unstyled, but it shows up on the page.
When I use Submaster as my layout, I get this error:
The model item passed into the dictionary is of type 'Models.MembersPageModel', but this dictionary requires a model item of type 'Umbraco.Web.Models.RenderModel'.
What I think that means is that because SubMaster inherits UmbracoTemplatePage, it's what wants the RenderModel, even though I'm not referencing any model in SubMaster and my MembersPage template is inheriting Umbraco.Web.Mvc.UmbracoViewPage
I've looked at changing things around so my model inherits from RenderModel, but then I get an error about my model not having a constructor that takes 0 arguments, but then I'm not sure which arguments I'd need to pass in.
So - how do I nest layouts that use two different types? Changing what SubMaster inherits isn't going to be ideal, because we're creating several "plugins" like this with different models.
You will need the model under Subtemplate to inherit from the subtemplate model.
Remember that both templates get rendered. The subtemplate parent will need its model to render the razor on its page, then the inherited template (news, etc) from subtemplate will need its specific model. However, that all needs to be wrapped up in one model.
Does that make sense?
A good example of this is the Hybrid Framework project. However, one caveat is the most recent versions of that are more "Starter Kit" than "custom routing example" so it may be hard to work through exactly what their doing.
I can post some simpler examples if you need more help.
Jason, I'd downloaded and messed around in Hybrid Framework, and I wasn't able to make much sense out of it in order to know what direction I should go in. I was looking for "custom routing example", and I couldn't extrapolate one. ;)
If you have some simpler examples to share, that would be awesome.
In the end, once I have this all working, I think I need to write a blog post about how to do it from start to end. Heh.
Just wanted to follow up with what I've done to make this thing work.
So in the end, since I'm inheriting a site that already had a bunch of work done on it, I didn't want to change what was already there if I could help it. There is one master template, a submaster, and then the layouts below those. So three layers of templates to inherit things from. I decided to stick with RenderModel.
I changed my model to inherit from RenderModel, and the main error I was getting was that RenderModel doesn't have a constructor with 0 parameters. So I'm just sending the model's Content property into my constructor when I declare my model. I also had to add a constructor to my model that accepts an IPublishedContent argument.
To break it down:
SubMaster inherits from UmbracoTemplatePage still. No change there.
Child template inherits UmbracoViewPage
Child template uses SubMaster as the Layout
MembersPageModel inherits from RenderModel
Added a constructor for MembersPageModel to accept one IPublishedContent argument, pretty much exactly what Rusty wrote.
I appreciate the help, guys. You helped me through a dark time in my new job, and I feel like I've learned a lot. Hopefully I can help someone else who's coming to this stuff new as well. :)
It feels a bit odd to me to be mixing dynamics and typed content: UmbracoTypePage (dynamic) / UmbracoViewPage (typed) and myself would tend to pick one of the other.
I've seen good setups with UmbracoTemplatePage views and UmbracoViewPage
Mixing the two in your views may be a bit like manipulating things to wind up with what Umbraco does for you - but then again you may be trying to do this for some reason understandable from the information in this thread.
Is it that the site you inherited has typed models and you're trying to get your head around how they are working or are you trying to add something new and follow the existing pattern?
A month later, and I'm still playing with this. :)
We eventually figured out our initial problem, but now we're running into similar ones because now we're creating views that need to display values after submitting a form.
Rusty, to answer your question at the end, I'm trying to add something new and follow the pattern. Before I got involved, there was none of this stuff. Umbraco was just allowed to do what it does, and they had been adding macros via the editor. Really simple stuff.
But now we're creating these tools that are almost like plugins, and we're following the paradigm of creating the Document Type to be the Controller name, and using the Template as the View that calls the partial view to pull it under the Umbraco umbrella.
And now my issue is that I get my view to render nicely, but when I submit the form, the model state is wiped out. I know exactly why:
Html.RenderPartial("~/Views/Partials/MeldCalculator.cshtml", new MeldCalculatorModel() {});
When I do things like that, obviously each time the MeldCalculator partial is called, it's going to be getting a brand new model.
If I take out the new MeldCalculatorModel call, the page won't load and I get the error that says I'm passing a RenderModel but it wants a MeldCalculatorModel.
Every piece of what I'm trying to do works just fine until I want to send my new model values back to my view. If I return the partial after my form submit action, I get my view with the new values populated, it's just not part of the site (clearly).
When I use CurrentUmbracoPage() or View("MeldCalculator", _model), the model is nulled out by the new MeldCalculatorModel in my RenderPartial call.
I've got to be missing something quite elementary with this. I just can't figure out what it is, nor can my teammates!
Edit: I tried some shenanigans, and I found out that even when I pass my MeldCalculatorModel to the View, the type of Model is RenderModel, even before I send in the default new MeldCalculatorModel. Why would it be RenderModel if I'm passing it in explicitly?
Thanks, Rusty. I'll need to check out Action to see how better to make it work. :) I read up on it, and I think I see how it would be used, but I still am not sure how to get away with the model difference, because I still need to know whether or not the model I'm passing is the first time the page loads or a result of the form submit. There are a couple things I'm thinking about to try. Nearly there!
Parent layout wants a RenderModel, but I want to use a custom model...
Ok, so I think I finally get how to create custom controllers and use route hijacking enough to tell someone else how to get a basic one going. ;)
I've created a minimal model, controller, and partial view. I have my document type and template, and it all generally seems to work nicely together. My model gets set up in the hijacked Index method in my controller and passed to my view, and I can display the properties I set. I was so excited to finally get that much working.
Where I start to have trouble now is when I use another layout in my template. On our site, we have a "Master" layout, a "Submaster" layout, and then most of the other templates use Submaster.
When my custom model/controller (what is the right terminology? haha) has Layout = null, it's unstyled, but it shows up on the page.
When I use Submaster as my layout, I get this error: The model item passed into the dictionary is of type 'Models.MembersPageModel', but this dictionary requires a model item of type 'Umbraco.Web.Models.RenderModel'.
What I think that means is that because SubMaster inherits UmbracoTemplatePage, it's what wants the RenderModel, even though I'm not referencing any model in SubMaster and my MembersPage template is inheriting Umbraco.Web.Mvc.UmbracoViewPage
I've looked at changing things around so my model inherits from RenderModel, but then I get an error about my model not having a constructor that takes 0 arguments, but then I'm not sure which arguments I'd need to pass in.
So - how do I nest layouts that use two different types? Changing what SubMaster inherits isn't going to be ideal, because we're creating several "plugins" like this with different models.
What am I missing?
You will need the model under Subtemplate to inherit from the subtemplate model.
Remember that both templates get rendered. The subtemplate parent will need its model to render the razor on its page, then the inherited template (news, etc) from subtemplate will need its specific model. However, that all needs to be wrapped up in one model.
Does that make sense?
A good example of this is the Hybrid Framework project. However, one caveat is the most recent versions of that are more "Starter Kit" than "custom routing example" so it may be hard to work through exactly what their doing.
I can post some simpler examples if you need more help.
Thanks, Jason
Jason, I'd downloaded and messed around in Hybrid Framework, and I wasn't able to make much sense out of it in order to know what direction I should go in. I was looking for "custom routing example", and I couldn't extrapolate one. ;)
If you have some simpler examples to share, that would be awesome.
In the end, once I have this all working, I think I need to write a blog post about how to do it from start to end. Heh.
Thanks! -j
In your SubMaster try inheriting from UmbracoViewPage
The constructor for these would be
Where the IPublishedContent would come from the RenderModel model
Just wanted to follow up with what I've done to make this thing work.
So in the end, since I'm inheriting a site that already had a bunch of work done on it, I didn't want to change what was already there if I could help it. There is one master template, a submaster, and then the layouts below those. So three layers of templates to inherit things from. I decided to stick with RenderModel.
I changed my model to inherit from RenderModel, and the main error I was getting was that RenderModel doesn't have a constructor with 0 parameters. So I'm just sending the model's Content property into my constructor when I declare my model. I also had to add a constructor to my model that accepts an IPublishedContent argument.
To break it down:
I appreciate the help, guys. You helped me through a dark time in my new job, and I feel like I've learned a lot. Hopefully I can help someone else who's coming to this stuff new as well. :)
-j
It feels a bit odd to me to be mixing dynamics and typed content: UmbracoTypePage (dynamic) / UmbracoViewPage (typed) and myself would tend to pick one of the other.
I've seen good setups with UmbracoTemplatePage views and UmbracoViewPage
Mixing the two in your views may be a bit like manipulating things to wind up with what Umbraco does for you - but then again you may be trying to do this for some reason understandable from the information in this thread.
Is it that the site you inherited has typed models and you're trying to get your head around how they are working or are you trying to add something new and follow the existing pattern?
A month later, and I'm still playing with this. :)
We eventually figured out our initial problem, but now we're running into similar ones because now we're creating views that need to display values after submitting a form.
Rusty, to answer your question at the end, I'm trying to add something new and follow the pattern. Before I got involved, there was none of this stuff. Umbraco was just allowed to do what it does, and they had been adding macros via the editor. Really simple stuff.
But now we're creating these tools that are almost like plugins, and we're following the paradigm of creating the Document Type to be the Controller name, and using the Template as the View that calls the partial view to pull it under the Umbraco umbrella.
And now my issue is that I get my view to render nicely, but when I submit the form, the model state is wiped out. I know exactly why:
When I do things like that, obviously each time the MeldCalculator partial is called, it's going to be getting a brand new model.
If I take out the new MeldCalculatorModel call, the page won't load and I get the error that says I'm passing a RenderModel but it wants a MeldCalculatorModel.
Every piece of what I'm trying to do works just fine until I want to send my new model values back to my view. If I return the partial after my form submit action, I get my view with the new values populated, it's just not part of the site (clearly).
When I use CurrentUmbracoPage() or View("MeldCalculator", _model), the model is nulled out by the new MeldCalculatorModel in my RenderPartial call.
I've got to be missing something quite elementary with this. I just can't figure out what it is, nor can my teammates!
Edit: I tried some shenanigans, and I found out that even when I pass my MeldCalculatorModel to the View, the type of Model is RenderModel, even before I send in the default new MeldCalculatorModel. Why would it be RenderModel if I'm passing it in explicitly?
-j
Hi Jason,
I think you might want to try to use an Html.Action to render your form rather than RenderPartial.
Thanks, Rusty. I'll need to check out Action to see how better to make it work. :) I read up on it, and I think I see how it would be used, but I still am not sure how to get away with the model difference, because I still need to know whether or not the model I'm passing is the first time the page loads or a result of the form submit. There are a couple things I'm thinking about to try. Nearly there!
-j
Generally you would have two methods in your surface controller, one to render the form and then one to handled the form submit.
The "SignInForm" partial would look something like:
is working on a reply...