According to this answer from 2010, regarding mvc-2, it wasn't possible. What about now, in asp.net-core 2.2?
My usecase:
I have a BaseViewModel that is being used by 2 views: TableView (for users) and TableManagentView (for admins). The BaseViewModel is invoked by a ViewComponent. Here are some samples:
BaseViewModel:
public class BaseViewModel {
[Display(Name = "Comment")
public string UserComment { get; set; }
}
TableView:
@await Component.InvokeAsync(nameof(Base), new { myObject = myObject, stringName = "User"})
TableManagementView:
@await Component.InvokeAsync(nameof(Base), new { myObject = myObject, stringName = "Admin"})
Base:
public class Base : ViewComponent
{
public IViewComponentResult Invoke(BaseViewModel myObjet, string invokingView)
{
// here i want to do something like that
if (invokingView == "User") {
myObject.UserComment.SetDisplayName("My Comment");
}
if (invokingView == "Admin") {
myObject.UserComment.SetDisplayName("User's Comment");
}
return View("BaseViewComponent", myObject);
}
}
BaseViewComponent:
@Html.DisplayNameFor(model => model.UserComment)
The BaseViewModel is simplified, but there are a lot more attributes. The reason I want to do this is to avoid code duplication in both tables. The only thing that should change are the label names.
I've tried reflection, but without success:
public IViewComponentResult Invoke(BaseViewModel myObject, string invokingView)
{
MemberInfo property = typeof(BaseViewModel).GetProperty("UserComment");
property.GetCustomAttributes(typeof(DisplayAttribute)).Cast<DisplayAttribute>().Single().Name = "test";
return View("BaseViewComponent", myObject);
}
The Name doesn't change and remains "Comment" from the initial setting.
If it's not possible to set the attribute name programmatically, what other solutions do I have? I'm thinking about ViewBag/ViewData or TempData, but this solution doesn't appeal to me. What would be the pro's and con's of that?
Extending on the comment I left, one way you could solve this is by having your
BaseViewModelbeing an abstract class and have concrete classes deriving from it. SoUserViewModelandAdminViewModel. These two concrete classes would then be the models for bothTableViewandTableManagentViewand would be responsible for telling the "outside world" how to label fields.The base class has two main aspects (apart from your normal fields): An abstract
Dictionary<string, string>which will contain the labels and a method to get the label from the list:string GetLabel(string propName). So something like this:Then you create the two deriving classes
UserandAdmin:They only implement the
Dictionary<string, string>and set the appropriate text for each field on the base class.Next, changing your
BaseViewComponentto this:View:
ComponentView class (simpler now)
Finally, changing your views
TableViewandTableManagentViewto this:and the Controller to:
Now when you navigate to
TableView, you'll pass aUserViewModelto theBaseViewComponentand it will figure it out the correct label. Introducing new fields will just now require you to change your viewmodels, adding a new entry to the Dictionary.It's not perfect, but I think it's an okay way to solve it. I'm by far not an MVC expert so maybe others can come up with a more natural way to do it as well. I also prepared a working sample app and pushed to GitHub. You can check it out here: aspnet-view-component-demo. Hope it helps somehow.