3

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

对象的属性在表格行中显示为文本。我可以单独单击每个文本范围以显示一个文本框,以便我可以编辑文本。请务必注意,与对象相关的其他属性不会变为可编辑的。一次只能编辑一个属性。为了实现这一点,在 KO 映射期间,我实际上用一个具有两个属性的对象覆盖了每个属性:一个保存原始属性值的“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) };
    

例如,'name' 属性变为对象 Name: { value: 'Item1', hasFocus: false }。

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

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

  3. 当文本框模糊时,'hasFocus' 属性设置为 false,这会隐藏文本框并显示文本。

我想知道是否有更好的方法来完成此功能。我是不是偏离轨道了?谢谢!

更大的代码片段:

<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>
4

1 回答 1

1

我认为在根 VM 对象中设置字段来指定当前正在编辑的项目/字段并使用它更简单,而不是为每个项目和字段的编辑状态设置单独的 observables。你可以在你的 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>
于 2013-03-31T06:28:33.700 回答