阵列中的Knockout observable字段表示项目的顺序

时间:2017-05-15 10:09:57

标签: html knockout.js

我有一个html视图,它连接到Knockout视图模型,并显示一个项目列表。 列表中的每个项目都包含文本名称字段和数字顺序字段。 用户可以执行"拖放"对UL列表中的项目的操作。 "拖放" event更改项目的顺序如下:

 <div id="wrapper">
<ul data-bind="foreach:Items">
    <li draggable="true"
        ondragover="event.preventDefault();"
        data-bind="event:{dragstart:$root.dragItem,drop:$root.dropItem}">
        <label data-bind="text:name"></label>
        <label data-bind="text:orderNo"></label>
        <input type="text" data-bind="value:name" />
    </li>
</ul>

<script type="text/javascript">
var list = [{ name: 'Red', orderNo: 0 }
    , { name: 'Green', orderNo: 1 }
    , { name: 'Blue', orderNo: 2 }];
function viewmodel() {
    var self = this;
    self.Items = ko.mapping.fromJS(list);

    self.ItemToDrag = ko.observable();
    self.dragItem = function (item, event) {
        self.ItemToDrag(item);
        return true;
    }
    self.dropItem = function (item, event) {
        event.preventDefault();
        var up = self.ItemToDrag().orderNo() > item.orderNo();
        self.ItemToDrag().orderNo(up ? item.orderNo() - 0.5 : item.orderNo() + 0.5);
        //order this list
        self.Items.sort(function (left, right) {
            return left.orderNo() == right.orderNo() ? 0 : (left.orderNo() < right.orderNo() ? -1 : 1);
        });
        //set integer number
        for (var i = 0; i < self.Items().length; i++) {
            self.Items()[i].orderNo(i);
        }
    }
}


var vm;
$(document).ready(function () {
    vm = new viewmodel();
    ko.applyBindings(vm, $("#wrapper")[0]);
});

我的问题是,如果列表中的项目通过UI更改订单,Knockout可以自动更改订单字段的内容。 像

这样的东西
<ul data-bind="foreach:Items,orderKey:orderNo"></ul>

其中orderKey表示商品的订单,以及订单更改时要更新的字段。

2 个答案:

答案 0 :(得分:0)

我不确定这正是你所需要的。这是自定义绑定,它在foreach绑定之前对数组进行排序:

  ko.bindingHandlers.foreach["after"] = ["orderKey"];
  ko.bindingHandlers.orderKey = {
	    update: function (el, valueAccessor, allBindingsAccessor, viewModel) {
	        var key = ko.unwrap(valueAccessor());
	        var allBindings = allBindingsAccessor();
	        if("foreach" in allBindings) {
	            var array = ko.unwrap(allBindings.foreach);
	            array.sort(function(a, b) { return a[key] > b[key]; });
	            allBindings.foreach = array;
	        }                    
	    }
	};
  
  // The model
  var model = { Items: ko.observableArray([{text: 3}, {text: 1}, {text: 2}]) };
  // Apply
  ko.applyBindings(model);

  // This simulate changes in observableArray
  setTimeout(function() { model.Items.push({text: 0}) }, 1000);
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.0/knockout-min.js"></script>

<ul data-bind="foreach: Items, orderKey: 'text'">
  <li data-bind="text: text"></li>
</ul>

答案 1 :(得分:0)

不,该用例没有特定的绑定。然而,在淘汰赛中,编写自定义绑定很简单。见the documentation。在我工作的公司,我们使用基于淘汰赛的框架(由我们开发)和大量的自定义绑定,其中一些非常复杂。

我刚开始为您的用例创建这样的绑定。但我意识到,除非你有几十个这样的清单,否则它不符合目的。

然而,您可以做的是将实际排序排序为knockout computed,并在drop函数中更新排序索引。请参阅下面的示例,并且不要犹豫,询问是否有不清楚的事情。

&#13;
&#13;
var list = [{ name: 'Red', orderNo: 0 }
    , { name: 'Green', orderNo: 1 }
    , { name: 'Blue', orderNo: 2 }];
    
function viewmodel() {
    var self = this;
    self._items = ko.mapping.fromJS(list);
    self.Items = ko.pureComputed(function () {
    	return self._items().sort(function (a, b) {
      	return a.orderNo() < b.orderNo() ? -1 : 1;
      });
    });

    self.ItemToDrag = ko.observable();
    self.dragItem = function (item, event) {
        self.ItemToDrag(item);
        return true;
    }
    self.dropItem = function (item, event) {
        event.preventDefault();
        var up = self.ItemToDrag().orderNo() > item.orderNo();
        self.ItemToDrag().orderNo(up ? item.orderNo() - 0.5 : item.orderNo() + 0.5);
    }
}


var vm;
$(document).ready(function () {
    vm = new viewmodel();
    ko.applyBindings(vm, $("#wrapper")[0]);
});
&#13;
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout.mapping/2.4.1/knockout.mapping.min.js"></script>

<div id="wrapper">
<ul data-bind="foreach:Items">
    <li draggable="true"
        ondragover="event.preventDefault();"
        data-bind="event:{dragstart:$root.dragItem,drop:$root.dropItem}">
        <label data-bind="text:name"></label>
        <label data-bind="text:orderNo"></label>
        <input type="text" data-bind="value:name" />
    </li>
</ul>
&#13;
&#13;
&#13;