简体   繁体   English

使用ViewModel外部的计算数组进行JS淘汰

[英]Knockout JS using computed arrays outside of ViewModel

I would like to display a list of items on a page, and be able to dynamically reposition items by using a dropdown list of all positions. 我想在页面上显示项目列表,并能够通过使用所有位置的下拉列表来动态地重新定位项目。 Selecting a position from the dropdown will change the current position of the item and re-shift the position any affected elements in the list. 从下拉列表中选择一个位置将更改该项目的当前位置,并重新移动列表中所有受影响元素的位置。

I do have a working version of this concept, but it is not ideal. 我确实有这个概念的可行版本,但这并不理想。 For some reason, when I reference my selectedItems computed array (I filter my items by setting the selectedItem observable), the position that is contained in the returned item is the original position value of the item, and not the one that has been set via the dropdowns/my reposition function. 由于某种原因,当我引用我的selectedItems计算数组时(我通过将selectedItem设置为observable来过滤我的项目),返回的项目中包含的位置是该项目的原始位置值,而不是通过设置的位置值下拉菜单/我的重新定位功能。 This is somewhat odd because the 'items' observableArray does contain the updated value, and the computedArray does return the right item, just not with the most up to date value. 这有点奇怪,因为'items'observableArray确实包含了更新后的值,而calculatedArray确实返回了正确的项目,只是没有最新的值。

A working JSfiddle is below. 正常工作的JSfiddle在下面。 However, it does a lot of manual calculation and does not take advantage of the computed array as described above. 但是,它进行了大量的手动计算,并且没有利用如上所述的计算数组。 The issue might be somehow related to setting Knockout observables from outside the ViewModel. 问题可能与从ViewModel外部设置Knockout可观察对象有关。 To see the issue, uncomment the 2 lines in the 'document ready' block', where I attempt to find the current position of an item, and comment out the for loop where I look for the current item manually. 要查看该问题,请取消注释“文档准备好”块中的两行,在该行中,我尝试查找项目的当前位置,并注释掉for循环,在该循环中,我手动查找当前项目。

https://jsfiddle.net/tq1m873m/5/ https://jsfiddle.net/tq1m873m/5/

I'm new to KnockoutJS & JS in general, be gentle :) 我是KnockoutJS和JS的新手,请保持柔和:)

$(document).ready(function () {

    $("select[id^='selectName_']").change(function () {

        //Extract the item ID from the select html id attribute
        var curItemIDNum = $(this).attr('id').substring(15);

        var currentPos = 0;

        // myViewModel.selectedItem("item" + curItemIDNum);
        // currentPos = myViewModel.selectedItems()[0].position();  

        // START - really bad code, shield your eyes
        // I can't seem to get the current position via the 2 commented lines above and have to resort to converting the observable array to a regular array and pulling the value that way. Not pretty!
        var itemsJS = ko.toJS(self.items());
        for (var x = 0; x < itemsJS.length; x++) {
            if (("item" + curItemIDNum) == itemsJS[x].name) {
                currentPos = itemsJS[x].position;
                break;
            }
        }
        // END - really bad code

        reposition("item" + curItemIDNum, currentPos, $(this).val());
        refreshDropDowns();
    });

    refreshDropDowns();

});

You were working on this before, and I didn't have a working solution for you. 您之前就已经在为此工作,而我没有适合您的解决方案。 Today, I do. 今天,我愿意。 Your use of a jQuery trigger is not going to work out well. 您对jQuery触发器的使用效果不佳。 Let's do it all with Knockout. 让我们用淘汰赛来完成所有工作。

I made items to be just an array of objects that do not have assigned positions. 我将items设置为只是一组没有分配位置的对象。 orderedItems is a computed that goes through items in order and creates an observable for position . orderedItems是一种计算方法, orderedItems顺序遍历items并创建一个position的可观察值。

A subscription on that position observable calls moveItemTo , which rearranges items, and all the dependencies are updated by Knockout. 可观察到的该position上的预订调用moveItemTo ,该预订重新排列项目,并且所有依赖项都由Knockout更新。

 $(function() { ko.applyBindings(viewModel()); }); function item(name) { return { name: name }; } var viewModel = function() { var self = {}; self.items = ko.observableArray([ item('item1'), item('item2'), item('item4'), item('item5'), item('item3') ]); function moveItemTo(item, pos) { var oldPos = self.items.indexOf(item), newPos = pos - 1, items; if (oldPos < newPos) { items = self.items.slice(oldPos, newPos + 1); items.push(items.shift()); self.items.splice.bind(self.items, oldPos, items.length).apply(self.items, items); } else { items = self.items.slice(newPos, oldPos + 1); items.unshift(items.pop()); self.items.splice.bind(self.items, newPos, items.length).apply(self.items, items); } } self.orderedItems = ko.computed(function() { return ko.utils.arrayMap(self.items(), function(item, index) { var pos = ko.observable(index + 1); pos.subscribe(moveItemTo.bind(null, item)); return { name: item.name, position: pos }; }); }); return self; }; //end of viewmodel 
 <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script> <div>Set the order of the item by selecting a new position from the dropdown: <ul data-bind="foreach: orderedItems"> <li> <div> <span data-bind="text: name"></span> <select data-bind="options: $root.orderedItems, optionsValue:'position', value: position"></select> </div> </li> </ul> </div>ITEMS CONTENTS: <BR> <span data-bind="text: JSON.stringify(ko.toJS(items), null, 4)"></span> 

One way I can think of doing this would be to use a computed value to hold the current state of the position. 我可以想到的一种方法是使用计算出的值来保存头寸的当前状态。 This would allow you to do your reshuffling when a new position is set on an item like below : 当在如下所示的项目上设置新职位时,这将允许您进行改组:

ko.utils.arrayForEach(self.items(), function (x) {
        x.newPosition = ko.computed({
            read: function () {
                return x.position();
            },
            write: function (val) {
                //get the item in the prev position
                var temp = ko.utils.arrayFirst(self.items(), function (y) {
                    return y.position() == val;
                });
                //swap positons here
                if (temp) {
                    temp.position(x.position());
                    x.position(val);
                }
            }
        });
    });

in the Mark up it would be 在标记中

<select data-bind="options:positions,value:newPosition"></select>

so on computed "write" ... the script swaps the position values. 如此计算得出的“写入”值...脚本会交换位置值。 I left your original binding to the orderedItems. 我将您的原始绑定留给了orderedItems。 you can find a working sample here https://jsfiddle.net/p1yhmvcr/2/ ... the one thing worth noting here would be the sorting is not really physical. 您可以在这里https://jsfiddle.net/p1yhmvcr/2/找到一个工作示例。这里值得注意的一件事是排序并不是真正的物理。 the observable array items are still on their original index in the array and the code is only changing the position values. 可观察的数组项仍在数组的原始索引上,并且代码仅更改位置值。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM