簡體   English   中英

在Foreach或For循環中使用EditorFor(ASP.NET MVC + RAZOR)

[英]Using EditorFor in Foreach or For loop (ASP.NET MVC + RAZOR)

我目前正在ASP.NET MVC項目中實現Family Tree系統。 為了設置族成員之間的關系,我需要每行顯示兩個ComboBox / DropDownList來定義從一個成員到另一個成員的關系。

首先,我將分享我的代碼,然后我將解釋我到目前為止嘗試過的方式以及最后的結果。

視圖模型

public class FamilyTreeRelationshipViewModel
{

    [ScaffoldColumn(false)]
    public string FromMemberId { get; set; }

    [ScaffoldColumn(false)]
    public string ToMemberId { get; set; }


    public Member FromMember { get; set; }
    public IEnumerable<Member> ToMembers { get; set; }


    [UIHint("FTComboBox")]
    [AdditionalMetadata("BindTo", "relationships")]
    [Required]
    [Display(Name = "From Relationship")]
    public string FromRelationship { get; set; }


    [UIHint("FTComboBox")]
    [AdditionalMetadata("BindTo", "relationships")]
    [Required]
    [Display(Name = "To Relationship")]
    public string ToRelationship { get; set; }
}

調節器

public class FamilyTreeController : Controller
{
    private AppMVC db = new AppMVC();


    public ActionResult Index(Guid? cid, Guid? mid)
    {

        if (cid == null && mid == null)
        {
            return new HttpStatusCodeResult(HttpStatusCode.NotFound);
        }


        var frommember = db.Member.FirstOrDefault(x => x.MemberId == mid && x.CaseId == cid && x.Deleted == false);
        var tomembers = db.Member.Where(x => x.CaseId == cid && x.MemberId != mid.Value && x.Deleted == false).ToList();


        ViewBag.cid = cid;
        ViewBag.mid = mid;

        PopulateRelationship();


        var familyTreeRelationshipViewModel = new FamilyTreeRelationshipViewModel
        {
            FromMember = frommember,
            ToMembers = tomembers,
        };

        return View(familyTreeRelationshipViewModel);
    }



    public void PopulateRelationship()
    {
        var relationship = db.RelationshipDD
        .Where(c => c.Deleted == false && c.Code != "PA")
        .OrderBy(c => c.OrderIndex)
        .Select(c => new RelationshipDDViewModel
        {
            Code = c.Code,
            Definition = c.Definition
        })
        .ToList();

        ViewData["relationships"] = relationship;

    }

}

ComboBox的編輯器模板

@model object

@{
    var bindto = ViewData.ModelMetadata.AdditionalValues["BindTo"].ToString();
    var fieldname = ViewData.ModelMetadata.PropertyName;
    var prefix = ViewData.TemplateInfo.HtmlFieldPrefix;

}

@(Html.Kendo().ComboBoxFor(m => m)
          .Filter("contains")
          .Placeholder(ViewData.ModelMetadata.DisplayName)
          .DataTextField("Definition")
          .DataValueField("Code")
          .HtmlAttributes(new { style = "width: 100%", Id = prefix})
          .BindTo((System.Collections.IEnumerable)ViewData[bindto])

          )

@Html.ValidationMessageFor(m => m, "", new { @class = "mdc-text-red-400" })

為了向每一行顯示一個新結果,我在View中使用了foreach

@if (Model.ToMembers != null)
{
    if (Model.ToMembers.Any())
    {
        foreach (var othermembers in Model.ToMembers.OrderBy(x => x.MemberNumberSuffix))
        {
            @Html.EditorFor(m => m.ToRelationship)
            @Html.EditorFor(m => m.FromRelationship)
        }
    }
}

正如您在屏幕截圖中看到的那樣,只渲染了第一行中的ComboBox。 我假設這是因為每個ComboBox的控制ID相同。 我檢查了瀏覽器開發人員工具(F12),他們都有相同的ID。 的ForEach

后來我以為我應該用For而不是Foreach,看看會發生什么:

@if (Model.ToMembers != null)
{
    if (Model.ToMembers.Any())
    {
        for (var i = 0; i < Model.ToMembers.Count(); i++)
        {
            var othermembers = Model.ToMembers.ToList()[i];

            @Html.EditorFor(m => m.ToRelationship[i])
            @Html.EditorFor(m => m.FromRelationship[i])
        }
    }
}

正如您在屏幕截圖中看到的那樣,所有ComboBox都已消失,所有內容都被渲染為Char 這里唯一的區別是每個控件/輸入都有它自己的Id,這很好,但並不像我期望的那樣好。 對於

畢竟,我決定使用內置DropDownList(MVC)來達到這個目的,結果也是如此。 因為我認為Telerik控件有問題。

我甚至試圖直接在View中使用ComboBox而不是EditorFor ,結果是不同的。 每一行都是單獨成功渲染的,但它又是Char類型,甚至錯誤信息都說明了這一點。 通常它應該說,“從關系字段是必需的”。

@if (Model.ToMembers != null)
{
    if (Model.ToMembers.Any())
    {
        for (var i = 0; i < Model.ToMembers.Count(); i++)
        {

            @(Html.Kendo().ComboBoxFor(m => m.ToRelationship[i])
            .Filter("contains")
            .Placeholder(ViewData.ModelMetadata.DisplayName)
            .DataTextField("Definition")
            .DataValueField("Code")
            .HtmlAttributes(new { style = "width: 100%" })
            .BindTo((System.Collections.IEnumerable)ViewData["relationships"])
            )

            @(Html.Kendo().ComboBoxFor(m => m.FromRelationship[i])
            .Filter("contains")
            .Placeholder(ViewData.ModelMetadata.DisplayName)
            .DataTextField("Definition")
            .DataValueField("Code")
            .HtmlAttributes(new { style = "width: 100%" })
            .BindTo((System.Collections.IEnumerable)ViewData["relationships"])
            )
        }
    }
}

截圖: 直接


問題

  1. 為什么我不能為此目的使用EditorFor?
  2. 為什么類型已更改為Char,我該如何解決?
  3. 任何替代方法來實現這一目標?

在此先感謝您的幫助!

  1. 您可以但是您需要將ViewData傳遞給子模板才能在子視圖中訪問它,因為我的理解是Viewdata不是全局可用的(對請求是全局的)對象,它只對當前有效view(除非傳入,否則不會顯示任何子視圖)

  2. 我認為這里的問題是你的視圖模型,你似乎試圖將所有行的下拉綁定到根視圖模型中的同一屬性,但我相信你可能更需要做的是在每個成員上設置關系值在根模型的ToMembers屬性中,例如...

作為一個只有seudocode的例子,感覺就像而不是......

foreach(var child in model.members)
{
    dropdownfor(m => m.FromRelationship, ViewData["relationships"]);
    dropdownfor(m => m.ToRelationship, ViewData["relationships"]);
}

...你應該通過建立設置屬性的下拉菜單來設置兩個人之間的關系,比如......

foreach(var child in model.members)
{
    dropdownfor(m => child.FromRelationship, ViewData["relationships"]);
    dropdownfor(m => child.ToRelationship, ViewData["relationships"]);
}

......以下是真實的......

  1. 第一個參數是您在下拉列表中設置所選項的值的屬性
  2. 第二個參數是您似乎存儲在viewdata對象中的“關系類型”列表
  3. 這存在於根視圖中,而不是某些模板/子視圖

....

  1. 我們如何才能達到預期的效果?

好吧,我們可以確保我們正在設置正確的東西開始,我認為你真正想要的是一個看起來像這樣的根模型......

public class FamilyTreeRelationshipViewModel
{
    public IEnumerable<MemberPair> FamilyMemberPairs{ get; set; }
    public IEnumerable<Relationship> Relationships { get;set; }
}

...從那里為每個成員你可以像這樣呈現你的行......

@foreach(var pair in Model.MemberPairs)
{
    @* Template out a member pair, pass through the set of relationships *@
    @Html.EditorFor(m => pair, Relationships)
}

..好的,從這里你需要一個名為“MemberPair.cshtml”的EditorTemplates文件夾中的一個視圖,它將模擬一個成員對對象,該對象將像這樣...

class MemberPair
{
   public Member From { get;set; }
   public Member To { get;set; }
   public int FromRelationshipId {get;set;}
   public int ToRelationshipId {get;set;}
}

...這個viewmodel定義了一對家庭成員,你想要定義一個家庭成員之間的關系,我們將使用我們將在視圖中生成的下拉列表來定義關系。 。

@Html.DisplayFor(m => m.From) 

@(Html.Kendo().ComboBoxFor(m => m.FromRelationshipId)
            .DataTextField("Definition")
            .DataValueField("Code")
            .DataSource((System.Collections.IEnumerable)ViewData["Relationships"])
            )

@(Html.Kendo().ComboBoxFor(m => m.ToRelationshipId)
            .DataTextField("Definition")
            .DataValueField("Code")
            .DataSource((System.Collections.IEnumerable)ViewData["Relationships"])
            )

@Html.DisplayFor(m => m.To)

在此上下文中,關系集合可以從viewdata獲得,因為我們在前一個視圖中顯式地傳遞了它。

我們還可以在這里注意到,當我們綁定下拉列表時,我們以一種在單個關系對對象上設置單個int關系id的方式進行綁定。

我認為你上面出錯的地方是你試圖將所有下拉列表綁定到父模型上的相同int值,我不知道MVC是否允許/適應,因為這會導致客戶端上的字段ID沖突。

免責聲明:

這個答案描述了實現背后的理論,但不是您可能需要的確切代碼,這也需要在控制器中進行一些邏輯更改來管理這些視圖的模型構造。

花點時間考慮構建和構建具有較小可管理組件的解決方案所需的結構。

這感覺就像一個分層的問題,而不是你可以在一個視圖中實現的東西,你也可以考慮使用一個kendo網格來做你的綁定,因為它能夠為你找出這些東西,但我不想建議使用一個復雜的控件,用復雜的綁定問題替換一個簡單的組合綁定問題。

首先,解釋你的錯誤。 您不能使用foreach循環為集合生成form控件。 它生成重復的name屬性,這些屬性無法綁定回集合(以及無效的html的重復id屬性)。 您需要使用for循環,或者更好的自定義EditorTemplate 第二個錯誤(當你使用for循環時)是因為你綁定的屬性是string ,所以綁定到m => m.ToRelationship[i]綁定到string的字符(typeof Char )。

真正的問題是您的視圖模型不正確,與您在視圖中顯示/編輯的內容沒有關系,您需要將下拉列表綁定到集合中的屬性。

此外,您對ToRelationshipFromRelationship使用2個組合框將導致許多潛在錯誤,您應該重新定義您的Relationship表以包括(比方說)以下字段

ID, Relationship, MaleReverseRelationship, FemaleReverseRelationship

所以典型的行可能包含

1, Son, Father, Mother
2, Wife, Husband, NULL
3, Niece, Uncle, Aunt

然后你需要一個下拉列表,如果傑克與羅傑的關系是Son ,你知道反向關系是Father (假設你的Member模型有一個Gender字段)。 這將極大地簡化您的視圖模型,使UI更簡單,並在用戶選擇錯誤的反向關系時防止用戶錯誤。

您沒有提供有關其他表/模型的任何信息,但是您需要一個包含字段的MemberRelationShip

ID, Member (FK to Members), OtherMember(FK to Members), RelationShip(FK to Relationships).

然后,您需要2個視圖模型,一個用於父Member ,另一個用於創建關系

public class ParentMemberVM
{
    public int ID { get; set; }
    public string Name { get; set; }
    .... other properties to display
    public IEnumerable<MemberVM> Members { get; set; }
    public IEnumerable<SelectListItem> RelationshipList { get; set; }
}
public class MemberVM
{
    public int ID { get; set; }
    public string Name { get; set; }
    public int Relationship { get; set; }
}

然后,你需要一個EditorTemplateMemberVM

/Views/Shared/EditorTemplates/MemberVM.cshtml

@model MemberVM
@Html.HiddenFor(m => m.ID)
@Html.DisplayFor(m => m.Name)
@Html.DropDownListFor(m => m.Relationship, (IEnumerable<SelectListItem>)ViewData["relationships"])

並在主視圖中

@model ParentMemberVM
....
@using (Html.BeginForm())
{
    @Html.HiddenFor(m => m.ID)
    @Html.DislayFor(m => m.Name)
    @Html.EditorFor(m => m.Members, new { relationships = Model.RelationshipList })
    <input type="submit" value="Save" />
}
  1. 作為替代解決方案,您可能根本不使用HTML幫助程序並編寫自己的HTML。 使用HTML幫助程序會導致諸如您遇到的問題。 你可以克服它們,但是如果不是幫助你,那么使用HTML助手會產生什么困難呢? (在我看來,MVC是關於控制你為瀏覽器提供的前端代碼。)

使用Razor,直接編寫所需的HTML可以比讓HTML幫助程序為您執行此操作更有效。

當然,這是我的觀點。 我在這里對一些類似的問題進行詳細介紹。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM