使用KnockoutJS使对象的属性可单独编辑

时间:2013-03-31 04:57:50

标签: knockout.js knockout-mapping-plugin

我是KnockoutJS的新手。我想知道是否有更好的方法来完成下面的功能。

对象的属性在表格行中显示为文本。我可以单独单击每个文本范围以显示文本框,以便我可以编辑文本。请务必注意,与对象相关的其他属性不可编辑。一次只能编辑一个属性。为了实现这一点,在KO Mapping期间,我实际上用一个具有两个属性的对象覆盖每个属性:一个'value'属性,用于保存原始属性的值,以及一个'hasFocus'属性,用于跟踪文本框的可见性

这是一个JSFiddle来展示我目前如何编程。请务必点击项目名称和价格以查看文本框。

进一步说明

我有一个表,其中每一行代表一个TransactionItem。

在开始状态下,每个字段都是文本。单击时,文本消失并显示文本框。当文本框失去焦点时,文本框将消失,修改后的文本会重新出现。

以下步骤显示了我为实现此目的所做的工作:

  1. 使用KO Mapping插件(在此示例中为'myData')映射服务器中的数据,

    var  myData = [
        {
        TransactionItems: [
            {
            Name: "Item1",
            Price: "1.00"       
            },
            {
            Name: "Item2",
            Price: "2.00"       
            },
            {
            Name: "Item3",
            Price: "3.00"       
            },
        ]},
    ];
    
    var mappingOptions = {          
        'TransactionItems': {
            create: function (options) {
                return new TransactionItem(options.data);
            }
        }   
    }
    var viewModel = {};
    var self = viewModel;
    viewModel.transactions = ko.mapping.fromJS(myData, mappingOptions);
    
    ko.applyBindings(viewModel);
    
  2. 在TransactionItems构造函数中,获取TransactionItem属性的当前值('name','price'),并将它们存储在临时变量中。使用新对象覆盖属性。这些新对象包含两个值:原始属性的“值”和新的“hasFocus”属性

    //Grab the current values of the properties
    var itemValue = this.Name();
    var priceValue = this.Price();
    
    //Recreate properties as objects, give them a 'hasFocus' property
    this.Name = { value: itemValue, hasFocus: ko.observable(false)};
    this.Price = { value: priceValue, hasFocus: ko.observable(false) };
    
  3. 例如,'name'属性成为对象Name:{value:'Item1',hasFocus:false}。

    1. 在HTML中,数据绑定每个属性的'hasFocus'以控制何时显示/隐藏文本/文本框。

    2. 单击文本时,'hasFocus'属性设置为true,这将隐藏文本,并显示文本框。

    3. 当文本框模糊时,'hasFocus'属性设置为false,隐藏文本框,并显示文本。
    4. 我想知道是否有更好的方法来完成此功能。我离开了轨道吗?谢谢!

      更大的代码片段:

      <table>
          <thead>
              <tr>            
                  <th>Name</th>
                  <th>Price</th>
              </tr>
          </thead>
          <tbody>  
              <!-- ko foreach: transactions -->
                  <!-- ko template: { name: 'listItems', foreach: TransactionItems } --> 
                  <!-- /ko -->
              <!-- /ko -->
          </tbody>
      </table>
      
      <script type="text/html" id="listItems">
          <tr>
              <td>
                  <!-- Either show this -->
                  <span data-bind="visible: !Name.hasFocus(), 
                                   text: Name.value, 
                                   click: editItem.bind($data, Name)"></span>
      
                  <!-- Or show this -->
                  <input data-bind="visible: Name.hasFocus, 
                                    value: Name.value, 
                                    hasfocus: Name.hasFocus, 
                                    event: { 
                                              focus: editItem.bind($data, Name), 
                                              blur: hideItem.bind($data, Name) 
                                           }" 
                  /><!-- end input -->
              </td>
              <td>
                  <!-- Either show this -->
                  <span data-bind="visible: !Price.hasFocus(), 
                                   text: Price.value, click: editItem.bind($data, Price)"></span>
      
                  <!-- Or show this -->
                  <input data-bind="visible: Price.hasFocus, 
                                    value: Price.value, 
                                    hasfocus: Price.hasFocus, 
                                    event: { 
                                              focus: editItem.bind($data, Price), 
                                              blur: hideItem.bind($data, Price) 
                                           }" 
                  /><!--input end -->
              </td>
          </tr>
      </script>
      
      <!-- END OF HTML -->
      
      <script >
      
      function TransactionItem(data) {
      
          ko.mapping.fromJS(data, {}, this);
      
          this.editable = ko.observable(false);
          this.hasFocus = ko.observable(false);
      
          //Grab the current values of the properties
          var itemValue = this.Name();
          var priceValue = this.Price();
      
          //Recreate properties as objects, give them a 'hasFocus' property
          this.Name = { value: itemValue, hasFocus: ko.observable(false)};
          this.Price = { value: priceValue, hasFocus: ko.observable(false) };
      
          this.editItem = function (objProperty) {
              this.editable(true);
              objProperty.hasFocus(true);        
          }
      
          this.hideItem = function (objProperty) {
              this.editable(false);
              objProperty.hasFocus(false);
          }       
      
      }
      
      //MAPPING
      var mappingOptions = {
      
          'TransactionItems': {
              create: function (options) {
                  return new TransactionItem(options.data);
              }
          }   
      }
      
      //DATA
      var  myData = [
          {
          TransactionItems: [
              {
              Name: "Item1",
              Price: "1.00"       
              },
              {
              Name: "Item2",
              Price: "2.00"       
              },
              {
              Name: "Item3",
              Price: "3.00"       
              },
          ]},
              ];
      
      //VIEWMODEL        
      var viewModel = {};
      var self = viewModel;
      viewModel.transactions = ko.mapping.fromJS(myData, mappingOptions);
      
      ko.applyBindings(viewModel);
      
              </script>
      

1 个答案:

答案 0 :(得分:1)

我认为在根VM对象中使用字段来指定当前正在编辑哪个项目/字段并使用该字段更简单,而不是为每个项目和字段的编辑状态分别设置单独的可观察对象。您可以放置​​辅助函数在您的虚拟机中,以帮助您在绑定中处理它。

为了处理输入模糊,我处理了文档元素(或任何合适的元素)的点击,并确保点击事件不会从可编辑元素中冒出来。我使用clickBubble绑定做到了这一点,但使用jQuery(或替代方法)可能更容易。

JSFiddle:http://jsfiddle.net/antishok/a2EPT/7/

JS:

function TransactionItem(data) {
    ko.mapping.fromJS(data, {}, this);
}

function ViewModel(data) {
    var self = this;
    this.transactions = ko.mapping.fromJS(data, mappingOptions);
    this.editedItem = ko.observable();
    this.editedField = ko.observable();

    this.isEdited = function (item, field) {
        return self.editedItem() === item && self.editedField() === field;
    }

    this.editItem = function (field, item) {
        self.editedItem(item);
        self.editedField(field);
    }

    this.stopEditing = function() {
         self.editItem(undefined, undefined);
    }
}

var viewModel = new ViewModel(myData);
ko.applyBindings(viewModel);

ko.utils.registerEventHandler(document, 'click', function(event) {
    viewModel.stopEditing();
});

HTML:

<td>
        <!-- Either show this -->
        <span data-bind="visible: !$root.isEdited($data, 'Name'), 
                         text: Name, 
                         click: $root.editItem.bind($data, 'Name'), clickBubble: false"></span>

        <!-- Or show this -->
        <input data-bind="visible: $root.isEdited($data, 'Name'), 
                          value: Name, 
                          hasfocus: $root.isEdited($data, 'Name'), 
                          click: function(){}, clickBubble: false" 
        /><!-- end input -->
</td>