Kentico Xperience 13 for developers: Builders exercise issue

Nattawut Comlon asked on April 28, 2023 11:10

I've follow the steps in the Builders modules. And I've finished these exercises (before starting this Builders, I've also finished all the Essential + Prerequisites to other module)

  • Exercise: Enabling Page Builder
  • Exercise: Creating a page infrastructure
  • Exercise: Implementing a single-column section
  • Exercise: Implementing a parametrized two-column section
  • Exercise: Implementing a text widget

When I start testing the text widget by creating a landing page in the CMS, I got this error on the "Page" tab

InvalidOperationException: The model item passed into the ViewDataDictionary is of type 'MedioClinic.Models.PageViewModel`1[XperienceAdapter.Models.BasicPage]', but this ViewDataDictionary instance requires a model item of type 'MedioClinic.Models.UserMessage'.
Microsoft.AspNetCore.Mvc.ViewFeatures.ViewDataDictionary.EnsureCompatible(object value)
Microsoft.AspNetCore.Mvc.ViewFeatures.ViewDataDictionary..ctor(ViewDataDictionary source, object model, Type declaredModelType)
lambda_method1447(Closure , ViewDataDictionary )
Microsoft.AspNetCore.Mvc.Razor.RazorPagePropertyActivator.CreateViewDataDictionary(ViewContext context)
Microsoft.AspNetCore.Mvc.Razor.RazorPagePropertyActivator.Activate(object page, ViewContext context)
Microsoft.AspNetCore.Mvc.Razor.RazorPageActivator.Activate(IRazorPage page, ViewContext context)
Microsoft.AspNetCore.Mvc.Razor.RazorView.RenderPageCoreAsync(IRazorPage page, ViewContext context)
Microsoft.AspNetCore.Mvc.Razor.RazorView.RenderPageAsync(IRazorPage page, ViewContext context, bool invokeViewStarts)
Microsoft.AspNetCore.Mvc.Razor.RazorView.RenderAsync(ViewContext context)
Microsoft.AspNetCore.Mvc.TagHelpers.PartialTagHelper.RenderPartialViewAsync(TextWriter writer, object model, IView view)
Microsoft.AspNetCore.Mvc.TagHelpers.PartialTagHelper.ProcessAsync(TagHelperContext context, TagHelperOutput output)
Microsoft.AspNetCore.Razor.Runtime.TagHelpers.TagHelperRunner.<RunAsync>g__Awaited|0_0(Task task, TagHelperExecutionContext executionContext, int i, int count)
AspNetCoreGeneratedDocument.Views_LandingPage_Index.ExecuteAsync() in Index.cshtml
Microsoft.AspNetCore.Mvc.Razor.RazorView.RenderPageCoreAsync(IRazorPage page, ViewContext context)
Microsoft.AspNetCore.Mvc.Razor.RazorView.RenderPageAsync(IRazorPage page, ViewContext context, bool invokeViewStarts)
Microsoft.AspNetCore.Mvc.Razor.RazorView.RenderAsync(ViewContext context)
Microsoft.AspNetCore.Mvc.ViewFeatures.ViewExecutor.ExecuteAsync(ViewContext viewContext, string contentType, Nullable<int> statusCode)
Microsoft.AspNetCore.Mvc.ViewFeatures.ViewExecutor.ExecuteAsync(ViewContext viewContext, string contentType, Nullable<int> statusCode)
Microsoft.AspNetCore.Mvc.ViewFeatures.ViewExecutor.ExecuteAsync(ActionContext actionContext, IView view, ViewDataDictionary viewData, ITempDataDictionary tempData, string contentType, Nullable<int> statusCode)
Microsoft.AspNetCore.Mvc.ViewFeatures.ViewResultExecutor.ExecuteAsync(ActionContext context, ViewResult result)
Microsoft.AspNetCore.Mvc.ViewResult.ExecuteResultAsync(ActionContext context)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextResultFilterAsync>g__Awaited|30_0<TFilter, TFilterAsync>(ResourceInvoker invoker, Task lastTask, State next, Scope scope, object state, bool isCompleted)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResultExecutedContextSealed context)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.ResultNext<TFilter, TFilterAsync>(ref State next, ref Scope scope, ref object state, ref bool isCompleted)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.InvokeResultFilters()
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextResourceFilter>g__Awaited|25_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, object state, bool isCompleted)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResourceExecutedContextSealed context)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Next(ref State next, ref Scope scope, ref object state, ref bool isCompleted)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeFilterPipelineAsync>g__Awaited|20_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, object state, bool isCompleted)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope)
Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)
Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
MedioClinic.Middleware.CultureMiddleware+<>c__DisplayClass3_0+<<InvokeAsync>g__invokeImplementation|0>d.MoveNext() in CultureMiddleware.cs
+
                await _next(httpContext);
MedioClinic.Middleware.CultureMiddleware.InvokeAsync(HttpContext httpContext) in CultureMiddleware.cs
+
            await invokeImplementation();
Microsoft.AspNetCore.Session.SessionMiddleware.Invoke(HttpContext context)
Microsoft.AspNetCore.Session.SessionMiddleware.Invoke(HttpContext context)
Kentico.Content.Web.Mvc.ContentOutputMiddleware.InvokeAsync(HttpContext context)
Kentico.Web.Mvc.KenticoRequestLocalizationMiddleware.InvokeAsync(HttpContext context)
Kentico.Content.Web.Mvc.PageRedirectionContextMiddleware.InvokeAsync(HttpContext context)
Kentico.Web.Mvc.KenticoRequestEventsMiddleware.InvokeAsync(HttpContext context)
Microsoft.AspNetCore.Diagnostics.StatusCodePagesMiddleware.Invoke(HttpContext context)
Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)

Could you please help me of where should I start looking at? I've check in the code of the LandingPageController.cs and \MedioClinic\Views\LandingPage\Index.cshtml and \MedioClinic\Middleware\CultureMiddleware.cs are the same as the git repo (https://github.com/Kentico/xperience-training-13)

Thank you in advance

Correct Answer

Jan Lenoch answered on April 28, 2023 16:00

Hello,

Thanks for letting us know about that. The UserMessage submodel needs to be forwarded into the partial view.

In ~/MedioClinic/Views/LandingPage/Index.cshtml, find the line that invokes the _UserMessage.cshtml partial. In that HTML helper, add an attribute like so:

<partial name="~/Views/Shared/_UserMessage.cshtml" model="@Model.UserMessage" />

Now your _UserMessage.cshtml partial consumes just the inner part of your whole PageViewModel

0 votesVote for this answer Unmark Correct answer

Recent Answers


Nattawut Comlon answered on April 28, 2023 14:28

I tried checking out the git https://github.com/Kentico/xperience-training-13 on the specific branch 'xp-for-developers-builders'

set the connection string to point the the database that restore from CI files and build. Without changing anything, it works fine.

If I change the LandingPageController.cs by uncomment these lines

  • [assembly: RegisterPageRoute(CMS.DocumentEngine.Types.MedioClinic.LandingPage.CLASS_NAME, typeof(LandingPageController))]
  • var viewModel = GetPageViewModel(pageDataContext.Metadata, landingPage);
  • return View(viewModel);

and comment out

  • return new TemplateResult();

so the LandingPageController.cs looks like this

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

using Business.Models;

using CMS.DocumentEngine;

using Common.Configuration;

using Kentico.Content.Web.Mvc;
using Kentico.Content.Web.Mvc.Routing;
using Kentico.PageBuilder.Web.Mvc.PageTemplates;

using MedioClinic.Controllers;

using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;

using XperienceAdapter.Localization;
using XperienceAdapter.Models;
using XperienceAdapter.Repositories;

// Uncomment if this controller returns a view instead of TemplateResult.
[assembly: RegisterPageRoute(CMS.DocumentEngine.Types.MedioClinic.LandingPage.CLASS_NAME, typeof(LandingPageController))]
namespace MedioClinic.Controllers
{
    public class LandingPageController : BaseController
    {
        private readonly IPageDataContextRetriever _pageDataContextRetriever;

        private readonly IPageRepository<BasicPage, TreeNode> _landingPageRepository;

        public LandingPageController(
            ILogger<LandingPageController> logger,
            IOptionsMonitor<XperienceOptions> optionsMonitor,
            IStringLocalizer<SharedResource> stringLocalizer,
            IPageDataContextRetriever pageDataContextRetriever,
            IPageRepository<BasicPage, TreeNode> landingPageRepository)
            : base(logger, optionsMonitor, stringLocalizer)
        {
            _pageDataContextRetriever = pageDataContextRetriever ?? throw new ArgumentNullException(nameof(pageDataContextRetriever));
            _landingPageRepository = landingPageRepository ?? throw new ArgumentNullException(nameof(landingPageRepository));
        }

        public async Task<IActionResult> Index(CancellationToken cancellationToken)
        {
            if (_pageDataContextRetriever.TryRetrieve<CMS.DocumentEngine.Types.MedioClinic.LandingPage>(out var pageDataContext)
                && pageDataContext.Page != null)
            {
                var landingPagePath = pageDataContext.Page.NodeAliasPath;

                if (!string.IsNullOrEmpty(landingPagePath))
                {
                    var landingPage = (await _landingPageRepository.GetPagesInCurrentCultureAsync(
                        cancellationToken,
                        filter => filter
                            .Path(landingPagePath, PathTypeEnum.Single)
                            .TopN(1),
                        buildCacheAction: cache => cache
                            .Key($"{nameof(LandingPageController)}|Page|{landingPagePath}")
                            .Dependencies((_, builder) => builder
                                .PageType(CMS.DocumentEngine.Types.MedioClinic.LandingPage.CLASS_NAME)
                                .PagePath(landingPagePath, PathTypeEnum.Single))))
                            .FirstOrDefault();

                    if (landingPage != null)
                    {
                        // Implementation without page templates (begin)
                        var viewModel = GetPageViewModel(pageDataContext.Metadata, landingPage);

                        return View(viewModel);
                        // Implementation without page templates (end)

                        // Page template implementation (begin)
                        //return new TemplateResult();
                        // Page template implementation (end)
                    }

                }
            }

            return NotFound();
        }
    }
}

and then build and try to create a page of type "Landing page" again in the CMS, I'll see the error.

Is there something in the Exercise that I miss here?

0 votesVote for this answer Mark as a Correct answer

Nattawut Comlon answered on May 1, 2023 08:01

thank you Jan,

when I add that to the view it works, I think in the training, Builder module, in the Exercise: Creating a page infrastructure, slide #3 (Design the view), the code snippet should be updated too, I guess.

0 votesVote for this answer Mark as a Correct answer

   Please, sign in to be able to submit a new answer.