2012-11-07 97 views
2

我們在我們的Web應用程序中有一個表單,它在一個地方要求用戶輸入一個值列表。我們使用Razor和knockout.js編寫了這部分表單,以便爲列表中的每個值都有一個文本框,類似於this tutorial。我們如何將這些文本框與我們的MVC模型進行數據綁定?數據綁定MVC-4到Knockout.js foreach

這裏是我們的形式:

@model OurProject.Models.Input.InputModel 
@{ 
    ViewBag.Title = "Input"; 
} 
<h2> 
    Inputs</h2> 
<div id="inputKOApp"> 
    @using (Html.BeginForm()) 
    { 
     <!-- snip - lots of part of our form that work correctly --> 
     <div class="row-fluid"> 
      <div class="control-group"> 
       <div class="span8 control-label"> 
        @Html.LabelFor(model => model.POSTransactionCodes) 
       </div> 
       <div class="controls"> 
        <!-- These are the textboxes we would like to bind to MVC --> 
        <ul class="pull-right" data-bind="foreach: POSTransactionCodes"> 
         <li> 
          <input data-bind="value: code" /> 
          <a href="#" data-bind="click: $root.removePOSTransactionCode">Delete</a></li> 
        </ul> 
        <button class="pull-right" data-bind="click: addPOSTransactionCode"> 
         Add another POS Transaction Code</button> 
        @Html.ValidationMessageFor(model => model.POSTransactionCodes, null, new { @class = "help-inline" }) 
       </div> 
      </div> 
     </div> 
     <!-- snip - more cshtml that is irrelevant to the problem --> 
</div> 
     <input type="submit" value="Submit" /> 
    } 
</div> 
<script type="text/javascript" src='~/Scripts/jquery-1.8.2.min.js'></script> 
<script type="text/javascript" src='~/Scripts/knockout-2.1.0.js'></script> 
<script type="text/javascript" src='~/Scripts/OP/OP.js'></script> 
<script type="text/javascript" src='~/Scripts/OP/Input/OP.Input.Input.Form.js'></script> 
<script type="text/javascript" src='~/Scripts/OP/Input/OP.Input.Input.Data.js'></script> 
<script type="text/javascript"> 
    var inputApp = $('#inputKOApp')[0]; 
    OP.Input.Form.init(inputApp); 
</script> 

這裏是我們的knockout.js腳本,OP.Input.Input.Form.js:

extend(OP, 'OP.Input.Form'); 
OP.Input.Form = function (jQuery) { 
    var TransactionCodeView = function() { 
     var self = this; 
     self.code = ""; 
    }; 

    //The ViewModel for the page 
    var ViewModel = function() { 
     var self = this;   

     //Fields 
     /* snip - lots of fields that work as expected */ 
     self.POSTransactionCodes = ko.observableArray([]); //is a list of transaction codes 

     //Set up with initial data 
     /* I'm guessing that we won't need this function anymore since MVC will populate 
     * everything for us, but I'll leave it in until I'm far enough along to know 
     * I won't need to gut lots of stuff */ 
     self.initialize = function() { 
      var c = function (data, status, response) { 
      /* snip - lots of fields that work as expected */ 
       if (status === "success") { 
        if(data.POSTransactionCodes != null) ko.utils.arrayPushAll(self.POSTransactionCodes, data.POSTransactionCodes); 
        self.POSTransactionCodes.valueHasMutated(); 
       } else { 

       } 
      }; 
      OP.Input.Data.GetInput(c); 
     } 

     //When saving, submit data to server 
     self.save = function (model) { 
      var c = function (data, status, response) { 
       if (status === "success") { 

       } else { 
       } 
      }; 
      OP.Input.Data.SaveInput(model, c); 
     } 

     //Modifying POSTransactionCodes array 
     self.removePOSTransactionCode = function (POScode) { 
      self.POSTransactionCodes.remove(POScode); 
     } 

     self.addPOSTransactionCode = function() { 
      self.POSTransactionCodes.push(new TransactionCodeView()); 
     } 
    }; 

    //Connect KO form to HTML 
    return { 
     init: function (elToBind) { 
      var model = new ViewModel(); 
      ko.applyBindings(model, elToBind); 
      model.initialize(); 
     } 
    }; 
} ($); 

這裏是我們的MVC模式:

namespace OurProject.Models.Input 
{ 
    public class InputModel : IModel 
    { 
     //Snip - lots of properties that aren't interesting for this problem 
     [Required] 
     [DisplayName("POS Transaction Codes")] 
     public List<double> POSTransactionCodes { get; set; } 

     public InputModel() 
     { } 

     /* I ommitted a few other methods that 
     * aren't relevant to this problem. */ 
    } 
} 

回答

2

我不明白你如何將數據發回服務器,但你需要命名你的輸入的方式允許模型綁定:

如果你綁定到一個列表/收集你的投入應名稱,如:

<input type="text" name="CollectionPropertyName[index]" /> 

你可以閱讀有關Model Binding To A List in this article

所以你只需要爲你的投入產生的專有名詞:

<input data-bind="value: code, attr: { name: 'POSTransactionCodes[' + $index() + ']' }" /> 

您應該注意,上述解決方案可能只適用於使用提交按鈕併發送數據的情況下form-urlencoded如果您要發送數據爲j兒子,你可能需要調整你的序列化邏輯,使模型綁定開心:

在這種情況下,你應該JSON是這樣的:

{ 
    //... 
    POSTransactionCodes: [ 1 , 3 ] 
    //.. 
} 
1

由於nemesv的回答讓我在這一個光。

但我需要有用於jquery驗證的「正常」名稱和id屬性。所以我想出了寫我自己的數據綁定的想法。

ko.bindingHandlers.nameId = { 
    update: function(element, valueAccessor, allBindingsAccessor, viewModel) { 
     var value = valueAccessor(), 
      allBindings = allBindingsAccessor(); 
     var valueUnwrapped = ko.utils.unwrapObservable(value); 
     var bindName = $(element).data('bind-name'); 
     bindName = bindName.replace('{0}', valueUnwrapped); 
     $(element).attr({name : bindName, id : bindName}); 
    } 
}; 

,而在HTML

<input 
     data-bind="value: qty, nameId: $index" 
     data-bind-name="inventory[{0}].qty" /> 

jsfiddle

使用