2013-03-31 26 views
3

我是KnockoutJS的新手。我想知道是否有更好的方法来完成下面的功能。使用KnockoutJS使对象的属性可单独编辑

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

这里是一个JSFiddle来显示我目前如何编程。一定要点击商品名称和价格才能看到文本框出现。

进一步说明

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

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

下列步骤显示我做了什么做到这一点:

  1. 使用KO映射插件(在这个例子中 '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) }; 
    

例如,“姓名”属性成为对象名称:{值:' Item1',hasFocus:false}。

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

  2. 当单击文本时,“hasFocus”属性设置为true,这将隐藏的文本,显示文本框。

  3. 当文本框模糊时,“hasFocus”属性被设置为假,隐藏文本框,并显示文本。

我想知道是否有更好的方法来完成这个功能。我走偏了轨道吗?谢谢!代码

较大的片段:

<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

我认为这是简单的在你的根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> 
+1

您的解决方案效果很好!是否有方法遍历每个属性(例如名称,价格等),以便我不必将“Name”或“Price”明确硬编码为编辑函数的参数?如果是这样,我可以在模板中放入一段HTML代码,并让该模板为每个属性呈现数据绑定代码。 – JerryM

+0

下面是如何做到这一点的示例:http://jsfiddle.net/antishok/a2EPT/8/ – antishok

+0

非常好!谢谢! – JerryM