7

我有一个包含迭代项目的视图模型。我通过 EditorFor() 方法将它们放在我的视图中。

看法:

@model Models.MyModel 

@using (Html.BeginForm(@Model.Action, @Model.Controller))
{
    <div class="section" id="Terms">
        @Html.EditorFor(m => m.Terms)
    </div>

    <input type="submit" value="Save" />
}

模型:

public class MyModel 
{
    public IEnumerable<Term> Terms  { get; set; }
}

EditorTemplates\Term.cshtml:

@model Models.Term

@if (Model != null) 
{
    <fieldset>
        <legend>Term</legend>

        @Html.HiddenFor(model => model.TermID)

        <div class="editor-label">
            @Html.LabelFor(model => model.Identifier)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.Identifier)
            @Html.ValidationMessageFor(model => model.Identifier)
        </div>

        <div class="editor-label">
            @Html.LabelFor(model => model.Description)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.Description)
            @Html.ValidationMessageFor(model => model.Description)
        </div>

    </fieldset> 
}

我希望能够从视图中的列表中动态添加/删除项目,就像 knockout.js 上的这个示例一样,但是如何保留 auto-id 的 MVC 创建的??:

http://knockoutjs.com/examples/cartEditor.html

以下是我对此的要求:

  • 添加新条款
  • 删除条款
  • 验证添加的新术语视图

我已经阅读了关于 SO 的其他问题,但我还没有找到一个真正明确的答案。淘汰赛.js 是公认的方式吗?有没有使用 Knockout 和 MVC 执行此操作的示例?

谢谢!

4

3 回答 3

1

我找到了 Jarrett Meyer 的这篇文章Nested Collection Models in MVC3,他有一个不使用敲除和最大化代码重用的解决方案。

这涵盖了添加和删除方法。我将在这里概述添加方法。

该模型

public class Person {
  public string FirstName { get; set; }
  public string LastName { get; set; }
  public IList<PhoneNumber> PhoneNumbers { get; set; }
  public IList<EmailAddress> EmailAddresses { get; set; }
  public IList<Address> Addresses { get; set; }
}

意见

//New.cshtml:    
@using (Html.BeginForm("New", "Person", FormMethod.Post))
{
  @Html.EditorForModel()
  <p>
    <button type="submit">
      Create Person
    </button>
  </p>
}

//Person.cshtml:
@Html.AntiForgeryToken()
@Html.HiddenFor(x => x.Id)

<p>
  <label>First Name</label>
  @Html.TextBoxFor(x => x.FirstName)
</p>
<p>
  <label>Last Name</label>
  @Html.TextBoxFor(x => x.LastName)
</p>
<div id="phoneNumbers">
  @Html.EditorFor(x => x.PhoneNumbers)
</div>
<p>
  @Html.LinkToAddNestedForm("Add Phone Number", "#phoneNumbers", ".phoneNumber", "PhoneNumbers", typeof(PhoneNumber))
</p>

//PhoneNumber.cshtml:
<div class="phoneNumber">
  <p>
    <label>Telephone Number</label>
    @Html.TextBoxFor(x => x.Number)
  </p>
  <br/>
</div>

帮手

/// <param name="linkText">Text for Link</param>
/// <param name="containerElement">where this block will be inserted in the HTML using a jQuery append method</param>
/// <param name="counterElement">name of the class inserting, used for counting the number of items on the form</param>
/// <param name="collectionProperty">the prefix that needs to be added to the generated HTML elements</param>
/// <param name="nestedType">The type of the class you're inserting</param>
public static IHtmlString LinkToAddNestedForm<TModel>(this HtmlHelper<TModel> htmlHelper, string linkText,
    string containerElement, string counterElement, string collectionProperty, Type nestedType)
{
    var ticks = DateTime.UtcNow.Ticks;
    var nestedObject = Activator.CreateInstance(nestedType);
    var partial = htmlHelper.EditorFor(x => nestedObject).ToHtmlString().JsEncode();

    partial = partial.Replace("id=\\\"nestedObject", "id=\\\"" + collectionProperty + "_" + ticks + "_");
    partial = partial.Replace("name=\\\"nestedObject", "name=\\\"" + collectionProperty + "[" + ticks + "]");

    var js = string.Format("javascript:addNestedForm('{0}','{1}','{2}','{3}');return false;", containerElement,
        counterElement, ticks, partial);

    TagBuilder tb = new TagBuilder("a");
    tb.Attributes.Add("href", "#");
    tb.Attributes.Add("onclick", js);
    tb.InnerHtml = linkText;

    var tag = tb.ToString(TagRenderMode.Normal);
    return MvcHtmlString.Create(tag);
}

private static string JsEncode(this string s)
{
    if (string.IsNullOrEmpty(s)) return "";
    int i;
    int len = s.Length;

    StringBuilder sb = new StringBuilder(len + 4);
    string t;

    for (i = 0; i < len; i += 1)
    {
        char c = s[i];
        switch (c)
        {
            case '>':
            case '"':
            case '\\':
                sb.Append('\\');
                sb.Append(c);
                break;
            case '\b':
                sb.Append("\\b");
                break;
            case '\t':
                sb.Append("\\t");
                break;
            case '\n':
                //sb.Append("\\n");
                break;
            case '\f':
                sb.Append("\\f");
                break;
            case '\r':
                //sb.Append("\\r");
                break;
            default:
                if (c < ' ')
                {
                    //t = "000" + Integer.toHexString(c); 
                    string tmp = new string(c, 1);
                    t = "000" + int.Parse(tmp, System.Globalization.NumberStyles.HexNumber);
                    sb.Append("\\u" + t.Substring(t.Length - 4));
                }
                else
                {
                    sb.Append(c);
                }
                break;
        }
    }
    return sb.ToString();
}

Javascript

//since the html helper can change the text of the item inserted but not the index,
//this replaces the 'ticks' with the correct naming for the collection of properties
function addNestedForm(container, counter, ticks, content) {
  var nextIndex = $(counter).length;
  var pattern = new RegExp(ticks, "gi");

  content = content.replace(pattern, nextIndex);
  $(container).append(content);
} 
于 2014-05-06T13:17:12.797 回答
0

你想要淘汰 MVC http://knockoutmvc.com/CartEditor

您不必为此使用淘汰赛,您真正需要的是带有验证和创建/删除操作的 javascript,这些操作映射到 MVC 方面的静止控制器操作。你如何去实现它取决于你。淘汰赛使它变得容易。

于 2013-02-05T04:51:39.910 回答
0

您需要执行以下操作:

  1. 将模型发送到您的控制器并为术语呈现一个可编辑字段。
  2. 您要求用户提交此内容或单击添加以添加更多条款。
  3. 如果用户单击添加,您将创建现有字段的副本并将其清空或其他任何内容,以便他们可以创建新字段。
  4. 当用户提交时,您将其发送回接受一系列术语的操作并将其添加到数据库中或不添加到数据库中。或者您可以使用上面示例中的 ajax,或者从这个示例中您可以看到它发送到服务器的是 json 数组对象,而不是使用命名元素形成的。
  5. 您可以在创建它们时对其进行处理,也可以在提交时对其进行处理。

也取决于您的应用程序,因此淘汰赛只是帮助您在客户端进行第 3 步,您从旧副本创建新副本。您也可以为此起诉 JQuery 模板和 json2 以使用旧浏览器支持进行序列化。

您需要了解的是,一旦您在客户端上,您已经将您的模型发送到这里一次,所以不要担心服务器端。无论您在客户端构建什么,都可以一次将一个模型发送到 saveTerm 操作,该操作使用术语 id 和其他信息重新运行 json,或者您可以将 saveTerm 的集合作为 json 数组返回,它可以正常工作。

如果您正在考虑在 postBack 而不是 ajax 上发送一个数组,只需保持表单元素名称相同并复制术语输入字段并发送过来。MVC 会将它们映射到一个术语数组,就像它对 json 所做的那样。

于 2013-02-05T16:31:16.373 回答