【问题标题】:Make an object's properties individually editable using KnockoutJS使用 KnockoutJS 使对象的属性可单独编辑
【发布时间】:2013-03-21 12:38:57
【问题描述】:

我是 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,这会隐藏文本框并显示文本。

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

更大的sn-p代码:

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

【问题讨论】:

    标签: knockout.js knockout-mapping-plugin


    【解决方案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>
    

    【讨论】:

    • 您的解决方案效果很好!有没有办法迭代每个属性(例如名称、价格等),这样我就不必明确地将“名称”或“价格”硬编码为编辑函数的参数?如果是这样,我可以将 HTML 的 sn-p 放入模板中,并让该模板呈现每个属性的数据绑定代码。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-06-16
    • 2016-06-18
    • 1970-01-01
    • 2016-06-12
    • 1970-01-01
    • 1970-01-01
    • 2014-07-31
    相关资源
    最近更新 更多