映射和绑定嵌套对象和数组

Mapping and binding nested objects and arrays

本文关键字:数组 对象 绑定 映射 嵌套      更新时间:2023-09-26

我有一个object,在这个对象中我有个项,其中一个项是array,它也包含objects。数据示例如下所示。

我使用敲除将这些数据绑定到视图,所以我认为我需要实现一个双循环,用于返回对象和子数组中的对象,以便能够在视图中绑定它们。

样本数据:

"singers": {
        "ijiyt6ih": {
            "id": ObjectId('ijiyt6ih'),
            "name": "John",
            "songs": [
                {
                    "id": ObjectId('okoiu8yi'),
                    "songName": "Hello There",
                    "year": "1980"
                },
                {
                    "id": ObjectId('sewfd323'),
                    "songName": "No More",
                    "year": "1983"
                }
            ]
        },
        "98usd96w": {
            "id": ObjectId('98usd96w'),
            "name": "Jack",
            "songs": [
                {
                    "id": ObjectId('iew342o3'),
                    "songName": "Hurry Up",
                    "year": "1985"
                }
            ]
        }
    }

我需要找到一种适当循环的方法,这样我就可以修改返回的数据,使用knockout将其绑定到viewModel。

以下是我的viewModel的样子:

singersViewModel = function(data) {
   var self = {
           singerId: ko.observable(data.id),
           singerName: ko.observable(data.name),
           songName: ko.observable(...),
           songYear: ko.observable(...)
       };

我不确定是否必须返回两组不同的数据。

至于循环。我可以循环并返回歌手列表以显示在页面上,但我无法获得每个歌手中显示的歌曲列表。

到目前为止,这是我的循环:

var self = {},
    singer,
    tempSingers = [];
    self.singers = ko.observableArray([]);
    for (singer in singers) {
        if (singers.hasOwnProperty(singer)) {
             tempSingers.push(new singersViewModel(singers[singer]));
           }
     }
     self.singers(tempSingers);

我试图为循环中的歌曲复制相同类型的循环,但使用hasOwnProperty时会出错,因为歌曲是array

在包含的片段中,您可以看到如何将原始数据映射到可以绑定到视图的视图模型。

我将id保留为常规属性,并将名称转换为可观测值,以便对其进行编辑。在底部,您可以看到当前的视图模型状态。

还有一个示例视图,它迭代歌手列表,以及每个歌手中的歌曲列表。

正如您所看到的,我正在使用映射实现解决方案。对于映射,您需要实现一个回调,该回调接收每个原始对象并返回一个具有新结构的新对象。例如,这部分代码

_.map(_singers, function(singer) {
    return {
      id: singer.id,
      name: ko.observable(singer.name),
      // ... songs: 
})

迭代每个歌手(问题中的样本数据),并为每个歌手创建一个带有id的新对象,一个包含名称的可观察对象(以及歌曲的映射,我在这个片段中没有显示)。

注意:我使用的是lodash,但许多浏览器本机支持将map作为数组函数

var ObjectId = function (id) { return id; }
var singers = {
        "ijiyt6ih": {
            "id": ObjectId('ijiyt6ih'),
            "name": "John",
            "songs": [
                {
                    "id": ObjectId('okoiu8yi'),
                    "songName": "Hello There",
                    "year": "1980"
                },
                {
                    "id": ObjectId('sewfd323'),
                    "songName": "No More",
                    "year": "1983"
                }
            ]
        },
        "98usd96w": {
            "id": ObjectId('98usd96w'),
            "name": "Jack",
            "songs": [
                {
                    "id": ObjectId('iew342o3'),
                    "songName": "Hurry Up",
                    "year": "1985"
                }
            ]
        }
    };
var SingersVm = function(_singers) {
  var self = this;
  self.singers = _.map(_singers, function(singer) {
    return {
      id: singer.id,
      name: ko.observable(singer.name),
      songs: _.map(singer.songs, function(song) {
        return {
          name: ko.observable(song.songName),
          id: song.id
          };
        })
    };
  });
  return self;
};
var vm = new SingersVm(singers);
//console.log(vm);
ko.applyBindings(vm);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/3.10.1/lodash.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div data-bind="foreach: singers">
  <div>
      <input data-bind="value: name"/> (<span data-bind="text: id"></span>)
      <ul data-bind="foreach:songs">
        <li>
          <input data-bind="value: name"/> (<span data-bind="text: id"></span>)
        </li>
      </ul>
  </div>
</div>
        
<pre data-bind="html: ko.toJSON($root,null,2)">
</pre>