Knockout JS ObservableArray具有多对多关系

Sea*_*ean 4 javascript mvvm knockout-mapping-plugin knockout.js

我正在使用Knockout.js创建一个访客列表应用程序,到目前为止,事情正在顺利进行.但是我有一个最佳实践问题.我的应用程序有几种不同类型的对象:其中包含guest和tags.客人可以拥有多个标签,标签可以包含多个客人.在应用程序的不同位置,我需要单独显示两个数组.例如,我有一个"访客"视图,其中可以看到所有访客及其相关标签,我还有一个"标签"视图,其中可以看到所有标签及其关联的客人.目前,我向客户添加标签的代码如下所示:

var tag = function(opts) {
  this.guests = ko.observableArray()

  // Other tag code here...
}

var guest = function(opts) {
  this.tags = ko.observableArray()
  // Other guest code here...

  var self = this

  this.addTag = function(tag) {
    self.tags.push(tag)
    tag.guests.push(self)
  }
}
Run Code Online (Sandbox Code Playgroud)

我知道除了独立更新每个observableArray之外,必须有更好的方法在Knockout中进行这种多对多关系.这也导致了一种递归的app结构,其中guest有一个标签属性/数组,其中包含一个标签,该标签包含一个guest属性/数组,其中包含一个guest,它有一个标签属性......你得到了图片.

现在,ViewModel结构如下:

- Parent Object
  - Guests ObservableArray
    - Guest Object
      - Tag Object as property of Guest
  - Tags ObservableArray
    - Tag Object
      - Guest Object as property of Tag
Run Code Online (Sandbox Code Playgroud)

所以我想我的问题是双重的:1)是否有更好的方法来构建我的ViewModel以避免递归数组?2)我怎样才能更好地使用Knockout.js以干燥的方式更新我的ViewModel,而不是单独更新标签数组和来宾数组?谢谢!

Kye*_*ica 6

可能有其他方法可以做到这一点,但这种方法具有非常小的重复,而不会牺牲正确的建模.服务器在生成此格式的数据时应该没有问题.

这是一个(粗俗的)小提琴.请注意,单击标记或来宾将导致其下方的选择更新(默认情况下会选择第一个).

基本上,通过id存储关系,并使用computed数组来表示关联.这是一个基本的viewmodel:

var ViewModel = function(guests, tags) {
    var self = this;
    self.guests = ko.observableArray(
        ko.utils.arrayMap(guests, function(i){ return new Guest(i); }
    ));
    self.tags= ko.observableArray(
        ko.utils.arrayMap(tags, function(i){ return new Tag(i); }
    ));

    self.selectedGuest = ko.observable(self.guests()[0]);
    self.selectedTag = ko.observable(self.tags()[0]);

    self.guestTags = ko.computed(function() {
        return ko.utils.arrayFilter(self.tags(), function(t) {
            return self.selectedGuest().tags().indexOf(t.id()) > -1;
        });        
    });

    self.tagGuests = ko.computed(function() {
        return ko.utils.arrayFilter(self.guests (), function(g) {
            return self.selectedTag().guests().indexOf(g.id()) > -1;
        });        
    });
};
Run Code Online (Sandbox Code Playgroud)

UPDATE

所以我做了一个新的小提示来演示不同类型的映射,但是这个代码可以很容易地与上面的视图模型共存; 它唯一的单独示范.它提供了一般查找,而不是处理选择,因此任何代码都可以使用它.下面是Tags中的HTML(来宾是对称的),以及guestMap添加到viewmodel 的函数.

您将注意到名称input现在是,所以您可以更改名称并观察所有绑定保持最新.让我知道你的想法:

<div>Tags
    <ul data-bind="foreach: tags">
        <li>
            <input data-bind="value: name, valueUpdate: 'afterkeydown'" />
            </br><span>Tags</span>
            <ul data-bind="foreach: guests">
                <li><span data-bind="text: $parents[1].guestMap($data).name()"></span></li>
            </ul>
        </li>
    </ul>
</div>
Run Code Online (Sandbox Code Playgroud)
self.guestMap = function(id) {
        return ko.utils.arrayFirst(self.guests(), function(g) {
            return id == g.id();
        });
    };   
Run Code Online (Sandbox Code Playgroud)