ASP.NET MVC Html.DropDownList SelectedValue


103

ฉันได้ลองแล้วคือ RC1 แล้วอัปเกรดเป็น RC2 ซึ่งไม่สามารถแก้ไขปัญหาได้

// in my controller
ViewData["UserId"] = new SelectList(
    users, 
    "UserId", 
    "DisplayName", 
    selectedUserId.Value); // this has a value

ผลลัพธ์: คุณสมบัติ SelectedValue ถูกตั้งค่าบนวัตถุ

// in my view
<%=Html.DropDownList("UserId", (SelectList)ViewData["UserId"])%>

ผลลัพธ์: ตัวเลือกที่คาดหวังทั้งหมดจะแสดงผลไปยังไคลเอนต์ แต่ไม่ได้ตั้งค่าแอตทริบิวต์ที่เลือกไว้ รายการใน SelectedValue มีอยู่ภายในรายการ แต่รายการแรกในรายการจะถูกเลือกโดยปริยายเสมอ

ฉันควรทำอย่างไร

อัปเดต ขอบคุณคำตอบของ John Feminella ฉันพบว่าปัญหาคืออะไร "UserId" เป็นคุณสมบัติใน Model my View ที่พิมพ์ลงไปอย่างมาก เมื่อ Html.DropDownList ("UserId" ถูกเปลี่ยนเป็นชื่ออื่น แต่ "UserId" ค่าที่เลือกจะแสดงผลอย่างถูกต้อง

สิ่งนี้ส่งผลให้ค่าไม่ถูกผูกไว้กับโมเดลแม้ว่า


คุณแน่ใจหรือไม่ว่ามีค่าอยู่ในรายการ
John Sheehan

ปัญหานี้ยังคงมีอยู่ในรุ่นที่ 1 ของ ASP.NET MVC
Mathias F

SelectUserId คืออะไร!?
fulvio

คุณคิดว่าจะผูกค่าในการอัปเดตได้อย่างไรหากชื่อของข้อมูลที่คุณป้อนไม่เหมือนกับคุณสมบัติของคุณ?
ryudice

คำตอบ:


68

นี่คือวิธีที่ฉันแก้ไขปัญหานี้:

ฉันมีสิ่งต่อไปนี้:

ตัวควบคุม:

ViewData["DealerTypes"] = Helper.SetSelectedValue(listOfValues, selectedValue) ;

ดู

<%=Html.DropDownList("DealerTypes", ViewData["DealerTypes"] as SelectList)%>

เปลี่ยนแปลงดังต่อไปนี้:

ดู

<%=Html.DropDownList("DealerTypesDD", ViewData["DealerTypes"] as SelectList)%>

ดูเหมือนว่า DropDown ต้องไม่มีชื่อเดียวกันมีชื่อ ViewData: S แปลก แต่ใช้งานได้


19
+1 สำหรับ "DropDown ต้องไม่มีชื่อเดียวกันมีชื่อ ViewData" ขอบคุณมาก
Daniel Dyson

ขอบคุณ!!! คุณเพิ่งแก้ปัญหาของฉันหลังจากหลายชั่วโมงที่สงสัยว่าทำไมมันถึงไม่ได้ผล :)
เจน

1
สิ่งนี้ใช้ได้กับค่าที่เลือก แต่ปัญหาเกิดขึ้นเมื่อคุณพยายามอัปเดต "DealerTypes" ในฐานข้อมูลเนื่องจากตอนนี้ DropDownList ถูกผูกไว้กับ "DealerTypesDD" ซึ่งไม่มีอยู่ในโมเดล วิธีแก้ปัญหาคือการเพิ่มฟิลด์ที่ซ่อนอยู่และ htmlAttributes ไปยัง DropDownList: <input type = "hidden" name = "DealerTypes" id = "DealerTypes" value = "" /> <% = Html.DropDownList ("DealerTypesDD", ViewData [ "DealerTypes"] เป็น SelectList ใหม่ {@onchange = "DealerTypes.value = this.value"})%>
Zim

3
คุณควรเพิ่ม 'DS' ในชื่อ viewdata ดังนั้นจึงไม่ทำลายการผูกโมเดล ...
noocyte

1
ห่าอะไร! สิ่งนี้ไม่ได้รับการแก้ไขใน MVC5 ??? เช่นเดียวกับเคล็ดลับอื่น ๆ ที่กล่าวถึงโดย @Paul Hatcher ให้คัดลอก id ที่เลือกลงใน ViewData ["DealerTypes"]
jsgoupil

51

ลองสิ่งนี้:

public class Person {
    public int Id { get; set; }
    public string Name { get; set; }
}

แล้ว:

var list = new[] {   
    new Person { Id = 1, Name = "Name1" }, 
    new Person { Id = 2, Name = "Name2" }, 
    new Person { Id = 3, Name = "Name3" } 
};

var selectList = new SelectList(list, "Id", "Name", 2);
ViewData["People"] = selectList;

Html.DropDownList("PeopleClass", (SelectList)ViewData["People"])

ด้วย MVC RC2 ฉันจะได้รับ:

<select id="PeopleClass" name="PeopleClass">
    <option value="1">Name1</option>
    <option selected="selected" value="2">Name2</option>
    <option value="3">Name3</option>
</select>

สิ่งนี้มีประโยชน์ในการติดตามสาเหตุ ฉันได้อัปเดตคำถามของฉันเพื่อให้สอดคล้องกับการเพิ่มเติม
blu

เป็นครั้งแรกในรุ่น? อันดับสองในคอนโทรลเลอร์? และสามมองเห็นชัดเจน แต่ 2 ตัวแรก .. ???
rr

คำตอบที่ดี แต่ใช้ ViewModel ไม่ดีกว่าเหรอ?
Jess

@ เจส: ฉันเขียนคำตอบนี้ในเดือนมีนาคม 2552 เมื่อ 5 ปีที่แล้ว เวลาเปลี่ยนไป! อย่าลังเลที่จะอัปเดต :)
John Feminella

5

คุณยังคงสามารถตั้งชื่อ DropDown เป็น "UserId" ได้และยังคงให้การผูกโมเดลทำงานได้อย่างถูกต้องสำหรับคุณ

ข้อกำหนดเดียวในการทำงานคือคีย์ ViewData ที่มี SelectList ไม่มีชื่อเดียวกับคุณสมบัติ Model ที่คุณต้องการผูก ในกรณีเฉพาะของคุณสิ่งนี้จะเป็น:

// in my controller
ViewData["Users"] = new SelectList(
    users, 
    "UserId", 
    "DisplayName", 
    selectedUserId.Value); // this has a value

// in my view
<%=Html.DropDownList("UserId", (SelectList)ViewData["Users"])%>

สิ่งนี้จะสร้างองค์ประกอบที่เลือกชื่อ UserId ซึ่งมีชื่อเดียวกับคุณสมบัติ UserId ในโมเดลของคุณดังนั้นตัวประสานโมเดลจะตั้งค่าด้วยค่าที่เลือกในองค์ประกอบการเลือกของ html ที่สร้างโดยตัวช่วย Html.DropDownList

ฉันไม่แน่ใจว่าเหตุใดตัวสร้าง Html.DropDownList นั้นจึงไม่เลือกค่าที่ระบุใน SelectList เมื่อคุณใส่รายการที่เลือกใน ViewData ด้วยคีย์ที่เท่ากับชื่อคุณสมบัติ ฉันสงสัยว่ามันมีส่วนเกี่ยวข้องกับวิธีใช้ตัวช่วย DropDownList ในสถานการณ์อื่น ๆ โดยที่หลักการคือคุณมี SelectList ใน ViewData ที่มีชื่อเดียวกับคุณสมบัติในโมเดลของคุณ สิ่งนี้จะทำงานได้อย่างถูกต้อง:

// in my controller
ViewData["UserId"] = new SelectList(
    users, 
    "UserId", 
    "DisplayName", 
    selectedUserId.Value); // this has a value

// in my view
<%=Html.DropDownList("UserId")%>

ขอบคุณเพื่อนมากเหนือคำตอบทั้งหมดที่ได้ผลสำหรับฉันไม่รู้ว่าทำไม แต่มันเป็นเช่นนั้น
Lamin Sanneh

ในที่สุด! หลังจากพยายามมาหลายชั่วโมงในที่สุดฉันก็พบ "ข้อกำหนดเท่านั้น" ของคุณ ที่ทำมัน
SteveCav

2

หากเราไม่คิดว่านี่เป็นข้อบกพร่องที่ทีมควรแก้ไขเมื่อเช่า 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 เดิมมีรายการที่เลือกไว้แล้วทำไมคุณถึงเพิกเฉย

แค่ความคิดของฉัน


นี่เป็นคำอธิบายที่ดีที่สุดที่ฉันเคยอ่านมา ช่วยให้ฉันปวดหัวมาก สิ่งที่ฉันต้องทำคือตั้งค่าของคุณสมบัติ viewmodel และใช้งานได้ ขอบคุณ.
Moses Machua

2

รหัสในโพสต์ MVC 3 ก่อนหน้าไม่ทำงาน แต่เป็นการเริ่มต้นที่ดี ฉันจะซ่อมมัน. ฉันได้ทดสอบรหัสนี้และใช้งานได้ใน MVC 3 Razor C # รหัสนี้ใช้รูปแบบ ViewModel เพื่อเติมคุณสมบัติที่ส่งคืนไฟล์List<SelectListItem>.

คลาส Model

public class Product
{
    public string Name { get; set; }
    public decimal Price { get; set; }
}

คลาส ViewModel

using System.Web.Mvc;

public class ProductListviewModel
{
    public List<SelectListItem> Products { get; set; }
}

วิธีการควบคุม

public ViewResult List()
{
    var productList = new List<SelectListItem>();

    foreach (Product p in Products)
    {
        productList.Add(new SelectListItem
        {
            Value = p.ProductId.ToString(),
            Text = "Product: " + p.Name + " " + p.Price.ToString(),
            // To set the selected item use the following code 
            // Note: you should not set every item to selected
            Selected = true
        });
    }

    ProductListViewModel productListVM = new ProductListViewModeld();

    productListVM.Products = productList;

    return View(productListVM);
}

มุมมอง

@model MvcApp.ViewModels.ProductListViewModel

@using (Html.BeginForm())
{
    @Html.DropDownList("Products", Model.Products)
}

ผลลัพธ์ HTML จะเป็นอย่างไร

<select id="Products" name="Products">
    <option value="3">Product: Widget 10.00</option>
    <option value="4">Product: Gadget 5.95</option>
</select>

ขึ้นอยู่กับว่าคุณจัดรูปแบบผลลัพธ์อย่างไร ฉันหวังว่านี่จะช่วยได้. รหัสใช้งานได้


1
ไม่ได้ตั้งค่าselectedตัวเลือกนี้
Jess

ในการตั้งค่ารายการที่เลือกโดยใช้ SelectListItem ให้ตั้งค่าคุณสมบัติที่เลือกเป็นจริง
wayne.blackmon

1

สิ่งนี้ดูเหมือนจะเป็นข้อบกพร่องในคลาส SelectExtensions เนื่องจากจะตรวจสอบ ViewData เท่านั้นแทนที่จะเป็นแบบจำลองสำหรับรายการที่เลือก ดังนั้นเคล็ดลับคือการคัดลอกรายการที่เลือกจากโมเดลไปยังคอลเลกชัน ViewData ภายใต้ชื่อของคุณสมบัติ

สิ่งนี้นำมาจากคำตอบที่ฉันให้ไว้ในฟอรัม MVC ฉันยังมีคำตอบที่สมบูรณ์กว่านี้ในบล็อกโพสต์ที่ใช้แอตทริบิวต์ DropDownList ของ Kazi ...

รับแบบจำลอง

public class ArticleType
{
   public Guid Id { get; set; }
   public string Description { get; set; }
}

public class Article
{
    public Guid Id { get; set; }
    public string Name { get; set; }
    public ArticleType { get; set; }
}

และโมเดลมุมมองพื้นฐานของ

public class ArticleModel
{
     public Guid Id { get; set; }
     public string Name { get; set; }

     [UIHint("DropDownList")]
     public Guid ArticleType { get; set; }
}

จากนั้นให้เราเขียนเทมเพลตตัวแก้ไข DropDownList ดังนี้ ..

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %>
<script runat="server">  
    IEnumerable<SelectListItem> GetSelectList()
    {
        var metaData = ViewData.ModelMetadata;
        if (metaData == null)
        {
            return null;
        }

        var selected = Model is SelectListItem ? ((SelectListItem) Model).Value : Model.ToString();
        ViewData[metaData.PropertyName] = selected;

        var key = metaData.PropertyName + "List";
        return (IEnumerable<SelectListItem>)ViewData[key];
    }
</script>
<%= Html.DropDownList(null, GetSelectList()) %>

สิ่งนี้จะใช้งานได้เช่นกันหากคุณเปลี่ยน ArticleType ในโมเดลมุมมองเป็น SelectListItem แม้ว่าคุณจะต้องใช้ตัวแปลงประเภทตามบล็อกของ Kazi และลงทะเบียนเพื่อบังคับให้ binder ถือว่าเป็นประเภทง่ายๆ

ในคอนโทรลเลอร์ของคุณเรามี ...

public ArticleController 
{
     ...
     public ActionResult Edit(int id)
     {
          var entity = repository.FindOne<Article>(id);
          var model = builder.Convert<ArticleModel>(entity);

          var types = repository.FindAll<ArticleTypes>();
          ViewData["ArticleTypeList"] = builder.Convert<SelectListItem>(types);

          return VIew(model);
     }
     ...
}

ขอบคุณสำหรับคำตอบฉันจะต้องตรวจสอบสิ่งนี้
blu

0

ปัญหาคือดรอปบ็อกซ์ไม่ทำงานเหมือนกับลิสต์บ็อกซ์อย่างน้อยก็เป็นไปตามที่ ASP.NET MVC2 ออกแบบไว้: ดรอปบ็อกซ์อนุญาตให้มีค่าศูนย์หรือค่าเดียวเท่านั้นเนื่องจากลิสต์บ็อกซ์สามารถเลือกได้หลายค่า ดังนั้นการเข้มงวดกับ HTML ค่านั้นจึงไม่ควรอยู่ในรายการตัวเลือกเป็นแฟล็ก "ที่เลือก" แต่อยู่ในอินพุตเอง

ดูตัวอย่างต่อไปนี้:

<select id="combo" name="combo" value="id2">
  <option value="id1">This is option 1</option>
  <option value="id2" selected="selected">This is option 2</option>
  <option value="id3">This is option 3</option>
</select>

<select id="listbox" name="listbox" multiple>
  <option value="id1">This is option 1</option>
  <option value="id2" selected="selected">This is option 2</option>
  <option value="id3">This is option 3</option>
</select>

คำสั่งผสมที่มีตัวเลือกที่เลือก แต่ยังมีชุดแอตทริบิวต์ความคุ้มค่า ดังนั้นหากคุณต้องการให้ ASP.NET MVC2 แสดงผลดรอปบ็อกซ์และมีค่าเฉพาะที่เลือกไว้ (เช่นค่าเริ่มต้นเป็นต้น) คุณควรกำหนดค่าในการแสดงผลดังนี้:

// in my view             
<%=Html.DropDownList("UserId", selectListItems /* (SelectList)ViewData["UserId"]*/, new { @Value = selectedUser.Id } /* Your selected value as an additional HTML attribute */)%>

0

ใน ASP.NET MVC 3 คุณสามารถเพิ่มรายการของคุณใน ViewData ...

var options = new List<SelectListItem>();

options.Add(new SelectListItem { Value = "1", Text = "1" });
options.Add(new SelectListItem { Value = "2", Text = "2" });
options.Add(new SelectListItem { Value = "3", Text = "3", Selected = true });

ViewData["options"] = options;

... แล้วอ้างอิงตามชื่อในมุมมองมีดโกนของคุณ ...

@Html.DropDownList("options")

คุณไม่จำเป็นต้อง "ใช้" รายการในการโทร DropDownList ด้วยตนเอง การทำเช่นนี้ตั้งค่าที่เลือกให้ถูกต้องด้วย

คำเตือน:

  1. ยังไม่ได้ลองสิ่งนี้กับเอ็นจิ้นมุมมองแบบฟอร์มเว็บ แต่ก็ควรใช้งานได้เช่นกัน
  2. ฉันยังไม่ได้ทดสอบสิ่งนี้ใน v1 และ v2 แต่อาจใช้ได้

0

ฉันจัดการเพื่อให้ได้ผลลัพธ์ที่ต้องการ แต่ด้วยวิธีการที่แตกต่างกันเล็กน้อย ในรายการแบบหล่นลงฉันใช้โมเดลแล้วอ้างอิง ไม่แน่ใจว่านี่คือสิ่งที่คุณกำลังมองหาหรือไม่

@Html.DropDownList("Example", new SelectList(Model.FeeStructures, "Id", "NameOfFeeStructure", Model.Matters.FeeStructures))

Model.Matters.FeeStructures ข้างบนคือรหัสของฉันซึ่งอาจเป็นค่าของรายการที่ควรเลือก

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.