Bluesight
Bluesight

Reputation: 754

.NET Core - Load View Components asnychronous (client side) via Wrapper

To optimize the response time of our web application I would like to load View Components in .NET Core 2.2 asynchronous from client side without Javascript. Of course you could achieve this by loading them with an AJAX Call and then render the view from a Controller.

But I would like to keep the intellisense and work with tags in the code, so you can invoke a view component with a tag like this multiple times in different places and don't need to create multiple ajax posts in JS:

<vc:free-text ftx-code="STARTPAGE" obj-ref="null" custom-para="null"></vc:free-text>

I tried to load the view component via TagHelper, but apparently they also are rendered synchronously (only server side asynchronous):

    [HtmlTargetElement("widget", Attributes = WidgetNameAttributeName)]
    public class WidgetTagHelper : TagHelper
    {
        private readonly IViewComponentHelper _viewComponentHelper;
        public WidgetTagHelper(IViewComponentHelper viewComponentHelper)
        {
            _viewComponentHelper = viewComponentHelper;
        }

.....
        public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
        {
            ((IViewContextAware)_viewComponentHelper).Contextualize(ViewContext);
            var content = await _viewComponentHelper.InvokeAsync(typeof(FreeTextViewComponent), new { ftxCode = FTXCode, objRef = OBJRef, customPara = CustomPara });
            output.Content.SetHtmlContent(content);
        }
    }

Does anyone know an approach (wrapper, TagHelper, etc.?) where I first load the ViewComponent with an "empty" modal and when it's rendered it makes an client side asynchronous callback to fetch the actual data, like described in this post? https://github.com/aspnet/AspNetCore.Docs/issues/7914#issuecomment-441280663

Upvotes: 0

Views: 652

Answers (2)

Bluesight
Bluesight

Reputation: 754

I ended up by reloading them via an AJAX call, but only one for all rendered views. First, I load the ViewComponent with a parameter "async" set to true (it only fetches data, when async is set to "false"):

        public async Task<IViewComponentResult> InvokeAsync(string ftxCode, string objRef = "", List<FTXPara> customPara = null, bool async = false)
        {
            if (async)
            {
                ViewData["ftxCode"] = ftxCode;
                ViewData["async"] = async;
                return View(new GetFreeTextResponse());
            }
            List<CMailCustomParameter> para = new List<CMailCustomParameter>();
            if (customPara != null)
            {
                foreach (var p in customPara)
                {
                    para.Add(new CMailCustomParameter { ParameterName = p.Name, ParameterValue = p.Value });
                }
            }

            var resp = await GetItemsAsync(ftxCode, objRef, para);
            return View(resp);
        }
        private async Task<GetFreeTextResponse> GetItemsAsync(string ftxCode, string objRef, List<CMailCustomParameter> para)
        {
            return await FreeTextService.GetFreeText(new GetFreeTextRequest { FTX_Code = ftxCode, ObjRef = objRef, CustomParameters = para });
        }

Then I loop through every rendered "empty" View Component and load the View Component with the actual data from a controller (this time "async" is false"):

    $('.view-component').each(function (i, obj) {
        var that = this;
        var id = $(this).attr('id');
        var test = $(this).data();
        console.log($(this).data('async'));
        if ($(this).data('async')) {

            $.ajax({
                url: "/VC/Get" + "FreeText" + "ViewComponent",
                type: 'POST',
                data: $(this).data(),
                contentType: 'application/x-www-form-urlencoded',
                success: function (data) {
                    $(that).html(data);
                },
                error: function (xhr, status, error) {
                    console.log(xhr.status + " - " + error);
                }
            });
        }
    });

Upvotes: 0

Chris Pratt
Chris Pratt

Reputation: 239430

View components and tag helpers cannot be accessed directly. They aren't part of the request pipeline and don't respond to anything. The best you can do is create a view that happens to utilize the component or tag helper, and then return that view from an action, which is what you would actually make the request to.

Generally speaking, for this type of stuff, you'd be better served with a client-side library like Vue, Angular, etc. and create client-side components via those libraries. Then, your server acts as an API, returning JSON data that you feed into those components.

Upvotes: 1

Related Questions