Dotnetcore mvc dynamic binding in a partial view

Grant 1 Reputation point
2021-07-05T08:50:36.843+00:00

I am building an edit form by looping through the ModelMetaData and utilising the Html.Editor extension.

This allows me to create a edit form with a dynamic model. I have created a simple dotnetfiddle of how this works. (dotnetfiddle uses .net 4.7.2, but i am using dotnetcore 5.)

Link: dotnetfiddle example

In my real project i have started adding some custom formatting and want to now move this into a partial. I created a partial, and call Html.RenderPartial passing the ModelMetaData into the partial. The control renders correctly, but the data does not get set into the controls. The input's render without their default values.

Why does this not work within a Partial?

I cannot make use of a partial in dotnetfiddle, but have created a small example illustrating the problem.

Controller

public class HomeController : Controller  
{  
    public IActionResult Index()  
    {  
        return View(new Person()  
        {  
            FirstName = "John",  
            LastName = "Doe"  
        });  
    }  
}  

public class Person  
{  
    public string FirstName { get; set; }  
    public string LastName { get; set; }  
}  

View

@foreach (var properties in ViewData.ModelMetadata.Properties)  
{  
    @Html.Editor(properties.PropertyName, new { htmlAttributes = new { @class = "form-control" } })  
}  
<br /><br />  
@foreach (var properties in ViewData.ModelMetadata.Properties)  
{  
    Html.RenderPartial("_InputControl", properties);  
}  

Partial

@model Microsoft.AspNetCore.Mvc.ModelBinding.ModelMetadata  
@Html.Editor(@Model.PropertyName, new { htmlAttributes = new { @class = "form-control" } })  

What Renders

![111738-image.png]2

As you can see, the first two textboxes render with their default values. The second two textboxes however do not have their value.

Please do advice how can I get this to bind properly in the Partial view so that I can clean up my code and make it more maintainable.

Thanks

Developer technologies ASP.NET ASP.NET Core
0 comments No comments
{count} votes

2 answers

Sort by: Most helpful
  1. Chao Deng-MSFT 801 Reputation points
    2021-07-06T09:02:00.84+00:00

    Hi @Grant ,

    You can bind data like this:
    Model:

        public class Person  
        {  
            public string FirstName { get; set; }  
            public string LastName { get; set; }  
        }  
    

    Controller:

      public IActionResult Index()  
            {  
                return View(new Person()  
                {  
      
                    FirstName = "John",  
                    LastName = "Doe"  
                });  
            }  
    

    View:

    @{Html.RenderPartial("_Person");}  
    

    Partial:

    @model WebApplication35.Models.Person  
      
    @{  
        ViewBag.FirstName = Model.FirstName;  
        ViewBag.LastName = Model.LastName;  
    }  
      
    @Html.Editor("FirstName", new { htmlAttributes = new { @class = "form-control" } })  
    @Html.Editor("LastName", new { htmlAttributes = new { @class = "form-control" } })  
    

    Result:
    TmQEj.png

    You can read this article:https://stackoverflow.com/questions/25088115/asp-net-mvc-5-html-editorfor-and-property-of-type-object


    If the answer is helpful, please click "Accept Answer" and upvote it.

    Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.

    Best Regards,

    ChaoDeng

    0 comments No comments

  2. Grant 1 Reputation point
    2021-07-06T12:19:54.647+00:00

    Thanks for your insight @Chao Deng-MSFT

    Unfortunately this doesn't help with my specific problem.
    Let me try elaborate further.

    The control itself has both different HTML surrounding it, and some conditional logic on how it will display

    different HTML surrounding it

    For example, on some pages it may be a single column layout

    <div class="container-fluid px-4">  
    	@foreach (var property in ViewData.ModelMetadata.Properties)  
    	{  
    		<div class="mb-3">  
    			<label asp-for="@property.PropertyName">@(property.GetDisplayName())</label>              
    			@Html.Editor($"{property.PropertyName}", new { htmlAttributes = new { @class = "form-control") } })  
    		</div>  
    	}  
    </div>  
    

    On other's multi columns or more

    <div class="container-fluid px-4">  
    	@{var properties = ViewData.ModelMetadata.Properties.ToArray();}  
    	@for (int i = 0; i < properties.Length; i += 2)  
    	{  
    		<div class="row mb-2">  
    			<div class="col-md-6">  
    				@Html.Editor($"{prop.PropertyName}", new { htmlAttributes = new { @class = "form-control" } })  
    			</div>  
    			<div class="col-md-6">  
    				@{var prop = properties[i];}  
    				@Html.Editor($"{prop.PropertyName}", new { htmlAttributes = new { @class = "form-control" } })  
    			</div>  
    		</div>  
    	}  
    </div>  
    

    Conditional logic around the control itself

    In order to cater for required fields, readonly fields and boolean fields, I've had to add some additional logic which is now repeated on multiple pages

    @{var prop = properties[i];}  
    @if (prop.ModelType.Name.Equals("Boolean", StringComparison.InvariantCultureIgnoreCase))  
    {  
    	<div class="form-check">  
    		<label asp-for="@prop.PropertyName">@(prop.GetDisplayName())</label>  
    		@if (prop.IsReadOnly)  
    		{  
    			@Html.Editor($"{prop.PropertyName}", new { htmlAttributes = new { @class = "form-check-input", @type = "checkbox", @disabled = "disabled" } })  
    		}  
    		else  
    		{  
    			@Html.Editor($"{prop.PropertyName}", new { htmlAttributes = new { @class = "form-check-input", @type = "checkbox" } })  
    		}  
    	</div>  
    }  
    else  
    {  
    	<label asp-for="@prop.PropertyName">@(prop.GetDisplayName())</label>  
    	@if (prop.IsReadOnly)  
    	{  
    		@Html.Editor($"{prop.PropertyName}", new { htmlAttributes = new { @class = "form-control", @readonly = "readonly" } })  
    	}  
    	else  
    	{  
    		@Html.Editor($"{prop.PropertyName}", new { htmlAttributes = new { @class = "form-control" } })  
    		if (prop.IsRequired)  
    		{  
    			@Html.ValidationMessage($"{prop.PropertyName}", new { @class = "text-danger" })  
    		}  
    	}  
    }  
    

    This kind of logic is used on multiple pages making this one unmaintainable mess :(
    My hope and dreams were to put this messy conditional logic in a partial, then be able to implement it in multiple places.
    The Above would then look like:

    <div class="container-fluid px-4">  
    	@foreach (var property in ViewData.ModelMetadata.Properties)  
    	{  
    		<div class="mb-3">  
    			Html.RenderPartial("_InputControl", property);  
    		</div>  
    	}  
    </div>  
    

    I hope this clarified my question clearer.
    Thanks


Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.