2013-03-10 46 views
0

這裏是我所面臨的問題,不知道如何甚至接近它:我創建了模型,控制器和視圖中ASP.NET MVC 4翻譯成淘汰賽剃刀,以保持它的驗證工作

。有一次我不得不創建動態列表,所以我選擇了KnockoutJS,這個問題很容易解決。到現在爲止還挺好。 然後我意識到我使用我使用Fluent Validation在我的MVC模型上定義的驗證在淘汰賽視圖中不起作用。

我搜索過SO,發現很少有可行的解決方案:

我傾向於使用後者而言,有幾個原因。主要是因爲它給我機會不是爲了介紹(學習,測試,本地化,花時間)另一個庫。

我對MVC非常熟悉,並且非常喜歡它支持本地化的方式,從而完全控制消息,標籤等。我也喜歡Fluent Validation,並且不想將其替換爲其他(更靜態,更難以本地化,靈活更符合我的心意)

我發現敲除一些例子來剃鬚刀轉換,當數據綁定有可能成爲data_bind等

我不能找到一種方式來表達與在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經驗的人加入淘汰賽,請加強並分享您的經驗。

任何幫助將不勝感激&非常感謝你提前。

+0

而不是使用敲除來做foreach,使用剃鬚刀。您必須首先爲ContactEmail類定義EditorTemplate。我會在幾分鐘內上傳一個例子。 – amhed 2013-03-10 13:35:36

+0

@ amhed謝謝,它是一個好主意,但是我必須使用淘汰賽來讓變長列表變得容易。希望你明白我的意思。除非你說我可以輕鬆地用剃刀做同樣的功能,敲除(可變長度列表)。感謝評論 – 2013-03-10 13:39:02

+0

我在考慮使用foreach或EditorFor使用Emails ViewModel和定義kcnokout綁定的內部來渲染控件。 – amhed 2013-03-10 13:43:46

回答

0

我覺得這是一個黑客位,但它的工作原理。

控制器將返回User.Emails屬性內的郵件集合與需要呈現的列表。 Razor View產生的只是一個表格的HTML,只有一行,並且基於Emails 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> 
} 
+0

這呈現良好,相應地添加了驗證屬性,但ID仍與所有行相同,並且我認爲驗證不起作用。 :S – amhed 2013-03-10 21:43:48

+0

你介意看看我的ko viewModel並說我可以做些什麼改變嗎?我只是不明白你的語法,試圖調整你的建立? http://pastebin.com/f7xAMjT9謝謝! – 2013-03-10 22:39:58

+0

你的viewmodel看起來很好,唯一的區別是我直接使用映射函數渲染我的整個模型(你需要引用knockout.mapping.js)。確保您的document.ready()函數位於模型聲明後的腳本末尾。 – amhed 2013-03-10 23:23:04

0

編輯:更新,包括明確的淘汰賽綁定

指數。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; } 
    } 
+0

不起作用 - 你傳遞一個列表('d.Emails') 'IList ',但是您在EditorTemplate中只接受一個'ContactEmail'作爲模型。如果是這樣的話,我不會有任何問題。我在你的解決方案中遺漏了什麼?請告訴我。謝謝。 – 2013-03-10 14:07:34

+0

因爲我們使用的是EditorFor(),所以框架檢測到它是一個IEnumerable,並且將呈現列表中每個項目的EditorTemplate。試一試,只要確保它位於〜/ Views/Shared/EditorTemplates – amhed 2013-03-10 14:14:03

+0

「不引人注意的客戶端驗證規則中的驗證類型名稱必須是唯一的。以下驗證類型不止一次出現:需要」在線@Html「。 TextBoxFor(m => m.Name,new {data_bind =「value:Name,uniqueName:true」})'不知道,我在MVC方面進行了一次流暢的驗證......任何想法? – 2013-03-10 14:26:50