Copied to clipboard

Flag this post as spam?

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


  • Peter van den Dungen 66 posts 365 karma points
    May 19, 2022 @ 10:53
    Peter van den Dungen
    0

    Set ViewData in Default Controller constructor

    I have a default controller where all requests pass through. This part is working fine.

    I used this documentation: https://our.umbraco.com/documentation/Reference/Routing/custom-controllers

    The final goal is; To enrich the view model passed to the template with additional properties (from other published content items or outside Umbraco)

    // Configure Umbraco Render Controller Type
    services.Configure<UmbracoRenderingDefaultsOptions>(c =>
     {
         c.DefaultControllerType = typeof(DefaultController);
     );
    

    In the constructor I want to set some ViewData, in order to read this data in f.e the Layout view.

    private async Task SetLayoutViewModelAsync()
        {
            if (ViewData[LayoutViewModelKey] == null)
            {
                var publishedContent = TryGeyPublishedContent();
    
                if (publishedContent != null)
                {
                    ViewData[LayoutViewModelKey] = await _layoutFactory?.ConstructAsync(publishedContent);
                }
            }
        }
    

    When I set a breakpoint and call the LoginController, I see that the code in the DefaultController is executing and seems good to me. But in the LoginView its null.

    When I override the Index method in Login - and DefaultController, its working fine, but I want this to work everywhere by using the base constructor.

  • Dave Woestenborghs 3504 posts 12135 karma points MVP 9x admin c-trib
    May 19, 2022 @ 12:07
    Dave Woestenborghs
    0

    Hi Peter,

    Can you post some more code ?

    Dave

  • Peter van den Dungen 66 posts 365 karma points
    May 19, 2022 @ 12:30
    Peter van den Dungen
    0

    Hi Dave,

    Here I have my default controller.

     public DefaultController(
            ILogger<DefaultController> logger,
            IPublishedContentQuery publishedContentQuery,
            ICompositeViewEngine compositeViewEngine, 
            IUmbracoContextAccessor umbracoContextAccessor,
            ILayoutFactory layoutFactory) 
            : base(logger, compositeViewEngine, umbracoContextAccessor)
        {
            _logger = logger;
            _publishedContentQuery = publishedContentQuery;
            _layoutFactory = layoutFactory;
    
            SetLayoutViewModelAsync();
        }
    
        //public override IActionResult Index()
        //{
        //    SetLayoutViewModelAsync();
    
        //    return CurrentTemplate(CurrentPage);
        //}
    
        private async Task SetLayoutViewModelAsync()
        {
            if (ViewData[LayoutViewModelKey] == null)
            {
                var publishedContent = TryGetPublishedContent();
    
                if (publishedContent != null)
                {
                    ViewData[LayoutViewModelKey] = await _layoutFactory?.ConstructAsync(publishedContent);
                }
            }
        }
    

    In the constructor there is the SetLayoutViewModelAsync() method, this is responsible for nothing more then collecting usefull properties,settings etc.

    public async Task<Layout> ConstructAsync(IPublishedContent content)
        {
            var website = content.GetHome(); //Umbraco's root node
            var currentUser = _backofficeUserAccessor.BackofficeUser;
            var currentMember = await _memberManager.GetCurrentMemberAsync();
    
            return new Layout
            {
                Content = content,
                User = currentUser.IsAuthenticated 
                ? new Models.Structs.BackOfficeUser()
                    {
                        Id = currentUser.FindFirstValue(ClaimTypes.NameIdentifier).TryParse(null),
                        UserName = currentUser?.Name ?? string.Empty,
                        Roles = _backofficeUserAccessor.GetAllRoles(),
                        IsAuthenticated = currentUser.IsAuthenticated,
                        IsAdmin = _backofficeUserAccessor.UserIsAdmin(),
                    } : null,
                Member = currentMember,
            };
        }
    

    Now I have another controller, for example LoginController, where I use access the ViewData in Razor.

     var Model = (Layout)ViewData[DefaultController.LayoutViewModelKey];
    

    But this is always null.

    In case I call the method in base from Index, its working as expected. But I do not want to think about that.

    [HttpGet]
        public IActionResult Index()
        {
            base.SetLayoutViewModelAsync(); }
    

    Why ViewData is null even when I call base.SetLayoutViewModelAsync() in the LoginControlle ctor?

    Long strory, short; This concept is running fine in U8 and took it in a just setten up U9 project and cannot get it work jet. 👍

  • Dave Woestenborghs 3504 posts 12135 karma points MVP 9x admin c-trib
    May 19, 2022 @ 13:21
    Dave Woestenborghs
    0

    Hi Peter,

    Is your login controller inheriting from your DefaultController ?

    Dave

  • Peter van den Dungen 66 posts 365 karma points
    May 19, 2022 @ 13:29
    Peter van den Dungen
    0

    Hello Dave,

    Yes, it is.

    LoginController : DefaultController
    

    I notice now that DefaultController is inheriting from RenderController, in U8 this is inheriting SurfaceController and IRenderMvcController.

    Maybe that is the issue here.

  • Dave Woestenborghs 3504 posts 12135 karma points MVP 9x admin c-trib
    May 19, 2022 @ 13:31
    Dave Woestenborghs
    0

    Inherting from RenderController is what you need to do. Mixing Surface and RenderControllers is not a good idea. See https://our.umbraco.com/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 for some more background.

    Do you have a doctype called Login ?

    Dave

  • Peter van den Dungen 66 posts 365 karma points
    May 19, 2022 @ 13:42
    Peter van den Dungen
    0

    In that case I will keep the RenderController, good point!

    I have the Login doctype, debugging in LoginController is also working well.

  • Dave Woestenborghs 3504 posts 12135 karma points MVP 9x admin c-trib
    May 19, 2022 @ 14:10
    Dave Woestenborghs
    0

    Do you call the base constructor from the constructor of your login controller ?

    Dave

  • Peter van den Dungen 66 posts 365 karma points
    May 19, 2022 @ 14:13
    Peter van den Dungen
    0

    Yes, here is the Login controller:

    public class LoginController : DefaultController
    {
        public LoginController(
            ILogger<LoginController> logger,
            IPublishedContentQuery publishedContentQuery,
            ICompositeViewEngine compositeViewEngine,
            IUmbracoContextAccessor umbracoContextAccessor,
            ILayoutFactory layoutFactory)
            : base(logger, publishedContentQuery, compositeViewEngine, umbracoContextAccessor, layoutFactory)
        {
    
        }
    
        [HttpGet]
        public override IActionResult Index()
        {
            return base.Index();
        }
    }
    
  • Dave Woestenborghs 3504 posts 12135 karma points MVP 9x admin c-trib
    May 19, 2022 @ 14:55
    Dave Woestenborghs
    0

    If you put a debugger in the defaultController constructor. Does that hit when you call the login controller ?

    Dave

  • Peter van den Dungen 66 posts 365 karma points
    May 19, 2022 @ 20:40
    Peter van den Dungen
    0

    Yes, that is working well as expected.

  • Dave Woestenborghs 3504 posts 12135 karma points MVP 9x admin c-trib
    May 20, 2022 @ 06:58
    Dave Woestenborghs
    1

    Hi Peter,

    That is strange. I will try to reproduce this today.

    Dave

  • Peter van den Dungen 66 posts 365 karma points
    May 22, 2022 @ 18:00
    Peter van den Dungen
    0

    Hi Dave,

    I just created an new solution and created the same state. https://github.com/PetervandenDungen/Umbraco9.LayoutFactory

    Peter

  • Dave Woestenborghs 3504 posts 12135 karma points MVP 9x admin c-trib
    May 23, 2022 @ 07:26
    Dave Woestenborghs
    100

    Hi Peter,

    I just pulled your solution. And I can confirm setting it in the constructor does not work. Only when you call it from the base Index action.

    Probably something change how .NET core handles constructors in controllers compared to .NET Framework.

    Dave

  • Peter van den Dungen 66 posts 365 karma points
    May 23, 2022 @ 07:44
    Peter van den Dungen
    0

    Thanks for the confirmation. 👍

    Peter

Please Sign in or register to post replies

Write your reply to:

Draft