Home > Net >  Loop through GetType().GetProperties() and bind each property with asp-for (Razor Pages)
Loop through GetType().GetProperties() and bind each property with asp-for (Razor Pages)

Time:12-10

I just found out that GetType().GetProperties() even exists, is it possible to use this way?

The goal is to use a loop to get all properties on a specific entity without having to hardcode every property inside the HTML, update every edited property and save the new info in an OnPost with the help of the [BindProperty] annotation. I'm unable to correctly bind with the asp-for taghelper.

    <form method="post">

       @foreach (var prop in Model.Product.GetType().GetProperties())
       {
           <label asp-for="@prop.Name"></label>
           <input asp-for="@prop.GetValue(Model.Product)"  />
       }
       <button type="submit" >Update</button>

    </form>

Running this code gives me the following exception:

InvalidOperationException: Templates can be used only with field access, property access, single-dimension array index, or single-parameter custom indexer expressions.

If I use value="@prop.GetValue(Model.Product)" inside the tag I correctly get the information when loading the edit page. But when submitting the form I don't get the new values previously entered.

Here's the model from the modelpage

[BindProperty]
public ProductModel? Product { get; set; }

OnGet()
{
   Product = db.GetProduct(id);
}

Thanks for taking your time reading this!

CodePudding user response:

Tag Helper helps us to write code in a style of declarative programming. When you find it difficult to render different fields dynamically by reflection, try to use the Html helper as an alternative way.

If you just want to render the input without value set. Html Helper works fine. But it seems you also want to set value for inputs, I try the following way but it does not make sense in asp.net core 3.x:

@Html.Editor(prop.Name, new { htmlAttributes = new { @class = "form-control",@Value=prop.GetValue(Model.Product)} })

So I suggest you could customize a tag helper which sets id , name and value of input from the asp-for-model attribute and asp-for-prop attribute in the view.

ConditionTagHelper:

namespace AuthoringTagHelpers.TagHelpers
{
    [HtmlTargetElement("input", Attributes = "asp-for-prop")]

    public class ConditionTagHelper : TagHelper
    {
        [HtmlAttributeName("asp-for-prop")]
        public ModelExpression Property { get; set; }

        [HtmlAttributeName("asp-for-model")]
        public ModelExpression Model { get; set; }


        public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
        {
            var propName = Property.ModelExplorer.Model.ToString();
            var modelName = Model.Name.ToString();
            var modelExProp = Model.ModelExplorer.Properties.Single(x => x.Metadata.PropertyName.Equals(propName));
            var propValue = modelExProp.Model;

            var para = modelName   "."   propName;

            output.Attributes.Add("id", para);
            output.Attributes.Add("name", para);
            output.Attributes.Add("value", propValue);

            base.Process(context, output);
        }
    }
}

In the _viewImports.cshtml, :

@addTagHelper *, AuthoringTagHelpers   //AuthoringTagHelpers depends on your project name

Note: The first string after @addTagHelper specifies the tag helper to load (Use "*" for all tag helpers), and the second string AuthoringTagHelpers specifies the assembly the tag helper is in.

Page:

<form method="post">

    @foreach (var prop in Model.Product.GetType().GetProperties())
    {
        @Html.Label(prop.Name)
        <input asp-for-model="@Model.Product" asp-for-prop="@prop.Name"  />
    }
    <button type="submit" >Update</button>

</form>
  • Related