在Foreach或For循环中使用EditorFor(ASP.NET MVC + RAZOR) [英] Using EditorFor in Foreach or For loop (ASP.NET MVC + RAZOR)

查看:81
本文介绍了在Foreach或For循环中使用EditorFor(ASP.NET MVC + RAZOR)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我目前正在ASP.NET MVC项目中实现家谱系统.为了设置家庭成员之间的关系,我需要每行显示两个ComboBox/DropDownList来定义一个成员与另一个成员之间的关系.

I'm currently implementing a Family Tree system in my ASP.NET MVC project. In order to set the relationship between family members, I need to display two ComboBox/DropDownList per row to define relationships from one member to the other.

首先,我将分享我的代码,然后,我将说明到目前为止我尝试过的方法以及最终的结果.

First I will share my codes and then I will explain what ways I've tried so far and what was the result at the end.

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;

    }

}

组合框的编辑器模板

@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 :

In order to show each row a new result, I used foreach in View:

@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.

As you see below in the screenshot, only ComboBoxes in the first row were rendered. I assume it's because of the same control Id for each ComboBox. I checked the browser developer tools (F12), all of them had the same Id.

稍后,我认为我应该使用For而不是Foreach来看看会发生什么:

Later I thought I should use For instead of Foreach and see what happens:

@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])
        }
    }
}

正如您在屏幕快照中所见,所有组合框都消失了,所有内容都已呈现为 Char .唯一的区别是每个控件/输入都有其自己的ID,这很好,但不像我期望的那样好.

As you see below in the screenshot, all of the ComboBoxes are gone and everything has been rendered as Char. The only difference here is that each control/input has it's own Id and that's good but not good as I was expecting.

毕竟,我决定为此使用内置的DropDownList(MVC),结果是相同的.因为我认为Telerik控件有问题.

After all, I decided to use the Built-in DropDownList (MVC) for this purpose and it was the same result. Because I thought something is wrong with Telerik controls.

我什至尝试直接在View内部使用ComboBox而不是 EditorFor ,结果却有所不同.每行都分别成功地呈现,但是仍然是Char类型,甚至错误消息也是如此.通常,它应该说来自关系字段是必需的".

I even tried to use the ComboBox directly inside the View instead of EditorFor, the result was different. Each row was rendered separately and successfully but it was again Char type and even the error message says that. Normally it should say, "From relationship field is required".

@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. 为什么我不能为此目的使用Editor?
  2. 为什么类型更改为Char,我该如何解决?
  3. 有其他方法可以实现这一目标吗?

提前感谢您的帮助!

推荐答案

首先,解释您的错误.您不能使用 foreach 循环为集合生成 form 控件.它会生成无法绑定回集合的重复的 name 属性(和重复的 id 属性,它们是无效的html).您需要使用 for 循环,或者最好使用自定义的 EditorTemplate .您的第二个错误(当确实使用 for 循环时)是因为绑定到的属性是 string ,所以绑定到 m =>m.ToRelationship [i] 绑定到 string 中的一个字符(typeof Char ).

First, to explain your errors. You cannot use a foreach loop to generate form controls for a collection. It generates duplicate name attributes which cannot bind back to a collection (and duplicate id attributes which is invalid html). You need to use a for loop, or better a custom EditorTemplate. You second error (when you do use a for loop) is because the property your binding to is string, so binding to m => m.ToRelationship[i] is binding to a character (typeof Char) in the string.

真正的问题是您的视图模型不正确,并且与视图中的显示/编辑内容没有关系,因此您需要将下拉列表绑定到集合中的属性.

The real issue is that your view model is incorrect and has no relationship to what your displaying/editing in the view, and you need to bind the dropdownlists to a property in a collection.

此外,对于 ToRelationship FromRelationship 使用2个组合框会导致很多潜在的错误,因此您应该重新定义关系表以包含(说)以下字段

In addition, your use of 2 comboboxes for the ToRelationship and FromRelationship is going to lead to a lot of potential errors, and you should be redefining your Relationship table to include (say) the following fields

ID, Relationship, MaleReverseRelationship, FemaleReverseRelationship

因此典型的行可能包含

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

那么您只需要一个下拉列表,并且如果Jack与Roger的关系是 Son ,则您知道相反的关系是父亲(假设您的 Member 模型具有用于性别的字段.如果用户选择了错误的反向关系,这将大大简化您的视图模型,简化UI并防止用户出错.

then you need a single dropdownlist and if the relationship of Jack to Roger is Son, you know that the reverse relationship is Father (assuming your Member model has a field for Gender). This will greatly simplify your view model, make the UI simpler and prevent user errors if the user selects the wrong reverse relationship.

您尚未提供有关其他表/模型的任何信息,但是您将需要一个带有字段的 MemberRelationShip

You have not provided any information on your other tables/models, but you would need a MemberRelationShip table with fields

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

然后,您需要2个视图模型,其中一个用于父级 Member ,另一种用于创建关系

You then needs 2 view models, one for the parent Member and one to create the relationships

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; }
}

然后您需要用于 MemberVM

/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" />
}

这篇关于在Foreach或For循环中使用EditorFor(ASP.NET MVC + RAZOR)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆