0

I'm implementing asp.net core project. I have a method in my controller that should pass a data to a viewcomponent and then I need that data to be displayed in _Layout razor view. Below is what I have tried till now:

 public class AccountController : Controller {
    public IActionResult Index(string str)
            {
            _httpContext.HttpContext.Items["Shared"] = str;

            Debug.WriteLine("str:" + str);

            Debug.WriteLine("HttpContext Index shared:"+_httpContext.HttpContext.Items["Shared"]);
            // Use ViewData
            ViewData["Shared"] = str;
            Debug.WriteLine("ViewData Index shared:" + ViewData["Shared"]);
            return View();
        }
}



public class MySharedDataViewComponent : ViewComponent
    {
        private readonly IHttpContextAccessor _httpContext;

        public MySharedDataViewComponent(IHttpContextAccessor context)
        {
            _httpContext = context;
        }
        public Task<IViewComponentResult> InvokeAsync()
        {
            Debug.WriteLine("MyShred data:" + _httpContext.HttpContext.Items["Shared"]);
            return Task.FromResult<IViewComponentResult>(View(_httpContext.HttpContext.Items["Shared"]));
            
        }
}

In index.cshtml for Account controller:

@model string
    <h2>@Model</h2>

In Default.cshtml

@model dynamic
@{
    var passedDataFromItems = (Model as string);
    var passedDataFromViewData = (ViewData["Shared"] as string);
}

@passedDataFromItems
@passedDataFromViewData 

In _Layout I added this:

<div class="col-sm-10 col-8 p-0 m-0 text-left">
                            @await Component.InvokeAsync("MySharedData")
                                                   </div>

And in startup I pasted what you suggested as well.

My problem is in _Layout there isn't any data from ViewComponent to be displayed.

MinaMRM
  • 343
  • 4
  • 16

1 Answers1

1

First, you need to remove ( ) in _Layout.cshtml, just use @await ComponentAsync("SharedData"). Because ( ) will render HTMLEncoded string instead of HTML string.

Second, if you want to pass your shared data down from Controller, you don't need to call ViewComponent inside Controller. There are several way to pass, ex: HttpContext.Items or ViewData. You don't want to call render HTML from Controller. In previous .NET MVC, we have @Html.RenderAction() to render ChildControlOnly view in Controller. But this is removed, so there are no reason to use Controller to call ViewComponent. Let .NET Core handle that for you by Naming Convention

Third, you don't want to declare @{ Layout = null } in ViewComponent, it is useless because ViewComponent is as PartialView.

Not sure why you try to render whole HTML page in ViewComponent and put it in <head> tag in _Layout.cshtml.

Updated answer with sample code In your _Layout.cshtml

<html>
<head>
</head>
<body>
<!-- Use this for calling ViewComponent, Name must be prefix of VC class -->
@await ComponentAsync("SharedData")
<!-- Use this for render Body -->
@RenderBody()
</body>
</html>

Example you have HomeController to render Home Page

public class HomeController : Controller
{
    private readonly IHttpContextAccessor _httpContext;
    
    public HomeController (IHttpContextAccessor context)
    {
        _httpContext = context;
    }

        /// <summary>
        /// Define [Route] for parameterize
        /// str? means Nullable Param
        /// Ex: localhost/hello -> str = hello
        /// </summary>
        [Route("{str?}")]
        public IActionResult Index(string str)
        {
            _httpContextAccessor.HttpContext.Items["Shared"] = str ?? "Items Empty Param";
            ViewData["Shared"] = str ?? "View Data Empty Param";
            return View();
        }
}

Next you need to create Index.cshtml for placing @RenderBody()

<div>This is home page</div>

Next you need to create SharedDataViewComponentlocales on ViewComponents folder (Under root project)

public class SharedDataViewComponent : ViewComponent
{
    private readonly IHttpContextAccessor _httpContext;
    
    public SharedDataViewComponent(IHttpContextAccessor context)
    {
        _httpContext = context;
    }
    public Task<IViewComponentResult> InvokeAsync()
    {
        return Task.FromResult<IViewComponentResult>(View(_httpContext.HttpContext.Items["Shared"]));
    }
}

In your Views\Shared\SharedData\Default.cshtml, write with these markup

@model dynamic
@{
   var passedDataFromItems = (Model as string);
   var passedDataFromViewData = (ViewData["Shared"] as string);
}

@passedDataFromItems 
@passedDataFromViewData 

Ensure in your Configure method in Startup.cs should add this line

services.AddHttpContextAccessor();
Johnathan Le
  • 675
  • 3
  • 8
  • After searching on the net I understand, HttpContext.Items was removed in .net core. My problem is to share a data that comes from the account controller in _Layout view. – MinaMRM Jun 27 '20 at 08:01
  • Not sure with the article said HttpContext.Items has been removed but I am still working on .NET Core 3.1 and still fine. [here](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/app-state?view=aspnetcore-3.1#httpcontextitems) is still available. Based on your update, @model is a POCO class, can't be used as ValueType (`string` or `List`), if you are lazy to declare a class, you can use `@model dynamic`, then cast `Model` to any type that you want. [Example here](https://stackoverflow.com/questions/58107729/view-dynamic-model) – Johnathan Le Jun 27 '20 at 08:10
  • I can not understand how can I match your suggestion to my code – MinaMRM Jun 27 '20 at 08:17
  • @MinaMRM please review my sample code above to help you understand sample flow. – Johnathan Le Jun 27 '20 at 09:11
  • Thank you for your reply. I exactly pasted what you suggested to me. However, in MysharedDataViewComponent, after debuuging i undestand _httpContext.HttpContext.Items["Shared"] is empty. I edit my code above to show you my attempts – MinaMRM Jun 27 '20 at 12:02
  • @MinaMRM take a look on my HomeController, you are missing declare `[Route]`. When you want to have Action with Parameter, you need to define [Route]. – Johnathan Le Jun 27 '20 at 14:22