3

我正在尝试在嵌套的 JSON 对象上使用 Knockout 的映射插件,其中包含可变数据。但是,我不确定如何让它显示在我的 HTML 中。如何正确映射所有嵌套的 JSON 对象并将其显示为简单的字符串?这是我的代码:

JS

var ListModel = function(jsonData) {
  var self = this;
  self.master = ko.mapping.fromJS(jsonData);
}
var listModel = new ListModel(jsonData);
ko.applyBindings(listModel);

HTML

<!-- ko foreach: master -->
  <div data-bind="text: $data"></div> 
<!-- /ko -->

示例 JSON

{"Level 1a":"Hi","Level 1b":{
  "Level 2a":"Hello","Level 2b":{
    "Level 3":"Bye"}
  }
}

样本输出

Hi
  Hello
    Bye

我在这里要做的主要事情是打印出所有嵌套级别的值。嵌套级别的键值和数量是完全可变的(我在 SO 和在线找到的大多数嵌套 JSON 示例都是针对固定键的)。这可能吗?

更新:我找到了jQuery 等价物,但我仍然需要可观察对象的 Knockout 实现。

4

2 回答 2

5

由于您的 JSON 对象具有可变键,因此您必须首先将其转换为固定的、可预测的结构,否则嵌套模板映射将不起作用(knockout 是声明性的,因此您需要事先知道键名)。

考虑以下自定义映射代码(不需要淘汰映射插件):

var ListModel = function(jsonData) {
    var self = this;

    self.master = ko.observableArray([]);

    function nestedMapping(data, level) {
        var key, value, type;

        for (key in data) {
            if (data.hasOwnProperty(key)) {
                if (data[key] instanceof Object) {
                    type = "array";
                    value = ko.observableArray([]);
                    nestedMapping(data[key], value());
                } else {
                    type = "simple";
                    value = ko.observable(data[key]);
                }
                level.push({key: key, type: type, value: value});
            }
        }
    }

    nestedMapping(jsonData, self.master());
}

该函数nestedMapping()转换您的数据结构:

{
    "Level 1a": "Hi",
    "Level 1b": {
        "Level 2a": "Hello",
        "Level 2b": {
            "Level 3": "Bye"
        }
    }
}

进入:

[
    {
        "key": "Level 1a",
        "type": "simple",
        "value": "Hi"
    },
    {
        "key": "Level 1b",
        "type": "array",
        "value": [
            {
                "key": "Level 2a",
                "type": "simple",
                "value": "Hello"
            },
            {
                "key": "Level 2b",
                "type": "array",
                "value": [
                    {
                        "key": "Level 3",
                        "type": "simple",
                        "value": "Bye"
                    }
                ]
            }
        ]
    }
]

现在您可以创建一个像这样的模板:

<script type="text/html" id="nestedTemplate">
  <!-- ko if: type == 'simple' -->
  <div class="name" data-bind="text: value, attr: {title: key}"></div>
  <!-- /ko -->
  <!-- ko if: type == 'array' -->
  <div class="container" data-bind="
    template: {
      name: 'nestedTemplate', 
      foreach: value
    }
  "></div>
  <!-- /ko -->
</script>

看到它工作:http: //jsfiddle.net/nwdhJ/2/

请注意关于nestedMapping(). 它创建嵌套的 observables/observableArrays。但它适用于本数组实例(通过传递self.master()value()进入递归)。

这样您就可以避免在对象构造过程中出现不必要的延迟。每次将值推送到 observableArray 时,它都会触发淘汰更改跟踪,但我们不需要这样做。使用本机阵列会快得多。

于 2012-06-22T09:55:47.410 回答
2

将您的 JSON 数据更改为此(注意数组!):

[
  {
    "Text": "Hi",
    "Children": [
      {
        "Text": "Hello",
        "Children": [
          {
            "Text": "Bye"
          }
        ]
      }
    ]
  }
]

并使用自引用模板:

<script type="text/html" id="nestedTemplate">
  <div class="name" data-bind="text: Text"></div>
  <div class="container" data-bind="
    template: {
      name: 'nestedTemplate', 
      foreach: Children
    }
  "></div>
</script>

你这样称呼:

<div class="container" data-bind="
  template: {
    name: 'nestedTemplate', 
    foreach: master
  }
"></div>

然后您可以使用 CSS 来管理缩进:

/* indent from second level only */
div.container div.container {
  margin-left: 10px;
}

在 jsFiddle 上查看:http: //jsfiddle.net/nwdhJ/1/

于 2012-06-22T08:45:59.360 回答