หากเราไม่คิดว่านี่เป็นข้อบกพร่องที่ทีมควรแก้ไขเมื่อเช่า MSDN ควรปรับปรุงเอกสาร ความสับสนจริงๆมาจากเอกสารที่ไม่ดีนี้ ในMSDNจะอธิบายชื่อพารามิเตอร์ เป็น
Type: System.String
The name of the form field to return.
นี่หมายถึง html สุดท้ายที่สร้างขึ้นจะใช้พารามิเตอร์นั้นเป็นชื่อของอินพุตที่เลือก แต่จริงๆแล้วมันมีความหมายมากกว่านั้น
ฉันเดาว่าผู้ออกแบบสันนิษฐานว่าผู้ใช้จะใช้โมเดลมุมมองเพื่อแสดงรายการแบบเลื่อนลงและจะใช้โพสต์กลับไปที่โมเดลมุมมองเดียวกัน แต่ในหลาย ๆ กรณีเราไม่ได้ปฏิบัติตามสมมติฐานนั้นจริงๆ
ใช้ตัวอย่างด้านบน
public class Person {
public int Id { get; set; }
public string Name { get; set; }
}
หากเราทำตามสมมติฐานเราควรกำหนดโมเดลมุมมองสำหรับมุมมองที่เกี่ยวข้องกับรายการแบบเลื่อนลงนี้
public class PersonsSelectViewModel{
public string SelectedPersonId,
public List<SelectListItem> Persons;
}
เนื่องจากเมื่อโพสต์กลับเฉพาะค่าที่เลือกเท่านั้นที่จะโพสต์กลับดังนั้นจึงถือว่าควรโพสต์กลับไปยังคุณสมบัติของโมเดล SelectedPersonId ซึ่งหมายถึงชื่อพารามิเตอร์แรกของ Html.DropDownList ควรเป็น 'SelectedPersonId' ดังนั้นนักออกแบบจึงคิดว่าเมื่อแสดงมุมมองแบบจำลองในมุมมองคุณสมบัติของโมเดล SelectedPersonId ควรเก็บค่าเริ่มต้นของรายการดรอปดาวน์นั้นไว้ แม้จะคิดว่าบุคคล <SelectListItem> รายชื่อของคุณได้ตั้งค่าสถานะที่เลือกแล้วเพื่อระบุว่ารายการใดถูกเลือก / ค่าเริ่มต้น แต่ tml.DropDownList จะเพิกเฉยต่อสิ่งนั้นและสร้าง IEnumerable <SelectListItem> ของตัวเองและตั้งค่ารายการเริ่มต้น / ที่เลือกตามชื่อ
นี่คือรหัสจาก asp.net mvc
private static MvcHtmlString SelectInternal(this HtmlHelper htmlHelper, ModelMetadata metadata,
string optionLabel, string name, IEnumerable<SelectListItem> selectList, bool allowMultiple,
IDictionary<string, object> htmlAttributes)
{
...
bool usedViewData = false;
// If we got a null selectList, try to use ViewData to get the list of items.
if (selectList == null)
{
selectList = htmlHelper.GetSelectData(name);
usedViewData = true;
}
object defaultValue = (allowMultiple) ? htmlHelper.GetModelStateValue(fullName, typeof(string[])) : htmlHelper.GetModelStateValue(fullName, typeof(string));
// If we haven't already used ViewData to get the entire list of items then we need to
// use the ViewData-supplied value before using the parameter-supplied value.
if (defaultValue == null && !String.IsNullOrEmpty(name))
{
if (!usedViewData)
{
defaultValue = htmlHelper.ViewData.Eval(name);
}
else if (metadata != null)
{
defaultValue = metadata.Model;
}
}
if (defaultValue != null)
{
selectList = GetSelectListWithDefaultValue(selectList, defaultValue, allowMultiple);
}
...
return tagBuilder.ToMvcHtmlString(TagRenderMode.Normal);
}
ดังนั้นโค้ดจึงไปไกลกว่านั้นจริง ๆ แล้วไม่เพียง แต่พยายามค้นหาชื่อในแบบจำลองเท่านั้น แต่ยังอยู่ใน viewdata ทันทีที่พบรหัสจะสร้างรายการที่เลือกขึ้นมาใหม่และไม่สนใจรายการที่เลือกเดิมของคุณ
ปัญหาคือในหลาย ๆ กรณีเราไม่ได้ใช้มันอย่างนั้นจริงๆ เราแค่ต้องการโยนรายการที่เลือกด้วยรายการเดียว / หลายรายการชุดที่เลือกเป็นจริง
แน่นอนว่าวิธีแก้ปัญหานั้นง่ายมากใช้ชื่อที่ไม่มีในโมเดลหรือใน viewdata เมื่อไม่พบรายการที่ตรงกันจะใช้รายการเลือกเดิมและรายการที่เลือกเดิมจะมีผล
แต่ฉันยังคิดว่า mvc ควรปรับปรุงโดยเพิ่มเงื่อนไขอีกหนึ่งข้อ
if ((defaultValue != null) && (!selectList.Any(i=>i.Selected)))
{
selectList = GetSelectListWithDefaultValue(selectList, defaultValue, allowMultiple);
}
เพราะถ้า selectList เดิมมีรายการที่เลือกไว้แล้วทำไมคุณถึงเพิกเฉย
แค่ความคิดของฉัน