Search code examples
c#asp.net-coreasp.net-core-viewcomponent

How to pass runtime parameter to ViewComponent Invoke method


I am trying out the .net Core ViewComponents instead of partial views. Here is my scenario. I have a View that contains a few kendo listbox controls. When an item in any of the listbox is clicked, I need to invoke a webcomponent with the selected item id in the listbox. I also need to keep the ViewComponents hidden until the user clicks on a listbox item. Each listbox has it's own ViewComponent because the control in the viewcomponent is different for each listbox. So, I will have to hide some viewcomponents and show only the relevant component for the listbox. I have no clue how to pass the item id in any listbox when selected, to the Invoke method.

Here is my main view (non essential code removed)

<div style="height:25%; width:100%; background-color:antiquewhite;">
    <b>Sections:</b>
        @(Html.Kendo().ListBox()
                    .Name( "SectionListBox" )
                    .Selectable( ListBoxSelectable.Single )
                    .DataTextField( "Name" )
                    .DataValueField( "id" )
                    //.DataSource( ds => ds.Read( r => r.Action( "GetAllPageSectionsAsync", "Home" ) ) )
                    .Events( e => e.Change( "onSectionSelected" ) )
                    .HtmlAttributes( new { style = "width:95%;height:85%;" } )
        )
</div>
<div style="height:25%; width:100%; background-color:cornsilk;">
    <b>Fields:</b>
        @(Html.Kendo().ListBox()
                    .Name( "FieldListBox" )
                    .Selectable( ListBoxSelectable.Single )
                    .DataTextField( "Name" )
                    .DataValueField( "id" )
                    //.DataSource( ds => ds.Read( r => r.Action( "GetAllFieldsAsync", "Home" ) ) )
                    .HtmlAttributes( new { style = "width:95%;height:85%;" } )
        )
</div>
<div class="col-md-8" style="height:100%">
    <div class="col-md-12" style="height:100%;">
        <div id="AttrGridDiv" class="col-md-5" style="height:40%;">
            <label>Attributes</label>
            @await Component.InvokeAsync( "PageAttributes", new { pageId = 123 } )
        </div>
    </div>
</div>

and here is my component view:

@model IEnumerable<aaaaaaaa>

@(Html.Kendo().Grid<dynamic>()
.Name( "Attributes" )
.Selectable()
.Sortable()
.Scrollable()
.Events( e => e.Change( "onAttributeGridSelected" ) )
.HtmlAttributes( new { style = "width:100%;" } )
//.DataSource( ds => ds
//.Ajax()
//.Read( r => r.Action( "GetPages", "Home" ).Type( HttpVerbs.Post ) ) )
)

and here is my View Component:

[ViewComponent(Name ="PageAttributes")]
public class PageAttibutesViewComponent : ViewComponent
{
    private IRulesEngineService reService;

    public PageAttibutesViewComponent( IRulesEngineService _reService)
    {
        reService = _reService;
    }

    public async Task<IViewComponentResult> InvokeAsync(int pageId)
    {
        var pageAttribs = await reService.GetAllPageAttributesAsync( pageId );
        return View( pageAttribs );
    }
}

I have only shown one of the components. I have a few similar components that I need to invoke based on which listbox is selected the components will render different controls.

So, again, my questions are:

  1. How to invoke the component from the view Conditionally?
  2. How to pass the selected id from the chosen listbox to the invoke method?

Solution

  • ViewComponents are processed by Razor (or any alternative view engine that supports the feature). What you try to achieve happens in the browser, but you need some server-side processing as well.

    As I see it, you must (1) capture the ListBox change (Javascript), then (2) post it back to the controller (again Javscript) which (3) will process the request, change the model depending on what was submitted (C#), and finally (4) return the view with changed model, so that you the view can figure out how (what param value) to invoke the ViewComponent in question (C#, Razor). You may also decide to return a different view, but that is somewhat uncommon. Not so very straightforward, but this is web. Long ago, in web forms check boxes and drop downs had property PostBack (or simillar), which, when checked, did basically the same.

    In effect, the post back to the controller, the changed view model, and finally the view, rendered with the changed model result in change of ViewComponent's invocation.

    There are three types of ViewComponent invocations in Core 2.0. You can read about them here: https://learn.microsoft.com/en-us/aspnet/core/mvc/views/view-components#invoking-a-view-component.