Thank you for this great package! We are experimenting a bit with it at Perplex and I'm running into an issue with the Preview mode (i.e. the rendering in Umbraco) in combination with custom view models.
We almost always create custom controllers for every Doctype so we can use a custom model for the view which contains any logic, rather than having all this logic in the view itself. In order to do that, we hijack the Umbraco routes to inject our view model. For example, our Home controller will generally look something like this:
public class HomeController : SurfaceController, IRenderController
{
public ActionResult Index()
{
return View("Home", new HomeViewModel());
}
}
This raises an exception in App_Plugins\DoctypeGridEditor\Render\DocTypeGridEditorPreviewer.cshtml:
Cannot bind source type DTGE_Test.Models.Views.HomeViewModel to model type Umbraco.Core.Models.IPublishedContent.
I understand why this is happening, considering the Preview mode is basically "hijacking" the Index method of our Home controller (when the Grid property is on that doctype) to render its preview, and only changes the .ViewName property to the DTGEPreviewer.cshtml but not the Model (which is read-only). In the DocTypeGridEditorPreviewer the following line is at the top:
In our case, we pass in a custom model which does not directly bind to IPublishedContent hence it fails. Since you do not actually use the Model in that view, a simple fix would be to change it to something like this:
@inherits Umbraco.Web.Mvc.UmbracoViewPage<object>
In that case it will work with default Umbraco (where the model is a RenderModel) and with custom viewmodels. I don't think this breaks anything.
Please let me know what you think about that change and if there is another way to do this. It's totally possible I was doing it wrong to begin with.
I am also using a custom DocTypeGridEditorSurfaceController for the grid editor itself, but this does not seem to influence the outcome. I also get the error without this (i.e., when DTGE simply renders views/partials/[doctypeAlias].cshtml)
Thank you for your quick reply and suggestion. I just tried the regular way of route-hijacking (as far as I know), but this results in a similar issue.
That is, if I use the following definition of HomeController the exact same exception is raised:
public class HomeController : RenderMvcController
{
public override ActionResult Index(RenderModel m)
{
return CurrentTemplate(new HomeViewModel());
}
}
Could you perhaps show an example of on of your controllers with some custom view model so I can see if it works here? Thanks!
How does you HomeViewModel look ? I haven't used custom models in my templates before ? Can a master view handle this, because I think you will have different viewmodels per content type?
It appears that if the custom View Model implements the IRenderModel interface it will work, as Umbraco appears to work some magic in that case and pass the IPublishedContent properly to the preview View.
That is, when the View Model is specified like below it will work, and in our case the View Models indeed include the IPublishedContent so implementation is trivial, but I still think it would be nice if this would work with any View Model, not requiring them to implement IRenderModel.
public class HomeViewModel : IRenderModel
{
// We have this one available through a generic base class,
// put directly here for simplicity
public Home Content { get; }
// Explicitly implement IRenderModel here.
IPublishedContent IRenderModel.Content => Content;
}
I suppose if it works for your custom view models, those also implement IRenderModel (or perhaps IPublishedContent?). Otherwise I would expect a similar exception to be raised.
Ah exactly, that makes sense that those issues would not arise in that case. We are also thinking about perhaps just extending ModelsBuilder classes and use those as our View Models instead of creating view models that wrap a ModelsBuilder model, but there's some discussion as to whether that's always desirable. For example, if we want to render 2 totally different templates for the same Models Builder class, we may want to have 2 view models with different logic, but both containing that 1 models builder class with all Umbraco data. Anyway, that's a different topic :)
Thanks for your input on this topic! I do believe making DTGE work for regular view models (i.e., not requiring them to implement IRenderModel / IPublishedContent) would be nice, but let's see what the authors think about this.
Route Hijacking + DTGE Preview mode issues
Hi,
Thank you for this great package! We are experimenting a bit with it at Perplex and I'm running into an issue with the Preview mode (i.e. the rendering in Umbraco) in combination with custom view models.
We almost always create custom controllers for every Doctype so we can use a custom model for the view which contains any logic, rather than having all this logic in the view itself. In order to do that, we hijack the Umbraco routes to inject our view model. For example, our Home controller will generally look something like this:
This raises an exception in App_Plugins\DoctypeGridEditor\Render\DocTypeGridEditorPreviewer.cshtml:
I understand why this is happening, considering the Preview mode is basically "hijacking" the Index method of our Home controller (when the Grid property is on that doctype) to render its preview, and only changes the .ViewName property to the DTGEPreviewer.cshtml but not the Model (which is read-only). In the DocTypeGridEditorPreviewer the following line is at the top:
In our case, we pass in a custom model which does not directly bind to IPublishedContent hence it fails. Since you do not actually use the Model in that view, a simple fix would be to change it to something like this:
In that case it will work with default Umbraco (where the model is a RenderModel) and with custom viewmodels. I don't think this breaks anything.
Please let me know what you think about that change and if there is another way to do this. It's totally possible I was doing it wrong to begin with.
I am also using a custom DocTypeGridEditorSurfaceController for the grid editor itself, but this does not seem to influence the outcome. I also get the error without this (i.e., when DTGE simply renders views/partials/[doctypeAlias].cshtml)
Kind regards,
Daniël
Hi Daniël,
We are also using DTGE in combination with Models builder and Route Hijacking. We don't experience the issue that you are describing.
I think the problem occcurs because you are doing the route hijacking with a surface controller. We had some issues in the past with that as well.
According to Shannon in this post this should not be done : https://our.umbraco.org/forum/umbraco-7/using-umbraco-7/73791-getting-an-error-with-custom-routes-could-not-find-a-surface-controller-route-in-the-routetable-for-controller-name-authorization#comment-236516
Dave
Hi Dave,
Thank you for your quick reply and suggestion. I just tried the regular way of route-hijacking (as far as I know), but this results in a similar issue.
That is, if I use the following definition of HomeController the exact same exception is raised:
Could you perhaps show an example of on of your controllers with some custom view model so I can see if it works here? Thanks!
Daniël
Hi Daniël,
How does you HomeViewModel look ? I haven't used custom models in my templates before ? Can a master view handle this, because I think you will have different viewmodels per content type?
Dave
Hi Dave,
It appears that if the custom View Model implements the IRenderModel interface it will work, as Umbraco appears to work some magic in that case and pass the IPublishedContent properly to the preview View.
That is, when the View Model is specified like below it will work, and in our case the View Models indeed include the IPublishedContent so implementation is trivial, but I still think it would be nice if this would work with any View Model, not requiring them to implement IRenderModel.
I suppose if it works for your custom view models, those also implement IRenderModel (or perhaps IPublishedContent?). Otherwise I would expect a similar exception to be raised.
Daniël
Hi Daniël,
We use the models generated by models builder (some times extended with custom code) in our templates. And these models implement IPublishedContent.
Dave
Hi Dave,
Ah exactly, that makes sense that those issues would not arise in that case. We are also thinking about perhaps just extending ModelsBuilder classes and use those as our View Models instead of creating view models that wrap a ModelsBuilder model, but there's some discussion as to whether that's always desirable. For example, if we want to render 2 totally different templates for the same Models Builder class, we may want to have 2 view models with different logic, but both containing that 1 models builder class with all Umbraco data. Anyway, that's a different topic :)
Thanks for your input on this topic! I do believe making DTGE work for regular view models (i.e., not requiring them to implement IRenderModel / IPublishedContent) would be nice, but let's see what the authors think about this.
Daniël
is working on a reply...