Copied to clipboard

Flag this post as spam?

This post will be reported to the moderators as potential spam to be looked at


  • Declercq Pascal 8 posts 77 karma points
    Dec 03, 2021 @ 14:39
    Declercq Pascal
    0

    Surfacecontroller its view and their model

    After searching for a long time 'Cannot bind source type... to model type Umbraco.Core.Models.PublishedContent.IPublishedContent', I thought I could share what I found out about it and how to solve (or rather workaround).

    Context: a surfacecontroller with an action returning a view with a model als parameter:

    public IActionResult MyAction()
        {
            MyModel myModel=....;
    
            return View(myModel);
        }
    

    This gave me the IPublishedContent error.

    I found somewhere on the forum to inherit the view from UmbracoPageView < MyModel > , but that didn't work.

    Reason: my view inherits from a master layout which seams to also expect by default an IPublishedContent as Model (even if you don't use there). If I use the view without the master layout, then it works (but looks ugly of course).

    Solution: I don't pass my model to the view as a parameter, but instead I put it in the ViewBag.

    If anyone has a better solution, please stand up!

  • Marc Goodson 1718 posts 11411 karma points MVP 7x c-trib
    Dec 05, 2021 @ 13:10
    Marc Goodson
    1

    Hi Declercq

    A SurfaceController is usually used to handle the posting of a form in Umbraco, (or in previous versions, rendering a Child Action within a template with a PartialView).

    In V9 and .net 5, ChildActions have gone, and been replaced by ViewComponents...

    but SurfaceControllers still exist in V9 to handle those form submissions.

    If you want to work with Umbraco following an MVC pattern, eg a request comes in for a page, it's diverted or hijacked via a Controller and then a View/Template is returned (that inherits a master layout). Then you can use a techique called RouteHijacking:

    https://our.umbraco.com/Documentation/Reference/Routing/custom-controllers

    Which involves creating a controller that inherits from RenderController instead of SurfaceController, and gives you full control over the request, the model that is built up and the view/template that is returned...

    Is that more like what you are after?

    regards

    Marc

  • Declercq Pascal 8 posts 77 karma points
    Dec 05, 2021 @ 20:12
    Declercq Pascal
    0

    Hi Marc

    thanks for trying to clarify, the documentation is not very clear on how choosing the proper controller type.

    I am indeed looking to use a controller that is independant from umbraco content or in other words a conventional mvc controller 'hosted' in or under umbraco.

    I've tried the custom controllers concept as you suggested, but I can't make it working. I can compile and run the site but when a navigate to my custom controller I get a 404 error.

    I can think of 2 possible reasons:

    1. the path I'm using: /MyRender => is this correct or is there a specific path to use like with surface controllers?
    2. for some parameters in the constructor, I have different possibiblities proposed by intellisence for the namespace to use, maybe I'm choosing the wrong ones.

    Error I'm getting: Page not found No umbraco document matches the URL '/MyRender'.

  • Marc Goodson 1718 posts 11411 karma points MVP 7x c-trib
    Dec 06, 2021 @ 07:24
    Marc Goodson
    1

    Hi Declercq

    Yes, it's the names that always confuse me!

    There are

    • SurfaceControllers - (autorouted) - designed to handle form posts on the 'surface' of an existing controller

    • RenderControllers - (autorouted) - a convention based method to hijack and extend Umbraco's implementation of the MVC pattern for a content request.

    • UmbracoPageController - (manually routed) - used to manually route a request through Umbraco, with an associated IPublishedContent item to provide an Umbraco Context to the request.

    Possibly best explanation is in the github repo conversation for the PR that implemented these controllers in V9:

    https://github.com/umbraco/Umbraco-CMS/pull/9821

    So it depends on what you are trying to do, the simplest way is to hijack a request, if a content item has Document Type: LandingPage, then a RenderController called LandingPageController, then all requests to that page will be sent via your LandingPageController. If your route is separate to Umbraco Content then you can manually map a route using an UmbracoPageController see:

    https://our.umbraco.com/Documentation/Reference/Routing/custom-routes#custom-routes-within-the-umbraco-pipeline

    Hope that helps!

    regards

    Marc

  • Declercq Pascal 8 posts 77 karma points
    Dec 08, 2021 @ 12:13
    Declercq Pascal
    0

    Hi Marc,

    Still not out of the woods ... I checked the links you mentioned, but couldn't make something working out of it. Probably missing some small detail.

    I gathered some possiblities in an overview but unfortunatly can't paste it here. This is a picture of it, maybe you can complete it of correct the errors: enter image description here

  • Marc Goodson 1718 posts 11411 karma points MVP 7x c-trib
    Dec 09, 2021 @ 10:17
    Marc Goodson
    0

    Hi Declerca

    The routing convention for a RenderController is interesting to describe!

    Essentially if you create a controller called LandingPageController that inherits from RenderController, that controller will be hit for any request that goes to an Umbraco content item based on a DocumentType with alias LandingPage.

    So if you have a site with a Homepage, and under that a page based on the Landing Page document called 'Our Services' then the request for /our-services will be routed via the LandingPageController.

    If you have another LandingPage called 'Our Services' then the request to /our-services would be automatically routed via the LandingPageController...

    If you had a Contact Us page, using a ContactPage document type that woudn't be routed via the LandingPageController but you could create a ContactPageController to 'hijack' that request...

    With the UmbracoPageController this isn't tied to the structure and the urls of the site in the same way that the RenderController is...

    ... this is much more like a standard MVC controller...

    In that you have to provide the routing pattern for it: (see https://our.umbraco.com/Documentation/Implementation/Custom-Routing/#custom-mvc-routes)

    But here's the subtle thing, if you aim to return an Umbraco Template (View) from the controller, and that View uses the same master layout view, you might have code in that layout that calculates the Site Navigation or a Breadcrumb, based on the context of the 'current page'...

    ... Therefore this kind of route NEEDS an Umbraco Published Content item as Context... but if your MVC Controller returns views that don't share layout and partial views with your Umbraco site, then yes, you can just map a standard MVC Controller...

    So if I had a Products Database outside of Umbraco, and each product needs to have a page on the website, but it's details are not in Umbraco, then I might create a 'ProductsSection' node in Umbraco, and hijack requests to this page using a RenderController to build up a viewmodel with a list of Paged Products, drawn from the database.

    So it might have a url of /our-products/

    and then I might map a route to a VirtualProductController inheriting from UmbracoPageController

    endpoints.MapControllerRoute( "My virtual product controller", "/our-products/product-{id}, new {Controller = "VirtualProductController", Action = "Index", Id={id}}) .ForUmbracoPage(MyProductsSectionNode);

    now when a request came in for /our-products/product-123 it would route to my custom controller, I could read the 123, go off to the database pull back the details from the db and build a viewmodel that then has the context of the Our Products node for any code in the site layout and common partials, so it would be 'as if' the product was in Umbraco...

    It's not the only way to do that scenario, but hopefully gives you a scent as to what the difference is between the RenderController (gets it's umbraco context from the route it hijackcs) and the UmbracoPageController (you have to give it a context).

    As the context you give it just needs to implement IPublishedContent, you don't have to give a context that is in Umbraco, you can give it any model that implements IPublishedContent, it's just most often easier to create a dummy node in the Umbraco content tree to do so!

    regards

    marc

Please Sign in or register to post replies

Write your reply to:

Draft