将淘汰赛翻译成剃刀以保持其验证工作

时间:2013-03-10 01:23:12

标签: asp.net-mvc razor knockout.js knockout-mvc

这是我面临的问题,不知道如何处理它:

我在ASP.NET MVC 4中创建了模型,控制器和视图。有一次我不得不创建动态列表,所以我选择了KnockoutJS,这很容易解决这个问题。到现在为止还挺好。 然后我意识到我使用Fluent Validation在我的MVC模型上定义的验证在淘汰视图中不再起作用。

我搜索了SO并找到了一些可行的解决方案:

我倾向于使用后者,原因有几个。主要是因为它给了我机会介绍(学习,测试,本地化,花时间)另一个图书馆。

我对MVC非常熟悉并喜欢它支持本地化的方式,可以完全控制消息,标签等。我也喜欢Fluent验证,不想用其他人替换它(更静态,更难以本地化,更多根据自己的喜好不太灵活)

当数据绑定必须成为data_bind等时,我找到了一些关于敲除razor转换的例子。

我无法找到用and in表达foreach循环的方法。

MVC视图模型

  public class ContactEmail
  {
    public string SelectedLabel { get; set; }
    public string Name { get; set; }
  }

 public class User
 {
   public IList<ContactEmail> Emails { get; set; }
 }


ViewBag.EmailLabels = new string[] { "label1", "label2", ... };

淘汰模型

  var viewModel = {
    EmailLabels: ko.observableArray(@Html.Json(ViewBag.EmailLabels as string[]) || []),
    Emails: ko.observableArray(@Html.Json(@Model.Emails) || []),
  } 

淘汰视图(我想改造)

    <table>
    <tbody data-bind="foreach: Emails">
      <tr>
        <td>
        @* How to make razor below work instead of knockout syntax below it? *@
        @*Html.DropDownListFor(m => ????, new { data_bind="options: $root.EmailLabels, value: SelectedLabel, optionsCaption: 'Choose...'" } )
          <select data-bind="options: $root.EmailLabels, value: SelectedLabel, optionsCaption: 'Choose...'"></select></td>
          <td>
            @* How to make razor below work as well instead of knockout syntax below ?!?!? *@
            @Html.TextBoxFor(m => ????, new { data_bind="value: Name, uniqueName: true" } )
              <input type="text" data-bind="value: Name, uniqueName: true" class="required email" />
          </td>
          <td>
              <a href="#" data-bind="click: function() { viewModel.removeEmail(this); }">Delete</a>
          </td>
      </tr>
    </tbody>
    </table>

我看了MVC Controls toolkit一个人无情地宣传的内容将解决我所有的验证和本地化以及一切。我发现它无法使用,非常专有并且非常难以理解。它就像购买核武杀死一只鸟。

所以,请那些有MVC与淘汰赛结合经验的人,请加强并分享您的经验。

任何帮助将不胜感激&amp;非常感谢你。

2 个答案:

答案 0 :(得分:0)

编辑:更新以包含确定的Knockout Bindings

<强> index.cshtml

@model Stackoverflow5.Models.User

<form>
    <table>
        <tbody>

            @{
                var tempdropdownlist = new List<SelectListItem>();
            }
            @for (var i = 0; i < @Model.Emails.Count; i++)
            {
                <tr>
                    <td>
                        @Html.DropDownListFor(m => m.Emails[i], tempdropdownlist,
                            new { data_bind = String.Format("options: $root.EmailLabels, value: Emails()[{0}].SelectedLabel, optionsCaption: 'Choose...'", i)})
                    </td>
                    <td>
                        @Html.TextBoxFor(m => m.Emails[i].Name, 
                            new { data_bind = String.Format("value: Emails()[{0}].Name(), uniqueName: true", i) })
                    </td>
                </tr>
            }

        </tbody>
    </table>

    <button type="submit">Test</button>
</form>

**模型(验证工作)**

public class ContactEmail
    {
        public string SelectedLabel { get; set; }

        [Required]
        [StringLength(20, MinimumLength = 2)]
        public string Name { get; set; }

    }

    public class User
    {
        public User()
        {
            Emails = new List<ContactEmail>();
            EmailLabels = new List<string> {"Important", "Spam", "Family"};
        }

        public List<ContactEmail> Emails { get; set; }
        public List<string> EmailLabels { get; set; }
    }

答案 1 :(得分:0)

我认为这有点像黑客,但它确实有效。

控制器将返回User.Emails属性内的电子邮件集合,其中包含需要呈现的列表。 Razor View产生的是一个只有一行的表格的HTML,并且基于电子邮件IEnumerable的第一个元素进行验证(必须检查此为null或者可能导致异常)。

当ko.applyBindings()出现在客户端时,tbody标签上的foreach将生成所有行,并且由于ko ViewModel与整个集合一起初始化为映射的JsonString,因此整个列表将呈现。 removeEmail和addEmail方法也可以正常工作(我刚刚测试了removeEmail选项,它工作= D)

@using Newtonsoft.Json
@model Stackoverflow5.Models.User

    <table>
        @{var tempdropdownlist = new List<SelectListItem>();}

        <tbody data-bind="foreach: Emails">
            <tr>
                <td>
                    @Html.DropDownListFor(m => m.Emails.First().SelectedLabel, tempdropdownlist,
                                      new { data_bind="options: $root.EmailLabels, value: SelectedLabel, optionsCaption: 'Choose...'" })

                <td>
                    @Html.TextBoxFor(m => m.Emails.First().Name, new { data_bind="value: Name, uniqueName: true" } )
                </td>
                <td>
                    <a href="#" data-bind="click: function () { viewModel.removeEmail(this); }">Delete</a>
                </td>
            </tr>
        </tbody>
    </table>


@section scripts
{
    <script src="~/Scripts/knockout-2.2.1.js"></script>
    <script src="~/Scripts/knockout.mapping-latest.js"></script>

    <script>
        //Model definition
        var viewModel,
            ModelDefinition = function (data) {
            //Object definition
            var self = this;

            //Mapping from ajax request
            ko.mapping.fromJS(data, {}, self);

            self.removeEmail = function(row) {
                self.Emails.remove(row);
            };

            self.addEmail = function() {
                //Method for adding new rows here
            };
        };

        $(function() {
            viewModel = new ModelDefinition(@Html.Raw(JsonConvert.SerializeObject(Model)));
            ko.applyBindings(viewModel);
        });
    </script>
}