C# ASP.NET MVC-与循环视图和局部视图相比,允许子对象编辑的UX方法更好

C# ASP.NET MVC-与循环视图和局部视图相比,允许子对象编辑的UX方法更好,c#,asp.net-mvc,user-interface,razor,asp.net-mvc-5,C#,Asp.net Mvc,User Interface,Razor,Asp.net Mvc 5,我有一个联系人对象,它有许多属性,包括一个地址列表的子对象 public class Contact { public int? Id { get; set; } public string Name { get; set; } public IReadOnlyList<IAddress> Addresses { [Lazy Load Code to populate and return list] } [...] } 我希望允许用户编辑地

我有一个联系人对象,它有许多属性,包括一个地址列表的子对象

public class Contact
{ 
  public int? Id { get; set; }
  public string Name { get; set; }
  public IReadOnlyList<IAddress> Addresses
  {
      [Lazy Load Code to populate and return list]
  } 
  [...]
}
我希望允许用户编辑地址,而不必编辑或发布整个联系人对象。当前在UI中,我列出了地址,每个地址旁边都有一个编辑按钮:

我使用的是Bootstrap3中的模态语法,并且有隐藏的div,其中包含一个表单和用于编辑地址的字段。当用户单击edit时,会出现一个模式窗口窗体,允许用户编辑地址。此表单中的模型绑定和验证工作

它工作得很好,但我在实现方面有一些潜在的问题。我想对Address对象使用内置验证和模型绑定,但我不想为了编辑一个地址而发回整个对象

因此,我不得不创建一个for循环,写出调用部分视图的隐藏div,并作为模型传递地址:

@for (int i = 0; i < Model.Addresses.Count; i++)
{
        address = (Address)Model.Addresses[i];
        @Html.Partial("_AddressModal", address);
}
不幸的副作用是,模型绑定无法唯一地标识将ModelState应用于哪个地址,因此模型绑定将其应用于隐藏div中的所有地址,而不是更新的地址。这是因为它们具有完全相同的属性名称

我有一个工作,这是一个计划的一部分。基本上,除非对象无效,否则它不会写入ModelState。对于无效场景,我不显示地址列表,这基本上隐藏了问题。否则,每个编辑按钮将显示相同的表单内容

我想知道的是,是否有更好的UX方法允许用户编辑列表中的一个子地址

我的目标是找到一种更好的使用ASP.NET MVC的方法:

遵循PRG Post重定向Get模式 不要重定向到单独的页面/视图进行编辑,这会迫使用户再次导航回联系人编辑表单 避免弹出窗口,因为有拦截器 该解决方案允许对Address对象使用模型绑定和验证 你想要的是:

1.联系人表格 每个地址的另一个表格

在控制器中,您将有一个操作,该操作需要一个联系人作为参数,而没有地址,另一个操作需要一个地址

样板代码:

    [HttpPost]
    public RedirectToRouteResult EditAddress(int id, ContactAddressBindingModel address) {
        // ...
    }

    [HttpPost]
    public RedirectToRouteResult EditContact(int id, ContactBindingModel contact)
    {
        // ...
    }

    public class ContactViewModel : ContactBindingModel
    {
        public IReadOnlyList<IAddress> Addresses { // ...}
    }
    public class ContactBindingModel
    {
        public int? Id { get; set; }
        public string Name { get; set; }
    }
    public class ContactAddressBindingModel : IAddress
    {
        public int? Id { get; set; }
        public string City { get; set; }
        public string Country { get; set; }

    }
并且认为:

<form action="EditContact">
<!-- contacts inputs etc -->
</form>

// in razor you can do EditorFor(m => m.Addresses) instead of foreach
// and have partialview, or whatever you like.

@foreach (Model.Addresses) { 
<form action="EditAddress">
<!-- address inputs etc -->
</form>
}
你想要的是:

1.联系人表格 每个地址的另一个表格

在控制器中,您将有一个操作,该操作需要一个联系人作为参数,而没有地址,另一个操作需要一个地址

样板代码:

    [HttpPost]
    public RedirectToRouteResult EditAddress(int id, ContactAddressBindingModel address) {
        // ...
    }

    [HttpPost]
    public RedirectToRouteResult EditContact(int id, ContactBindingModel contact)
    {
        // ...
    }

    public class ContactViewModel : ContactBindingModel
    {
        public IReadOnlyList<IAddress> Addresses { // ...}
    }
    public class ContactBindingModel
    {
        public int? Id { get; set; }
        public string Name { get; set; }
    }
    public class ContactAddressBindingModel : IAddress
    {
        public int? Id { get; set; }
        public string City { get; set; }
        public string Country { get; set; }

    }
并且认为:

<form action="EditContact">
<!-- contacts inputs etc -->
</form>

// in razor you can do EditorFor(m => m.Addresses) instead of foreach
// and have partialview, or whatever you like.

@foreach (Model.Addresses) { 
<form action="EditAddress">
<!-- address inputs etc -->
</form>
}

这个问题对SO来说太主观了——你应该这样做:1决定一种方法,尝试并实施它,如果遇到技术上的障碍,问一个问题;或者2形成一个纯粹的用户体验问题并继续提问。如果你真的想问你要问的问题,StackExchange不是问这个问题的合适地方-你是在问想法,而不是问一个真正有答案的问题。我已经更新了它,以要求更好的用户体验方法来解决我的问题,因为它有一些潜在的问题。您正在使用分部呈现的每个“隐藏div”是否都包含一个用于编辑地址的表单?如果是这样的话,我建议使用一种不同的方法,只使用一个表单,当您单击Edit时,通过调用返回包含地址属性的json的方法,使用1个ajax填充表单;或者2使用data-atributes将地址属性存储在按钮中。您只有一组控件没有验证问题,使用ajax发布表单,并停留在同一页面上继续编辑其他地址是的,提交后每个控件都有一个遵循PRG模式的表单。选项1听起来不错,使用Ajax的选项2是否仍然支持验证,例如ModelState包含服务器端错误,因为您不会进行页面刷新?@Josh,是的,它仍然支持验证,只要在视图中呈现基于地址的“空白”表单(例如使用分部),并且为每个属性包含关联的@Html.ValidationMessageFor。因为这是一篇ajax文章,所以您需要调用.Valid并防止文章返回false。如果有帮助的话,很乐意举个例子。这个问题对SO来说太主观了-你应该:1决定一种方法,尝试并实施它,如果遇到技术障碍,问一个问题;或者2形成一个纯粹的用户体验问题并继续提问。如果你真的想问你要问的问题,StackExchange不是问这个问题的合适地方-你是在问想法,而不是问一个真正有答案的问题。我已经更新了它,以要求更好的用户体验方法来解决我的问题,因为它有一些潜在的问题。您正在使用分部呈现的每个“隐藏div”是否都包含一个用于编辑地址的表单?如果是这样的话,我会建议一个不同的方法,你只有一个罪
当您单击Edit时,通过调用返回包含地址属性的json的方法,使用1个ajax填充表单;或者2使用data-atributes将地址属性存储在按钮中。您只有一组控件没有验证问题,使用ajax发布表单,并停留在同一页面上继续编辑其他地址是的,提交后每个控件都有一个遵循PRG模式的表单。选项1听起来不错,使用Ajax的选项2是否仍然支持验证,例如ModelState包含服务器端错误,因为您不会进行页面刷新?@Josh,是的,它仍然支持验证,只要在视图中呈现基于地址的“空白”表单(例如使用分部),并且为每个属性包含关联的@Html.ValidationMessageFor。因为这是一篇ajax文章,所以您需要调用.Valid并防止文章返回false。如果有帮助,很乐意举个例子。你对这个问题的误解。通过使用多个表单,每个表单都有具有重复名称和id属性的控件(这是无效的html),jquery validate unobtrusive将无法将任何验证错误消息分配给正确的控件,因为存在多个输入,例如name=Address。City@StephenMuecke你的陈述是错误的。首先,重复名称是有效的HTML,并且在单选按钮上广泛使用,但它们不会在同一表单上重复,而且您也不会有重复的ID,因为MVC在编辑集合时会处理这个问题。它会创建id=Address[0].City.com之类的内容。此外,如果您不选择Helpers路线,则任何输入都不需要id。我没有误解这个问题,事实上我很清楚这一点。最后,我不喜欢你的语气。哦,顺便说一句,因为你不会在同一个表单上有重复的名称,jquery validate unobtrusive将不会出现问题。通过无效的html,我指的是重复的id属性!重复的名称属性是可以的,当您使用循环和部分属性呈现集合时,您会生成重复的名称和id属性,除非使用诸如@Html之类的帮助器。BeginCollectionItem@StephenMuecke我的示例在没有助手的情况下显示代码,因此我看不到如何生成重复ID。如果使用partials,正如我在注释代码中指出的,您不会生成重复的ID,因为您使用的是EditorFor而不是foreach。您不需要BeginCollectionItem,也不需要foreach,我在回复中明确指出了这一点。我认为你误解了mvc的内部运作。你误解了这个问题。通过使用多个表单,每个表单都有具有重复名称和id属性的控件(这是无效的html),jquery validate unobtrusive将无法将任何验证错误消息分配给正确的控件,因为存在多个输入,例如name=Address。City@StephenMuecke你的陈述是错误的。首先,重复名称是有效的HTML,并且在单选按钮上广泛使用,但它们不会在同一表单上重复,而且您也不会有重复的ID,因为MVC在编辑集合时会处理这个问题。它会创建id=Address[0].City.com之类的内容。此外,如果您不选择Helpers路线,则任何输入都不需要id。我没有误解这个问题,事实上我很清楚这一点。最后,我不喜欢你的语气。哦,顺便说一句,因为你不会在同一个表单上有重复的名称,jquery validate unobtrusive将不会出现问题。通过无效的html,我指的是重复的id属性!重复的名称属性是可以的,当您使用循环和部分属性呈现集合时,您会生成重复的名称和id属性,除非使用诸如@Html之类的帮助器。BeginCollectionItem@StephenMuecke我的示例在没有助手的情况下显示代码,因此我看不到如何生成重复ID。如果使用partials,正如我在注释代码中指出的,您不会生成重复的ID,因为您使用的是EditorFor而不是foreach。您不需要BeginCollectionItem,也不需要foreach,我在回复中明确指出了这一点。我想你误解了mvc的内部工作原理。