eadam
eadam

Reputation: 26261

How to invoke a View Component from controller

Is it possible to invoke a View Component from controller and render it to a string? I am really looking for some code example for this. Any help will be much appreciated.

Upvotes: 17

Views: 24093

Answers (6)

Saeid Alipour
Saeid Alipour

Reputation: 11

Based on https://gist.github.com/pauldotknopf/b424e9b8b03d31d67f3cce59f09ab17f

public class HomeController : Controller
{
    public async Task<string> RenderViewComponent(string viewComponent, object args)
    {
    var sp = HttpContext.RequestServices;
        
    var helper = new DefaultViewComponentHelper(
        sp.GetRequiredService<IViewComponentDescriptorCollectionProvider>(),
        HtmlEncoder.Default,
        sp.GetRequiredService<IViewComponentSelector>(),
        sp.GetRequiredService<IViewComponentInvokerFactory>(),
        sp.GetRequiredService<IViewBufferScope>());
    
    using (var writer = new StringWriter())
    {
        var context = new ViewContext(ControllerContext, NullView.Instance, ViewData, TempData, writer, new HtmlHelperOptions());
        helper.Contextualize(context);
        var result = await helper.InvokeAsync(viewComponent, args);
        result.WriteTo(writer, HtmlEncoder.Default);
        await writer.FlushAsync();
        return writer.ToString();
    }
}
}

and

public class NullView : IView
{
    public static readonly NullView Instance = new();

    public string Path => string.Empty;

    public Task RenderAsync(ViewContext context)
    {
        if (context == null)
        {
            throw new ArgumentNullException(nameof(context));
        }

        return Task.CompletedTask;
    }
}

Upvotes: 0

nam
nam

Reputation: 23868

Please refer to example from official ASP.NET article on ViewComponent

In their example, the view component is called directly from the controller as follows:

public IActionResult IndexVC()
{
    return ViewComponent("PriorityList", new { maxPriority = 3, isDone = false });
}

Upvotes: 9

Aries
Aries

Reputation: 229

Here's a tag helper that I created to embed components via HTML like syntax. Invoking from a TagHelper like this should closely match invoking from a Controller.

ViewComponent Tag Helper

using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Mvc.ViewComponents;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using Microsoft.AspNetCore.Razor.TagHelpers;


namespace TagHelperSamples.Web
{
    [HtmlTargetElement("component")]
    public class ComponentTagHelper : TagHelper
    {
        private DefaultViewComponentHelper _componentHelper;

        [HtmlAttributeName("name")]
        public string Name { get; set; }

        [HtmlAttributeName("params")]
        public object Params { get; set; }

        [ViewContextAttribute] // inform razor to inject
        public ViewContext ViewContext { get; set; }

        public ComponentTagHelper(IViewComponentHelper componentHelper)
        {
            _componentHelper = componentHelper as DefaultViewComponentHelper;
        }

        public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
        {       
            _componentHelper.Contextualize(ViewContext);

            output.Content.AppendHtml(
                await _componentHelper.InvokeAsync(Name, Params)
            );
        }
    }
}

Usage

<component name="RecentComments" params="new { take: 5, random: true }"></component>

Upvotes: 2

Daniel J.G.
Daniel J.G.

Reputation: 35032

As of beta7 it is now possible to return a ViewComponent directly from a controller. Check the MVC/Razor section of the announcement

The new ViewComponentResult in MVC makes it easy to return the result of a ViewComponent from an action. This allows you to easily expose the logic of a ViewComponent as a standalone endpoint.

So now the code for returning the sample view component just needs to be:

public class HomeController : Controller
{
    public IActionResult Index()
    {
        return ViewComponent("My");
    }
}

Upvotes: 19

Joe Daley
Joe Daley

Reputation: 46476

Code from dotnetstep's answer updated for MVC 6.0.0-beta4 (VS2015 RC):

public class HomeController : Controller
    {
        Microsoft.AspNet.Mvc.ViewComponents.DefaultViewComponentHelper helper = null;

        public HomeController(IViewComponentDescriptorCollectionProvider descriptorProvider, IViewComponentSelector selector, IViewComponentInvokerFactory invokerFactory)
        {
            helper = new DefaultViewComponentHelper(descriptorProvider, selector, invokerFactory);
        }
        public IActionResult Index()
        {                  
            ViewContext context = new ViewContext(ActionContext, null, ViewData, null, null);
            helper.Contextualize(context);
            string st1 = helper.Invoke("My", null).ToString();
            return View();
        }
}

Upvotes: 1

dotnetstep
dotnetstep

Reputation: 17485

You can do that but you have to apply following thing as It is render by DefaultViewComponentHelper.

You have to create instance of this and to create that you need IViewComponentSelector and IViewComponentInvokerFactory.

To do this I have done following thing.

public class HomeController : Controller
    {
        Microsoft.AspNet.Mvc.DefaultViewComponentHelper helper = null;
        Microsoft.AspNet.Mvc.Razor.RazorView razorView = null;
        public HomeController(IViewComponentSelector selector,IViewComponentInvokerFactory factory,IRazorPageFactory razorPageFactory,IRazorPageActivator pageActivator,IViewStartProvider viewStartProvider)
        {
            helper = new DefaultViewComponentHelper(selector, factory);
            razorView = new Microsoft.AspNet.Mvc.Razor.RazorView(razorPageFactory, pageActivator, viewStartProvider);           
        }
        public IActionResult Index()
        {                  
            ViewContext context = new ViewContext(ActionContext, razorView, ViewData, null);
            helper.Contextualize(context);
            string st1 = helper.Invoke("My", null).ToString();
            return View();
        }
}

Here is my sample View Component.

 public class MyViewComponent : ViewComponent
    {     

        public MyViewComponent()
        {

        }

        public IViewComponentResult Invoke()
        {
            return Content("This is test");
        }
    }

Upvotes: 6

Related Questions