locked
Passing asp-for into and for use in view component RRS feed

  • Question

  • User2012657016 posted

    I have a general select list view component that I'm calling with a tag helper. Currently I'm passing the field name and id into the component as strings but this is obviously not ideal if my model changes etc.

    Outside of the view component I can use the asp-for tag helper to generate the field name and id from the model, can I pass in and/or reference this in my view component?

    Wednesday, October 17, 2018 10:45 AM

All replies

  • User475983607 posted
    Unclear, view components generate there own model which is indepedant of the request. They are used to create reusable and consistent markup usually in the layout.

    If you need a generic select list that can handle any model propert then use the standard tag helper. It seems like a view component is not the right tool for the job.
    Wednesday, October 17, 2018 11:02 AM
  • User2012657016 posted

    Thanks for the reply. My example is perhaps too simple.

    I have a number of form controls that will be used elsewhere, e.g. user lists, status types, site lists etc. that I've implemented as view components. I can call them fine but I want their form id and name values to be dynamic and filled from the model underlying the view component rather than hard coded.

    My first attempt is to pass the form id and name values via the call to the component - I'm using tag helpers. This is fine and works as expected other than it is also, in effect, hard coding the id and name since, if the model changes, then these values will not update to reflect the changes.

    If I include the form controls directly in the form then I can simply use the asp-for tag helper to generate the name and id for the control from the model. I want, if it's possible, to be able to pass this value/object via the tag helper, into the view component, it's underlying model and thereby reference it in the view code of the view component.

    I've seen reference to ModelExpression which "describes an expression passed to a tag helper" - wondering if this or something similar can be used to effectively pass the reference through or, alternatively, if I can evaluate the value of asp-for in via parent view and then pass it to the view component as a plain string.

    Any help appreciated.

    Wednesday, October 17, 2018 11:18 AM
  • User2012657016 posted

    Okay, have it half working.

    My view component is based of a view model that contains a property of type ModelExpression -

    public ModelExpression AspFor { getset; }
    

    Calling my view component I pass this value in via the tag helper 

    <vc:team-user-list aspfor="@Model.Call.UserOwnerId"
                                          teamid="@Model.Call.TeamId"
                                          userid="@Model.Call.UserOwnerId">
                       </vc:team-user-list>

    and then, in the view can reference it in the name and id of the form element by

    <select class="form-control form-control-sm" name="@Model.AspFor.Name" id="@Model.AspFor.Name">
    

    However...
    whereas a normal call to asp-for would generate an id and name referencing Call_UserOwnerId (with underscore between model and property), the output of the Name method call on my property produces Call.UserOwnerId - i.e. with a dot separate rather than underscore and hence any referencing labels etc. don't see the correct form id.

    Anyone been here?

    Wednesday, October 17, 2018 11:40 AM
  • User475983607 posted

    You purposefully bound a View Component to the model? The main reason to implement a View Component is to get away from the model.

    As I understand you are trying to make a run-time component rather than a design time component.  That requires an Interface.  So let's say you have a select list.  To render the select HTML, you need a name attribute, the name of a function, proc, or SQL to populate the options, and the option name and value fields.  You'll end up with an interface with at least 4 properties and probably a Get method to get the options.  Then craft a generic class that implements the Interface.

    IMHO, it seems like the wrong use of a View Component.

    Wednesday, October 17, 2018 12:13 PM
  • User2012657016 posted

    No, the view components use view models.

    I have a number of list items that are used in a number of places whose contents depend on the values of other fields. When they update I need to update the values in the list.

    A view component seems the ideal use for such form control re-use and, since you can call a view component via a controller method then I can update them asynchronously via js/jquery call to the controller. I.e. I change the value of one list (e.g. a list of sites) and the contents of another list (e.g. departments) is silently updated in the background

    Edit: @Model.AspFor.Metadata.PropertyName gives me UserOwnerId , trying to get to call_UserOwnerId

    Wednesday, October 17, 2018 12:21 PM
  • User475983607 posted

    IMHO, the approach does not use View Components as intended.  It places a dependency on the View Model in the controller action which is the problem View Components solve.  You added it back in, as far as I can tell.

    ASKemp

    A view component seems the ideal use for such form control re-use and, since you can call a view component via a controller method then I can update them asynchronously via js/jquery call to the controller. I.e. I change the value of one list (e.g. a list of sites) and the contents of another list (e.g. departments) is silently updated in the background

    No matter how you slice it, a select needs options.  Options are usually stored in a table which must be queried - the implementation.  You can accomplish the same affect with an action that returns a JSON list which is far less code to manage.

    I understand what you are trying to do and this approach has been tried for years, server side includes in the old classic ASP, user controls in the new classic ASP, and partials in MVC.  They all require a standard interface, some reflection, and written descriptions of the dynamic fields.

    Again, just my opinion having dealt with this approach in other projects.

    Wednesday, October 17, 2018 1:39 PM
  • User2012657016 posted

    Thanks for your replies, I'll admit .net core is still very new to me and I have been tasked with the job of converting an older application.

    However, I do have a number of common form components that are used throughout the application as noted, e.g. a select list of users, a select list of sites etc. These are used over several forms. View components, from what I've read, are the obvious choice for this to avoid code duplication - I need some logic associated with them so more than partial views can give.

    At the same time, I have a need to dynamically update the contents of some of those list boxes depending on the other action in the form, e.g. other select values, a search box etc. I could dynamically re-populate the contents using a jquery ajax call and access the DOM to manipulate it directly - but this seems kind of ugly and far more manipulation code to manage when I've already got the code for the view component and can use a similar but simpler ajax call to directly and dynamically reload the component itself via a controller action.

    Edit: someone else with the same idea https://forums.asp.net/t/2121397.aspx?I+am+hopeless+with+this+Refresh+View+Component
    Basically the same: https://github.com/aspnet/Mvc/issues/3815
    And this and its links: https://stackoverflow.com/questions/51208763/is-it-possible-to-refresh-a-viewcomponent-in-a-razor-page-asp-net-core-2-1/51218176#51218176

    Wednesday, October 17, 2018 2:24 PM
  • User475983607 posted

    However, I do have a number of common form components that are used throughout the application as noted, e.g. a select list of users, a select list of sites etc. These are used over several forms. View components, from what I've read, are the obvious choice for this to avoid code duplication - I need some logic associated with them so more than partial views can give.

    We'll have to agree to disagree. I do not recommend this approach with form components because it places a dependency on the View Model.  I think of View Components as standalone components not dependent on the View's model in any way shape or form.  Items like a menu, a weather widget, a stock ticker, a login form are good candidates for a View Component because there are not dependent on the current View or Model.

    At the same time, I have a need to dynamically update the contents of some of those list boxes depending on the other action in the form, e.g. other select values, a search box etc. I could dynamically re-populate the contents using a jquery ajax call and access the DOM to manipulate it directly - but this seems kind of ugly and far more manipulation code to manage when I've already got the code for the view component and can use a similar but simpler ajax call to directly and dynamically reload the component itself via a controller action.

    Be careful when I blowing up sections of the DOM because it can have unexpected consequences.  For example, change handlers on a select input.  You have to reassign the change handler if the select element is overwritten.

    What about crafting a service that caches static select's options.  Then just call the service to get the items.  This can be done in the View using DI.  

    Wednesday, October 17, 2018 5:20 PM
  • User-854763662 posted

    Hi ASKemp ,

    ASKemp

    I have a number of form controls that will be used elsewhere, e.g. user lists, status types, site lists etc. that I've implemented as view components. I can call them fine but I want their form id and name values to be dynamic and filled from the model underlying the view component rather than hard coded.

    The links you provided seems to be able to dynamically update the content of the lists as you want.

    What other questions do you have?

    If you could share your demo with us ,this will help us provide you the suggestions .

    Best Regards,

    Sherry

    Friday, October 19, 2018 9:26 AM
  • User-1770875694 posted

    I'm curious if you were ever able to solve this. If so, do you have additional tips to share on your implementation?

    FWIW: I concur that a view component is an ideal way to approach this—and I'm using view components extensively for exactly this purpose in other contexts. My challenge is that in this case I need access to the ModelExplorer.Metadata, which is populated in the ViewData with asp-for. I'd like to replicate the functionality in my view components, so I can combine e.g. data access and business logic with contextual metadata about the property being bound to.

    Tuesday, November 19, 2019 8:00 PM