【发布时间】:2020-06-23 16:26:28
【问题描述】:
我正在开发一个 ASP.Net Core MVC 应用程序。我有一个模型配方,它有两个属性是其他对象的列表:
public class Recipe
{
public int Id { get; set; }
[Required(ErrorMessage = "Title is required")]
[StringLength(45, MinimumLength = 2)]
[Display(Name = "Title", Prompt = "Title")]
public string Title { get; set; }
[Required(ErrorMessage = "Description is required")]
[StringLength(500)]
[Display(Name = "Description", Prompt = "Description")]
public string Description { get; set; }
[Required(ErrorMessage = "You must include at least one ingredient.")]
public List<RecipeIngredient> Ingredients {get; set;}
[Required(ErrorMessage = "You must include at least one instruction step.")]
public List<Instruction> Directions { get; set; }
public byte[] Picture { get; set; }
}
RecipeIngredient.cs:
public class RecipeIngredient
{
public int Id { get; set; }
public int IngredientID { get; set; }
[Required]
public string Measurement { get; set; }
[Required]
public string Ingredient { get; set; }
}
指令.CS:
public class Instruction
{
[Required]
public int Step { get; set; }
[Required]
[StringLength(200, MinimumLength = 2)]
public string InstructionText { get; set; }
}
这是我对创建新配方的看法:
@model TheKitchen.Models.Recipe
@section Scripts {
<script src="@Url.Content("~/js/CreateRecipe.js")" type="text/javascript"></script>
<script>
$(document).ready(function () {
autocomplete();
});
</script>
<script>
$('#addNewIngredient').click(function () {
addIngredient();
});
</script>
<script>
$('#addNewInstruction').click(function () {
addInstruction();
});
</script>
}
@section Styles{
<link rel="stylesheet" href="~/css/CreateRecipe.css" />
}
@{
ViewData["Title"] = "New Recipe";
}
<h3>New Recipe</h3>
<!-- New recipe form -->
@using (Html.BeginForm("Create", "Recipes", FormMethod.Post))
{
<div class="form-group">
<input type="text" class="form-control" id="recipeName" asp-for="Title" placeholder="Recipe Name">
<span asp-validation-for="Title" class="text-danger"></span>
</div>
<div class="form-group">
<textarea class="form-control" id="recipeDescription" asp-for="Description" rows="3" placeholder="Description"></textarea>
<span asp-validation-for="Description" class="text-danger"></span>
</div>
<div class="form-group addIngredients">
<h4>Ingredients</h4>
<div id="ingredientSearchbar">
<input type="search" class="form-control" id="ingredientSearch" placeholder="Enter ingredient name here.">
<input type="button" class="btn btn-outline-secondary" id="addNewIngredient" value="Add" />
</div>
<br />
<table class="table" id="ingredientTable" name="ingredientTable">
<tbody>
</tbody>
</table>
<span asp-validation-for="Ingredients" class="text-danger"></span>
</div>
<div class="form-group addInstructions">
<h4>Instructions</h4>
<div id="instructionTextBar">
<input type="text" class="form-control" id="instructionText" placeholder="Enter instruction step here, then click Add Step.">
<input type="button" class="btn btn-outline-secondary" id="addNewInstruction" value="Add" />
</div>
<br />
<!-- model.Instructions is getting returned null if form validation fails -->
<table class="table" id="instructionTable" name="instructionTable">
<tbody>
</tbody>
</table>
<span asp-validation-for="Directions" class="text-danger"></span>
</div>
<button type="submit" class="btn btn-primary" id="newRecipeButton">Create Recipe</button>
}
而 jQuery 驱动将 RecipeIngredients 和 Instructions 添加到它们各自的模型列表和 html 表中以供显示:
/* Script for autocomplete in ingredient search box. */
function autocomplete() {
$("#ingredientSearch").autocomplete({
appendTo: ".addIngredients",
source: function (request, response) {
$.ajax({
url: '/Recipe/IngredientSearch',
type: 'GET',
cache: false,
data: request,
dataType: 'json',
success: function (data) {
response($.map(data, function (item) {
return {
label: item.label,
value: item.Value
}
}))
}
});
}
});
};
/* Script to add ingredient search bar text to ingredient list if not already present. */
function addIngredient() {
var listIndex = $('.recipeIngredientRow').length;
var measurement = '<input type="text" asp-for="Ingredients[' + listIndex + '].Measurement" name="Ingredients[' + listIndex +
'].Measurement" class="measurementTextBox" id="measurementTextBox' + listIndex + '" placeholder="Enter measurement">';
var ingredient = '<input type="text" class="noTextborder" asp-for="Ingredients[' + listIndex + '].Ingredient" name="Ingredients[' + listIndex +
'].Ingredient" value="' + $('#ingredientSearch').val() + '" readonly>';
var deleteButton = '<input type="button" class="cancel" value="X">';
var newRow = '<tr class="recipeIngredientRow"><td>' + measurement + '</td><td>' + ingredient +
'</td><td>' + deleteButton + '</td></tr>';
var hasDuplicates = false;
var hasEmptyMeasurements = false;
$("#ingredientTable td").each(function () {
var measurementContent = $(this).find('input').val().length;
var ingredientContent = $(this).find('input').val() == $('#ingredientSearch').val();
if (ingredientContent) {
hasDuplicates = true;
return;
}
if (measurementContent == 0) {
hasEmptyMeasurements = true;
return;
}
});
if (hasDuplicates)
alert('This ingredient is already on the ingredient list.');
else if ($('#ingredientSearch').val().length == 0)
alert("You can't add a blank ingredient.");
else if (hasEmptyMeasurements)
alert("You must provide a measurement before adding a new ingredient.");
else
$('#ingredientTable').append(newRow);
$('#ingredientSearch').val("");
};
/* Script for removing ingredient table row when 'X' button is clicked. */
$('#ingredientTable').on('click', 'tr input.cancel', function () {
$(this).closest('tr').remove();
});
/* Script for adding instruction step to instruction list */
function addInstruction() {
var listIndex = $('.recipeInstructionRow').length;
var stepNumber = '<input type="number" readonly="true" asp-for="Directions" name="Directions[' + listIndex +
'].Step" class="noTextborder stepNumber" value="' + (listIndex + 1) + '">';
var instruction = '<textarea asp-for="Directions" name="Directions[' + listIndex +
'].InstructionText" id="Directions[' + listIndex + ']" class="noTextborder">' + $('#instructionText').val() + '</textarea>';
var deleteButton = '<input type="button" class="cancel" value="X"';
var newRow = '<tr class="recipeInstructionRow"><td class="ten">' + stepNumber + ' </td><td class="eighty">' + instruction + '</td><td class="ten">' + deleteButton + '</td></tr>';
if ($('#instructionText').val().length == 0)
alert("You can't add a blank instruction.");
else
$('#instructionTable').append(newRow);
$('#instructionText').val("");
}
/* Script for removing instruction table row when 'X' button is clicked */
$('#instructionTable').on('click', 'tr input.cancel', function () {
$(this).closest('tr').remove();
$('#instructionTable tr').each(function () {
var rowIndex = $(this).index() + 1;
$('td:first-child', this).find('input').val(rowIndex);
});
});
最后,控制器方法:
[Authorize]
[HttpPost("Recipe/Create")]
public IActionResult Create(Recipe recipe)
{
try
{
if (ModelState.IsValid)
{
var userEmail = User.FindFirst(ClaimTypes.Email).Value;
var user = userData.GetUser(userEmail);
this.recipeData.CreateRecipe(recipe, user.UserId, DateTime.Now);
return RedirectToAction("Index");
}
else
return View("Create", recipe);
}
catch (MySqlException mySqlEx)
{
throw new Exception(mySqlEx.Message);
}
catch (Exception ex)
{
Console.WriteLine(ex);
return StatusCode(500, "Something went wrong. Please try again");
}
}
如果您提交包含所有必填字段的有效表单,则模型将传递给控制器并且一切正常。
但是,如果您提交了无效的表单并返回到视图,则食谱标题和描述将传递回表单,但两个列表都为空(例如,此列表属性本身为空)。在我的一生中,我无法弄清楚在从 View 到 Controller 再回到 View 的过程中,列表属性在哪里或为什么会变为 null。
有什么想法吗?
【问题讨论】:
标签: c# jquery asp.net-mvc asp.net-core