Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/javascript/452.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript 在计算的观察者中聚合数组数据,但在KnockoutJS中保持状态_Javascript_Knockout.js_Computed Observable - Fatal编程技术网

Javascript 在计算的观察者中聚合数组数据,但在KnockoutJS中保持状态

Javascript 在计算的观察者中聚合数组数据,但在KnockoutJS中保持状态,javascript,knockout.js,computed-observable,Javascript,Knockout.js,Computed Observable,这是一个相当复杂的问题。我试图尽可能地细化代码,使其仅限于演示问题所需的代码。这是一个很长的帖子-考虑自己警告! 在这里可以看到这个问题的工作演示 我试图做的是根据数组中项目的公共属性将数组聚合为组。实际上,将阵列聚合到组中相当简单: function ViewModel() { var self = this; this.groupProperty = ko.observable('groupId'); this.data = ko.observableArray

这是一个相当复杂的问题。我试图尽可能地细化代码,使其仅限于演示问题所需的代码。这是一个很长的帖子-考虑自己警告! 在这里可以看到这个问题的工作演示


我试图做的是根据数组中项目的公共属性将数组聚合为组。实际上,将阵列聚合到组中相当简单:

function ViewModel() {
    var self = this;

    this.groupProperty = ko.observable('groupId');

    this.data = ko.observableArray();
    this.view = ko.computed(function () {
        var i, element, value, grouped = {};

        for (i = 0; i < self.data().length; i++) {
            element = self.data()[i];
        
            if (!element.hasOwnProperty(self.groupProperty()))
                continue; //Exclude objects that don't have the grouping property...
        
            value = ko.isObservable(element[self.groupProperty()]) ? element[self.groupProperty()]() : element[self.groupProperty];
        
            if (!grouped.hasOwnProperty(value))
                grouped[value] = new Group(value);
        
            grouped[value].items().push(element);
        }
    
        return transformObjectIntoArray(grouped);
    });
}
将其转换为以下对象:

{
    'A': [
        Item { label: 'Item 1', groupId: 'A' },
        Item { label: 'Item 2', groupId: 'A' }
    ],
    'B': [
        Item { label: 'Item 1', groupId: 'B' },
        Item { label: 'Item 2', groupId: 'B' }
    ]
}
然后将其转换为以下数组:

[
    [
        Item { label: 'Item 1', groupId: 'A' },
        Item { label: 'Item 2', groupId: 'A' }
    ],
    [
        Item { label: 'Item 1', groupId: 'B' },
        Item { label: 'Item 2', groupId: 'B' }
    ]
]
到目前为止,一切都按预期进行,如JSFIDLE中所示

当我向数据数组添加新元素时,问题就开始了
vm
是下面代码中我的
ViewModel
实例

正如您可能已经猜到的,这会导致执行computed observable
视图,从而重新聚合底层数据数组。将创建所有新的
对象,这意味着与它们关联的任何状态信息都将丢失

这对我来说是个问题,因为我在
对象上存储了它们是否应该展开或折叠的状态:

function Group(label) {
    this.expanded = ko.observable(false); //Expanded state is stored here
    this.items = ko.observableArray();
    this.label = ko.observable(label);
}
因此,由于它们被重新创建,状态信息丢失。因此,所有组都会恢复到默认状态(已折叠)


我知道我面临的问题。然而,我正在努力想出一个不是笨拙的解决方案。我想到的可能解决方案包括:

维护组状态映射 我的第一个想法是创建一个用作地图的对象。它将使用groupId作为键名,状态为值:

{
    'A': GroupState { expanded: true },
    'B': GroupState { expanded: false }
}
即使每次添加元素时都会重新创建组,
GroupState
仍将保持不变。我无法解决的一个问题是如何从
GroupState
映射中删除不再存在的组

比如说,

vm.data(someNewArray);
其中,
someNewArray
是一个项目数组,没有与当前在
GroupState
映射中的项目对应的groupid。如何删除不再具有引用的条目?这似乎是我的应用程序中的内存泄漏

这个问题可以在这里看到。请注意,单击按钮后,组状态大小将增加到5个元素,但只有3个组。这是因为原来的两个组虽然不再使用,但没有被删除

维护viewState数组并删除计算的可观察对象 我的第二个想法是删除computed observable
视图。相反,我会有第二个可观察数组,名为
viewState
,它将是
数据
数组,在它被聚合到当前的“视图”中之后。然而,我很快就遇到了这个想法的问题

首先,我必须编写两个方法来维护两个数组之间的状态:
add()
remove()
clear()
,等等。考虑到必须这样做,我立即开始质疑是否有第二个数组是个好主意

其次,删除计算数组意味着线性搜索
viewState
数组,以查看当前元素是否包含与传入项类似的groupId。在实践中,这会非常快,我不喜欢在每次添加时迭代整个数组的理论(
O(n)
vs
O(1)
)。接下来,我可能会处理成千上万的项目


我觉得这可能有一个简单的解决办法,但我正在努力找到答案。有没有人有任何想法可以帮助解决这个问题,或者知道一种更好的方法来使用KnockoutJS实现这一点?也许,我上面的一个想法会带来一些新的见解(我仍然觉得
GroupState
地图走上了正确的轨道)


如果我遗漏了一些重要信息,请告诉我,我会尽力添加这些信息

这个建议怎么样:

  • 不要使用任何计算结果
  • 在创建新项目期间,如果组不存在,请创建该组,并将该项目添加到该组中
注: 我使用arrayFirst查找在创建项期间是否存在组,它是O(n),但您可以将组存储在属性中,以便查找应该是O(log(n))(未测试)


这听起来像是我上面提到的第二个“解决方案”。你能告诉我如何避免我在那里提到的问题吗?我想保留
数据
数组作为基础数据的原始数据。我需要保留一个
viewState
数组来保存已转换为适合当前视图的数据。我还必须线性导航
viewState
数组(
O(n)
),以确定新项目是否属于现有组,或者是否需要添加新组。不过,解决这一问题的一个办法可能是创建一个
映射。此外,如果您需要保持数据的可观察性(为什么?),您可以使用订阅功能在可观察数据发生更改时修改所有组和项。要获得更多改进,您应该使用pauseableObservables,以便在加载模型时不会更新视图模型。我之所以将其设置为可观察,是因为在添加项时,您会将其添加到原始数据数组中,然后所有内容都会从那里自动更新。我将检查这个订阅功能。直到现在才知道!我也去看看!还有一件事我在文档中没有看到
{
    'A': GroupState { expanded: true },
    'B': GroupState { expanded: false }
}
vm.data(someNewArray);
function Item(label, groupId) {
    var self = this;
    self.label = ko.observable(label);
    self.groupId = ko.observable(groupId);
}

function Group(label) {
    var self = this;
    self.expanded = ko.observable(false);
    self.items = ko.observableArray();
    self.label = ko.observable(label);

    self.addItem = function(item) {
        self.items.push(item);
    }
}

function ViewModel() {
    var self = this;

    self.groups = ko.observableArray();

    self.addItem = function(label, groupId) {
        var group = ko.utils.arrayFirst(self.groups(), function(gr) {
            return groupId == gr.label();
        });

        if(!group) {
            console.log('not group');
            group = self.addGroup(groupId);
        }

        var item = new Item(label, groupId);
        group.addItem(item);
    }

    self.addGroup = function(groupId) {
        var group = new Group(groupId);
        this.groups.push(group);
        return group;
    }

    this.buttonPressed = function () {
        vm.addItem("Item X", "A");
    };
}

var vm = new ViewModel();
ko.applyBindings(vm);

vm.addItem("Item 1", 'A');
vm.addItem("Item 2", 'A');
vm.addItem("Item 1", 'B');
vm.addItem("Item 2", 'B');