MinaMRM
MinaMRM

Reputation: 333

How to pass a data from a controller to _Layout view by using ComponentView

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.

Upvotes: 0

Views: 1002

Answers (1)

Johnathan Le
Johnathan Le

Reputation: 713

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();

Upvotes: 1

Related Questions