I've written previously about the best ways to use Kentico Xperience's Page Builder. We should be using the powerful Page Builder as the place in a...
For further actions, you may consider blocking this person and/or reporting abuse
Great Article @seangwright , I am working on an implementation based on this right now. However I am running into the same issue @chrisjwarnes has with not being able to use the base view model for use on the layout page. I thought alternatively I could use ViewBag as a workaround however this doesn't work either. I did read your article on setting values in a scoped service, but it seems overkill just to add a Css Class to the body tag. Any ideas on a simple solution for this?
Hey, thanks for reading!
I agree, a scoped service just for adding a CSS class to the
<body>
would feel like a lot of work for little gain.However, I end up using many scoped services to take lots of data/content from the "Page" and place it in the Layout. This can include JavaScript and CSS, but I also use it to customize
<meta>
tags, modify the structure (or existence) of navigation, and many other (usually client specific) scenarios.I don't think I ever got around to finishing the open source version of this code, but I used our variation of it heavily internally when working at WiredViews. That is a good example of how we used scoped services to transfer information.
ViewBag
and@section { ... }
are the simple solutions, but I never use theViewBag
because it's "stringly" typed - convenient for demos but not a good foundation for large sites built with Xperience.I also never use
@section { ... }
because it can't be used from View Components, Partials, or Tag helpers. Again, it's fine for demo sites but not the kinds of sites I've worked on.So, even if I wasn't using PTVC, I'd still be using scoped services to transfer information from the route/view rendering to the Layout. This has worked well for me on every K12 MVC/KX13 project I've worked on.
I think my issue with this approach is that it appears to work for simple cases, but not when things get more complex, one of the advantages of a controller is that you can create a view model with a base view model which has fields for use on the layout page. this can be important for adding information such as open graph tags or for when unique fall backs need to be managed for title/meta descriptions, or the header may need to be altered based on the current page for example.
As great as View Components are, they are not really a substitute for these kinds of scenarios.
Using Kentico in MVC5 currently has a different approach, I can still get all the advantages mentioned above (with the exception of all the dotnet core view component goodness) but I also have control of the output via a controller should I need this.
it seems a pity that kentico have followed this approach with dotnet core, whilst its possible you may have a simple enough project with no need for a controller, sometimes there are other complexities that need to be considered, the option to do either would be the perfect solution. Allowing developers to choose how they architecture a system is important, unless there are critical reasons why a particular approach will not work.
This is an excellent article about the advantages of page templates showing when to use fields on the template vs the page (which is super important) but it doesn't argue away the need for the MVC pattern. A combination of both would mean there is no need for any code smell anywhere. doesn't the fact that we have to write an article explaining how to avoid a code smell show there is a big potential problem here?
Keep up the good work Sean, I enjoy your articles.
Hey Chris, thanks for reading and thanks for the thoughtful reply!
I agree that for simply cases, Page Templates + View Components (or Child Actions in MVC5) is a great approach, and for advanced use cases you will want to use MVC Controllers.
This can also be accomplished by setting values in a scoped service in the View Component used with a Page Template. The scoped service's value can then be read in the
_Layout.cshtml
because the Page's "View" is always rendered before the "Layout" is.We can still use Controllers in ASP.NET Core for Page Templates if we want. In my examples I show how to route directly to the Template, but this is not required.
Unless I misunderstand, the only thing we can't do with Page Templates, that you would like to see, is create a custom View Model class that is made available to the Page Template Razor View.
This is why I use View Components to do the heavy lifting of creating a custom View Model.
I see Page Templates as a 2 phase process:
ComponentViewModel
and route to / render the Page Template View.I'm glad that Xperience handles all of step 1 and step 2 still allows me to customize everything I need.
Maybe? The code smell isn't using MVC - it's putting design customization in Page Type fields (however a code smell isn't necessarily an indication of a bad design or a problem). I think we should try to avoid combining content with design, but sometimes it's going to be the best choice 🤷♂️.
PTVC is a pattern that has helped me avoid that code smell where possible vs MVC, which only exists in ASP.NET because Ruby on Rails was popular back in the late 2000s. It's a pattern that the framework gives to Xperience to perform presentation logic and render HTML, but it's not the only one, and it wasn't designed to benefit content management in a CMS/DXP.
I recommend developers default to using PTVC, but I never stated that MVC is useless. I explicitly called out that it should be used if custom routing is required. I have not run into a situation using PTVC where I wasn't able to fully customize the rendered page, so I don't think that MVC brings me any additional 'power' here.
If you have use cases where MVC Controllers allow you to do things that PTVC doesn't, then that is a great reason to not use PTVC.
This post absolutely has a click-bait title 😁, but it's also a 12 minute read (according to DEV), so hopefully I've added context to my claims.
In summary, I believe that with ASP.NET Core Kentico Xperience 13+ sites, developers should default to the PTVC pattern over MVC. If MVC brings something they need, then use that instead. All of this is only about presentation logic anyway - there's plenty of room left in an application for other design patterns that let developers build their custom solutions as needed.
Again, thanks for the thoughtful comment - I want the Xperience community to think about all these things, share ideas, and pick the solutions that work best for their projects. This discussion helps everyone!
Hi Sean,
thanks for the quick reply, you say "We can still use Controllers in ASP.NET Core for Page Templates if we want." as far as I can see this is not the case for core, but you can do that if you build an MVC5 project.
if seems to be the difference to how the RegisterPageTemplate attribute is setup, in MVC5 you can point it to a controller which inherits from PageTemplateController, but this step doesn't appear to have been carried across to core, all you can do is specify a view.
MVC 5
[assembly: RegisterPageTemplate("CompanyName.HomeTemplate", typeof(HomeTemplateController), "Homepage template.")]
CORE
[assembly: RegisterPageTemplate("CompanyName.HomeTemplate", "Homepage template", "~/HomeTemplate/_HomeTemplate.cshtml")]
but i get your point about Scoped service, that could well be a way to approach the problem.
Ahhh, I understand what you're saying now 😁.
We can create a Controller that handles rendering Pages of a specific type that also use Page Templates in ASP.NET Core and we can access the Page that is being handled by that Controller action method via the
IPageDataContextRetriever
service. We can do all kinds of custom logic here, populating scoped services, even preparing a View Model that we can retrieve in the Page Template View (while I don't recommend that approach, the DancingGoatCore app does this with theArticlesController
).However this Controller is the one that returns the
TemplateResult
, which in MVC5 is 1 of 2 Controllers that can be defined.You are correct 👍👍 - in ASP.NET Core we cannot create a custom Controller that prepares the View Model and selects the View to be rendered, like we can in MVC5.
Having worked a lot with Page Templates in MVC5, I always found this 'double Controller' pattern to be confusing and made for a lot of boilerplate, and I've found its removal in ASP.NET Core isn't a limitation - it just moves the process of preparing the View Model from a Controller into a View Component (or multiple View Components).
I'm definitely biased, having worked with ASP.NET Core since the pre-1.0 days, so I'm probably promoting the use of things like View Components and Tag Helpers (and dependency injection) more than developers that are new to ASP.NET Core are used to. I also think these tools can be leveraged to great effect in Kentico Xperience.
A lot of things we do in Kentico 12 MVC sites are not because they are great solutions, but because the Kentico team was working against the constraints of a framework initially designed in 2008-2009.
I feel like in ASP.NET Core, a lot of those constraints are gone and better patterns are available and should be explored or encouraged. Or, if they aren't available yet, the architecture is set up in a way to make them available in the future if the community is vocal about needing them (example: Form Builder Section Properties coming in KX 13 Refresh 2 at the end of the month).
I may have missed it, is there a sample app that shows a full implementation of PTVC?
There's no link to external code, but there is also nothing special with the code. If you can create a Page Template and a View Component, then you've followed the pattern.
Was there something specific you were looking for an example of?
Hi, Sean. Great article, thank you for taking the time to share it! Would you be willing to share a sample project (GitHub?) of the PTVC pattern being implemented in a generic Kentico 13 .Net Core Dancing Goat project in the interest of helping less experienced Kentico developers (like myself) to better understand the principle through a working example? I like the idea of PTVC, but just need a little more clarity on it that a sample project could provide. I've been evaluating Kentico 13 (.Net Core) for a couple months trying to pull as much from the official docs and the Dancing Goat sample project as possible. The problem I've run into now is that when I create a custom page template that needs business logic, I was guided into injecting a service class into the page template by Kentico docs. Now I'm trying to figure out how to shift to PTVC. Ultimately, I'm working toward custom business logic along with support for the page builder and have not been successful in achieving that. PTVC seems like it might be a great way to get there without resorting to custom routing and MVC controller-based logic. Thanks! -Dave
Dave,
Glad to hear you've been evaluating Kentico Xperience! It's a great platform!
The Dancing Goat project is really meant as a feature demo, and not necessarily an architectural/developer guidance project.
Check out this blog post to understand some of the nuance of the different project examples available out there:
devnet.kentico.com/articles/kentic...
If you are looking for a kitchen sink approach that will get you up and running fast (or you don't feel comfortable designing your own patterns and project internals), check out the Kentico Xperience 13 Baseline, maintained by my fellow MVP Trevor:
devnet.kentico.com/articles/get-st...
I don't have any good examples online at the moment of PTVC, but I could definitely provide an example (through a GitHub repo or Gist) that should point you in the right direction.
The key to PTVC is to take what you would have done in a Controller Action and move it to a View Component.
Then, in your Page Template, call that View Component to perform the business logic.
If the View Component needs context about the current Page to query for the right data from the database you can either pass that as a parameter to the View Component from the Page Template using pattern matching to get the correct type:
Or you can use the
IPageDataContextRetriever
in your View Component to get the current Page's information.Does this help?
That's very helpful, Sean. Thank you for the response with the links and clarifications. I'll give it a go! -Dave
@seangwright I was able to get it working that way. The only thing I haven't been successful at is passing the page into the component as a parameter in the vc: tag... something I'm missing with the "pattern matching" I think. But I was able to use the alternative approach you suggested and get the page context from within the view component itself by way of the IPageDataContextRetriever. Thanks again for sharing your insight on the PTVC pattern and helping me along the way to implementing it. Cheers! -Dave
Dave,
The key to the pattern matching is to 'exit early' if the type isn't correct. That was C# will know that in the rest of the View, the type is what you expect
No, nothing specific, just the whole thing in general.