I´m experiencing a very strange behaviour with ASP .net core 2.2 Razor Pages.
Let´s say I have a standard Razor Page. The User can send a request over that page for a new Team that should be provisioned. I have some fields like, DisplayName etc. which the user can edit freely and a dropdown to choose from where he can select from which existing Team he would like to clone some settings from. After the User selected it, I´m using Ajax to render the partial View on the right side of the view and the User can also select some additional options. Until here I´m fine and everything is working so far. I expected, if the user clicks the save / submit button that everything will be included in the post request, even the partial View Data. After digging around and troubleshooting I found out that I have to set ViewData.TemplateInfo.HtmlFieldPrefix to name everything like in the Parent View to not mess the Binding up. Works also.
The strange behaviour is, that after the Partial View was rendered, in HTML two Inputs fields were generated. One where the user can select and one which is hidden. I´ll show my code and also the rendered HTML File so that guys can make yourself a picture of that. What now happens is, the hidden fields have a default or null values and these are going to be included in the request. I have no idea where they come from and / or how to fix it or what I did wrong?
I will shorten my code a little bit but I will include the necessary parts.
Team Request Model
public class TeamRequestModel
{
public string DisplayName { get; set; }
public string Description { get; set; }
public string Visibility { get; set; }
public List<string> Owners { get; set; }
public string CloneTeamID { get; set; }
public TeamCloneSettings TeamCloneSettings { get; set; }
public TeamRequestModel()
{
TeamCloneSettings = new TeamCloneSettings();
}
}
Team Clone Settings (only Parts can be Set by the user
public class TeamCloneSettings
{
[JsonProperty(PropertyName = "id")]
public string Id { get; set; }
public string PartitionKey;
public string DisplayName { get; set; }
public bool Released { get; set; }
public bool FixedVisibility { get; set; }
public string Visibility { get; set; }
public bool CloneEverything { get; set; }
public TeamCloneParts Parts { get; set; }
public TeamCloneSettings()
{
Parts = new TeamCloneParts();
}
}
public class TeamCloneParts
{
public bool CloneApps { get; set; }
public bool CloneChannels { get; set; }
public bool CloneTabs { get; set; }
public bool CloneSettings { get; set; }
}
}
Main "Team Request View"
@page
@model iwDashboard.Pages.Teams.TeamRequest
@{
ViewData["Title"] = Model.Title;
}
@{
ViewBag.PageTitle = Model.Title;
}
<style>
textarea {
resize: none;
}
</style>
<section class="content">
<form method="post" asp-page-handler="SaveTeamRequest" class="col-md-12">
<div class="row">
<div class="col-md-3">
<div class="card card-primary">
<div class="card-header">
<h3 class="card-title">Team Details</h3>
</div>
<div class="card-body">
<div class="form-group">
<label for="displayName">Display Name</label>
<input id="displayName" asp-for="@Model.teamRequest.DisplayName" type="text" class="form-control" required />
</div>
... More fields
<div style="float: left; width: 40%">
<button type="button" onclick="history.go(-1)" data-toggle="tooltip" title="Back" class="btn btn-primary btn-block btn-sm"><i class="fa fa-arrow-circle-left"></i><b> Back</b></button>
</div>
<div style="float: right; width: 40%">
<button type="submit" data-toggle="tooltip" title="Save" class="btn btn-success btn-block btn-sm"><i class="fas fa-save"></i><b> Save</b></button>
</div>
</div>
<!-- /.card-body -->
</div>
<!-- /.card -->
</div>
<div class="col-md-6">
<div class="card card-secondary">
<div class="card-header">
<h3 class="card-title">Team Settings</h3>
</div>
<div class="card-body">
<!--partial comes here-->
<div id="partialDiv"></div>
</div>
<!-- /.card-body -->
</div>
<!-- /.card -->
</div>
</div>
</form>
</section>
@section Scripts
{
<script>
function GetTeamTemplateProperties() {
var selectedTeam = $('#teamTemplateSelection').val();
$.ajax({
type: "Get",
beforeSend: function (xhr) {
xhr.setRequestHeader("XSRF-TOKEN",
$('input:hidden[name="__RequestVerificationToken"]').val());
},
url: '/Teams/TeamRequest?handler=TeamTemplateProperties',
data: {
SelectedTeam: selectedTeam
},
success: function (result) {
$("#partialDiv").after(result);
}
})
};
</script>
}
Get Template Properties Post called by Ajax
public async Task<IActionResult> OnGetTeamTemplateProperties(string SelectedTeam)
{
TeamCloneSettings teamCloneSettings = new TeamCloneSettings();
teamRequest = new TeamRequestModel();
if (!string.IsNullOrEmpty(SelectedTeam))
{
teamCloneSettings = await _teamCloneSettingService.GetTeamCloneSettingByIdAsync(SelectedTeam);
teamRequest.TeamCloneSettings = teamCloneSettings;
}
ViewData.TemplateInfo.HtmlFieldPrefix = "teamRequest";
SelectedValue = SelectedTeam;
return new PartialViewResult
{
ViewName = "_TeamCloneSettingsPartial",
ViewData = new ViewDataDictionary<TeamRequestModel>(ViewData, teamRequest)
};
}
View Model BindProperty
...More Code
[BindProperty]
public TeamRequestModel teamRequest { get; set; }
[BindProperty]
public string SelectedValue { get; set; }
...More Code
_TeamCloneSettingsPartial
@model iwDashboard.Models.TeamRequestModel
@{
<!--
Check if fixed visibility is enabled and
disable selection of visibility if it is
-->
string disabled = null;
string options = null;
if (Model.TeamCloneSettings.FixedVisibility)
{
disabled = "disabled";
options = Model.Visibility;
}
}
<div class="form-group">
<input type="checkbox" hidden id="cloneEverthing" checked=@Model.TeamCloneSettings.CloneEverything>
<ul class="list-group list-group-unbordered mb-3">
<li class="list-group-item">
<label for="visibility">Visibility</label>
<select class="form-control @disabled custom-select" asp-for="TeamCloneSettings.Visibility" required>
@if (!string.IsNullOrEmpty(disabled))
{
<option readonly selected>@Model.TeamCloneSettings.Visibility</option>
}
else
{
<option>Public</option>
<option>Private</option>
}
</select>
</li>
<!-- Checkbox parts here
Javascript will check if "CloneEverything"
is enabled and will disable all of the following checkboxes
-->
<li class="list-group-item">
<div class="form-group">
<div class="custom-control custom-switch">
<input type="checkbox" class="custom-control-input" id="cloneApps" checked=@Model.TeamCloneSettings.Parts.CloneApps asp-for="TeamCloneSettings.Parts.CloneApps">
<label class="custom-control-label" for="cloneApps">Clone Apps</label>
</div>
</div>
</li>
<li class="list-group-item">
<div class="form-group">
<div class="custom-control custom-switch">
<input type="checkbox" class="custom-control-input" id="cloneChannels" checked=@Model.TeamCloneSettings.Parts.CloneChannels asp-for="TeamCloneSettings.Parts.CloneChannels">
<label class="custom-control-label" for="cloneChannels">Clone Channels</label>
</div>
</div>
</li>
<li class="list-group-item">
<div class="form-group">
<div class="custom-control custom-switch">
<input type="checkbox" class="custom-control-input" id="cloneSettings" checked=@Model.TeamCloneSettings.Parts.CloneSettings asp-for="TeamCloneSettings.Parts.CloneSettings">
<label class="custom-control-label" for="cloneSettings">Clone Settings</label>
</div>
</div>
</li>
<li class="list-group-item">
<div class="form-group">
<div class="custom-control custom-switch">
<input type="checkbox" class="custom-control-input" id="cloneTabs" checked=@Model.TeamCloneSettings.Parts.CloneTabs asp-for="TeamCloneSettings.Parts.CloneTabs">
<label class="custom-control-label" for="cloneTabs">Clone Tabs</label>
</div>
</div>
</li>
</ul>
</div>
Strange rendered Input field
https://i.stack.imgur.com/aZtUD.png
As you can see, the Input is rendered twice and only the hidden values will be transmitted. But this only happens for the Input fields in the Partial View, all other fields are fine.
Any Ideas? Would be great :-).
Thanks a lot!!