具有这个复杂的模型:
public class Food
{
public int FoodID { get; set; }
public string FoodNameEN { get; set; }
public string FoodDescriptionEN { get; set; }
public int CategoryID { get; set; }
public bool Availability { get; set; }
public bool DailyMenu { get; set; }
public virtual Diet Diet { get; set; }
public virtual Category Category { get; set; }
public virtual ICollection<FoodAttributeValue> FoodAttributeValues { get; set; } = new HashSet<FoodAttributeValue>();
public class Category
{
public int CategoryID { get; set; }
public string CategoryEN { get; set; }
public bool Availability { get; set; }
public virtual ICollection<Food> Foods { get; set; } = new HashSet<Food>();
}
public class Attribute
{
public int AttributeID { get; set; }
public string AttributeNameEN { get; set; }
public virtual ICollection<AttributeValue> AttributeValues { get; set; } = new HashSet<AttributeValue>();
}
public class AttributeValue
{
public int AttributeValueID { get; set; }
public int AttributeID { get; set; }
public string AttributeValueEN { get; set; }
public virtual Attribute Attribute { get; set; }
public virtual ICollection<FoodAttributeValue> FoodAttributeValues { get; set; } = new HashSet<FoodAttributeValue>();
}
public class FoodAttributeValue
{
public int FoodAttributeValueID { get; set; }
public int FoodID { get; set; }
public int AttributeValueID { get; set; }
public decimal Price { get; set; }
public virtual Food Food { get; set; }
public virtual AttributeValue AttributeValue { get; set; }
}
使用 FoodIndexViewModel 如下所示,我可以列出食物属性及其相关的所有内容,例如其属性(大小或体积),其 AttributeValues (正常大小,大号...),最后是其价格,正如您在 FoodAttributeValue 中看到的那样,它取决于 FoodID 和< em> AttributeValueID 。
public class FoodIndexViewModel
{
public int FoodID { get; set; }
public string FoodNameEN { get; set; }
public string FoodDescriptionEN { get; set; }
public int CategoryID { get; set; }
public bool Availability { get; set; }
public bool DailyMenu { get; set; }
public Diet Diet { get; set; }
public ICollection<FoodAttributeValue> FoodAttributeValues { get; set; } = new List<FoodAttributeValue>();
}
我正在尝试对食品实施CRUD。 对于“创建/编辑”,我需要这样的内容:
使用Jquery填充 Attributes 下拉列表,并使用 onchange 事件对其进行调整,以适应所有 Attribute Values 的列表。 属性已选择。
如何将选中的 AttributeValues 绑定到 FoodAttributeValues 集合以进行编辑和创建表单?
更新:即使不是很满意,我还是设法实现了目标
我创建了 AttributeValuesViewModel :
public class AttributeValuesViewModel
{
public int? AttributeID { get; set; }
public int AttributeValueID { get; set; }
public string AttributeValueName { get; set; }
public bool IsChecked { get; set; }
[RegularExpression("^[0-9]*$", ErrorMessage = "Price must be a number 0 or higher")]
[Required(ErrorMessage = "Please specify the Price")]
public decimal Price { get; set; }
}
编辑GET:
//GET: Admin/Foods/Edit/5
public ActionResult Edit(int? id, int? attrID)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Food food = _db.Foods
.Include(f => f.Category)
.Include(f => f.Diet)
.Include(f => f.FoodAttributeValues)
.SingleOrDefault(f => f.FoodID == id);
if (food == null)
{
return HttpNotFound();
}
ViewBag.CategoryID = new SelectList(_db.Categories, "CategoryID", "CategoryEN");
// if "attrID" null, get the selected AttributeID from the attribute values the food has, else set it to the value provided.
// In this case dropdown was changed on edit food page
var selectedFoodAttrId = attrID == null ? food.FoodAttributeValues.FirstOrDefault().AttributeValue.AttributeID : attrID.Value;
ViewBag.AttributeID = new SelectList(_db.Attributes, "AttributeID", "AttributeNameEN", selectedFoodAttrId);
PopulateCheckedAttributeValues(food, selectedFoodAttrId);
return View(food);
}
private List<AttributeValuesViewModel> PopulateCheckedAttributeValues(Food food, int attributeId)
{
// All AttributeValues in DB
var allAttributeValues = _db.AttributeValues.Where(av => av.AttributeID == attributeId);
// The AttributeValueIDs of food checked AttributeValues
var foodCheckedAttributeValues = new HashSet<int>(food.FoodAttributeValues.Select(f => f.AttributeValueID));
var viewModel = new List<AttributeValuesViewModel>();
foreach (var attrValue in allAttributeValues)
{
viewModel.Add(new AttributeValuesViewModel
{
AttributeID = attributeId,
AttributeValueID = attrValue.AttributeValueID,
AttributeValueName = attrValue.AttributeValueEN,
Price = food.FoodAttributeValues.Where(fa => fa.AttributeValueID == attrValue.AttributeValueID).Select(fa => fa.Price).SingleOrDefault(),
IsChecked = foodCheckedAttributeValues.Contains(attrValue.AttributeValueID)
});
}
//ViewBag.AttributeValues = viewModel;
TempData["AttributeValues"] = viewModel;
return viewModel;
}
编辑POST:
// POST: Admin/Foods/Edit/5
[HttpPost]
[ValidateAntiForgeryToken]
[ActionName("Edit")]
public ActionResult EditPost(int? id, string[] foodCheckedAttributeValues, decimal[] price, int AttributeID)
{
Food foodToUpdate = _db.Foods
.Include(f => f.Category)
.Include(f => f.Diet)
.Include(f => f.FoodAttributeValues)
.Single(f => f.FoodID == id);
// Should not happen, but if for some reason form is posted with no price
if (price == null)
{
TempData["Error"] = $"Food edit Failed! Fill at least one Price!";
return RedirectToAction("Edit", new { id, foodCheckedAttributeValues});
}
if (TryUpdateModel(foodToUpdate))
{
UpdateAttributeValues(foodCheckedAttributeValues, price, foodToUpdate);
try
{
_db.SaveChanges();
TempData["message"] = $"Food with ID: {id} modified successfully!";
return RedirectToAction("Index");
}
catch (DbUpdateException updEx)
{
if (updEx.InnerException != null && updEx.InnerException.ToString().Contains("Violation of UNIQUE KEY constraint"))
{
TempData["error"] = $"Food edit failed! Food Names must be uniqe, and this one already exists in the database!";
}
}
catch (Exception e)
{
TempData["Error"] = $"Food edit Failed!!";
}
}
//PopulateCheckedAttributeValues(foodToUpdate, AttributeID);
//ViewBag.AttributeID = new SelectList(_db.Attributes, "AttributeID", "AttributeNameEN", AttributeID);
//ViewBag.CategoryID = new SelectList(_db.Categories, "CategoryID", "CategoryEN");
//return View(foodToUpdate);
return RedirectToAction("Edit", new {id = foodToUpdate.FoodID});
}
// Loads only the partial view, no refresh of the page
public PartialViewResult EditFoodAttributesPartial(int? id, int attrId)
{
Food food = _db.Foods
.Include(f => f.Category)
.Include(f => f.Diet)
.Include(f => f.FoodAttributeValues)
.SingleOrDefault(f => f.FoodID == id);
var model = PopulateCheckedAttributeValues(food, attrId);
return PartialView("_EditFoodAttributesPartial", model);
}
private void UpdateAttributeValues(string[] foodCheckedAttributeValues, decimal[] price, Food foodToUpdate)
{
// Has Attribute Selected
if (foodCheckedAttributeValues != null && foodCheckedAttributeValues.Length > 0)
{
for(int i = 0; i < foodCheckedAttributeValues.Length; i++ )
{
FoodAttributeValue fa = new FoodAttributeValue
{
AttributeValueID = Convert.ToInt32(foodCheckedAttributeValues[i]),
FoodID = foodToUpdate.FoodID,
Price = price[i]
};
_db.FoodAttributeValues.Add(fa);
}
}
// No Attributes
else
{
FoodAttributeValue fa = new FoodAttributeValue
{
AttributeValueID = 0,
FoodID = foodToUpdate.FoodID,
Price = price[0]
};
_db.FoodAttributeValues.Add(fa);
}
var favToDelete = _db.FoodAttributeValues.Where(fa => fa.FoodID == foodToUpdate.FoodID);
foreach (var fa in favToDelete)
{
_db.FoodAttributeValues.Remove(fa);
}
}
在属性值更改事件后如何加载部分:
编辑页面:
// The div where it resides in the complete edit page
<div id="attributesDiv" class="col-7">
@Html.Partial("_EditFoodAttributesPartial", (List<CodeFirstCosi.UI.Areas.Admin.ViewModels.AttributeValuesViewModel>)TempData["AttributeValues"])
</div>
@section scripts {
@Scripts.Render("~/bundles/jqueryval")
<script>
$(document).ready(function () {
$('#submitButton').prop("disabled", false);
// For the first load, before any Attribute ID change event
$("#attributesTable input[type=checkbox]").each(function () {
$(this).change(function () {
if ($(this).is(':checked')) {
$('#' + $(this).attr('id') + 'Price').prop('disabled', false);
// at least one is checked so remove the error on submit
$('#errorTr').remove();
} else {
$('#' + $(this).attr('id') + 'Price').prop('disabled', true);
}
});
});
// Attribute dropdown changed event
$('#AttributeName').change(function () {
$("#attributesDiv").load("@Url.Action("EditFoodAttributesPartial", new { id = Model.FoodID})?&attrID=" + $('#AttributeName').val(), function () {
});
</script>
}
部分视图:
@model List<CodeFirstCosi.UI.Areas.Admin.ViewModels.AttributeValuesViewModel>
@{
var attrId = Model.FirstOrDefault().AttributeID;
if (attrId != 0)
{
<table id="attributesTable" class="table table-borderless">
<thead>
<tr>
<td>Attribute Value</td>
<td>Price</td>
</tr>
</thead>
<tbody>
@foreach (var attrValue in Model)
{
var AttributeID = attrValue.AttributeID;
var AttributeValueID = attrValue.AttributeValueID;
var AttributeValueName = attrValue.AttributeValueName;
var Price = attrValue.Price;
var IsChecked = attrValue.IsChecked;
var checkboxID = "AV" + AttributeValueID;
var priceID = checkboxID + "Price";
<tr>
<td>
<input type="checkbox"
id="@checkboxID"
name="foodCheckedAttributeValues"
value="@AttributeValueID"
@(Html.Raw(IsChecked ? "checked=\"checked\"" : ""))
/>
<label for="@checkboxID">@AttributeValueName</label>
</td>
<td>
<input type="number"
step=".01"
min="0"
name="Price"
id="@priceID"
required="required"
value="@Price"
class="form-control form-control-sm"
@(Html.Raw(!IsChecked ? "disabled" : "")) />
</td>
</tr>
}
</tbody>
</table>
}
else
{
foreach (var attrValue in Model)
{
var AttributeValueID = attrValue.AttributeValueID;
var Price = attrValue.Price;
var checkboxID = "AV" + AttributeValueID;
var priceID = checkboxID + "Price";
<div class="form-group">
<label>Price</label>
<input type="number"
step=".01"
min="0"
name="Price"
id="@priceID"
required="required"
value="@Price"
class="form-control form-control-sm" />
</div>
}
}
}
在功能方面,它可以满足我的需求。但是我无法绑定视图模型AttributeValuesViewModel来使验证工作。在局部视图中,我希望这些输入是使用@Html帮助器生成的,例如:
<tr>
<td>
@Html.EditorFor(model => attrValue.IsChecked, new { htmlAttributes = new { @id = @checkboxID, @value = @AttributeValueID, @class = "form-control form-control-sm" } })
@Html.ValidationMessageFor(model => attrValue.IsChecked, "", new { @class = "text-danger" })
@Html.LabelFor(model => attrValue.AttributeValueName, new { htmlAttributes = new { @class = "form-control form-control-sm" } })
</td>
<td>
@Html.EditorFor(model => attrValue.Price, new { htmlAttributes = new { @id = @priceID, @class = "form-control form-control-sm" } })
@Html.ValidationMessageFor(model => attrValue.Price, "", new { @class = "text-danger" })
</td>
</tr>
为了触发mvc验证,但是,如果执行此操作,我将获得不同的输入名称,并且无法接收绑定到相应操作的值。
有帮助吗?