使用Knockout Mapping显示嵌套的JSON数据

时间:2012-06-22 08:17:40

标签: json knockout.js knockout-mapping-plugin knockout-2.0

我正在尝试在嵌套的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 equivalent,但我仍然需要针对observable的Knockout实现。

2 个答案:

答案 0 :(得分:5)

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

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

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。但它适用于 native 数组实例(通过将self.master()value()传递到递归中)。

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

答案 1 :(得分: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/