2

I'm new with ASP.Net Core (3.0 in this case) and I´m trying to create a menu that is visible on all views of a WebApplication, is created dynamically and must be populated only once. Below i explain the steps and try outs i did to reach the goal needed (if required i can share the code I'm using).

This is what i did:

  1. In a simple way, using the "_Layout.cshtml" page, i created a static HTML menu and made all other views simply inherit that layout. So far, so good;
  2. Next challenge comes from the fact that the menu items are dynamically created after a User has logged-in, which i managed to overcome by setting a ModelView inside a controller (HomeController.cs with Index action in this case), and then delivering it to the view. For this case works OK, because the default page is ~\Home\Index\, problem is when i change to a different view with a different controller, the menu has to be rendered again, and so i have to replicate the code (a problem dealt create a BaseController and BaseModel based on this post along side the OnActionExecuted to host the menu generating code)
  3. Now, the biggest problem is the fact that i can only populate the menu once, after the user logs-in. Each time there is a redirect between different controllers/views (post-back of same controller/view works fine), the model is null inside the OnActionExecuted, I tried using ViewData, ViewBag, TemData, but all are null.

So, my question is, how to keep that specific data alive and shared, basically across all the views, and only gets populated once (after each user login) between redirects from different views? I have been reading around and found several solutions besides the one i did, but i did not found any that could keep data alive throughout the user session the way I need:

To sum up, my flow at this moment, is like this:

  1. User Logged-in
  2. Redirect to default: ~\Home\Index
  3. MenuModelView.cs for the menu gets built and HomeController.cs returns to Index.cshtml with the model attached to it.
  4. Index.cshtml receives the populated ModelView and it uses _Layout.cshtml
  5. The _Layout.cshtml builds the HTML tags for the menu based on the MenuModelView.cs data
  6. User navigates to a different view and steps 3 to 5 are repeated from a specific controller/view
  • If you want to create a control that can be accessible in all pages without changing every controller, I strongly suggest creating a view component. – Anduin Xue Mar 13 '20 at 15:16

1 Answers1

0

If you want to create a control that can be accessible in all pages without changing every controller, I strongly suggest creating a view component. For a view component has no relationship with your controller, but can access dependencies like database and full HTTP context.

For example, you want to build a custom nav menu, you can just create a view component named NavHeader

using Microsoft.AspNetCore.Mvc;

namespace YourProject.Views.Shared.Components.NavHeader 
{
    public class NavHeader : ViewComponent
    {
        public NavHeader(YourDbContext context)
        {
             // you can access your dependencies, like database.
        }

        public IViewComponentResult Invoke()
        {
            // your own logic. You can access HTTPContext here.
            var model = new YourOwnModel();
            return View(model);
        }
    }
}

And just call it in any view or layout.

<!DOCTYPE html>
<html>
<head>
    <title>Example</title>
</head>
<body>
    @*Render your component like this*@
    <vc:nav-header></vc:nav-header>
</body>

For more details about view component, please reference:

https://learn.microsoft.com/en-us/aspnet/core/mvc/views/view-components?view=aspnetcore-3.1

https://anduin.aiursoft.com/post/2020/1/4/share-view-component-between-different-aspnet-core-web-project

Anduin Xue
  • 3,266
  • 2
  • 22
  • 43
  • Hello Anduin, thanks for the answer, i have changed everything to work with ViewComponent and its working fine, now i can use specific models/views on each different controller withouth any dependecy on the menuModel, and also take advantage of the httpContext :). But, still can seem to find a way the menu gets populated only once, i mean, the "Invoke" method gets called every time there is a postback and that means calling a webservice over and over again, is there a way to check if is populated? (like old school IsPostBack)... thanks –  Mar 19 '20 at 14:51
  • Do you mean that you want to render your view component by `ajax`? – Anduin Xue Mar 19 '20 at 14:53
  • Nop, its not the rendering part that worries me on the client side. The problem here is the model with the menu items that, on the server side, is always calling the webservice to fill the model with data and send it back to the view. I dont want to call the webservice everytime there is a postback, i want to store the data from the webservice only once and then fill the menuModel with it (never again calling the webService unless user log-out/in). Tried ViewBags, TempData, they all come null –  Mar 19 '20 at 17:49
  • No. That is not possible. ASP.NET Core is a server-side rendering framework. This means that every time you need a view, you need to call the server web service. You can't render a view once and use it all the time without calling the server. But for your requirements, you can use a client-side framework like Angular to render your view. This will maintain the navbar status while you can keep navigating. If you want to get better performance for your view rendering, please consider `MemoryCahce`.https://docs.microsoft.com/en-us/aspnet/core/performance/caching/memory?view=aspnetcore-3.1 – Anduin Xue Mar 20 '20 at 04:35
  • Oki Anduin, thanks once more for the help and knowledge, will look into the chaching and Angular.. Cheers –  Mar 20 '20 at 10:02