【问题标题】:Displaying nested JSON data using Knockout Mapping使用 Knockout 映射显示嵌套的 JSON 数据
【发布时间】:2012-06-24 12:52:27
【问题描述】:

我正在尝试在嵌套的 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,但我仍然需要可观察对象的 Knockout 实现。

【问题讨论】:

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


    【解决方案1】:

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

    这样可以避免对象构造过程中不必要的延迟。每次将值推送到 observableArray 时,它都会触发剔除更改跟踪,但我们不需要这样做。使用原生数组会快很多。

    【讨论】:

    【解决方案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/

    【讨论】:

    • 您的 JSON 数据具有固定键(文本和子项),但我也需要允许可变键。
    • @Wei:从您的问题中不清楚这些变量键的外观(我可以猜到,但我不想)。如果您的键名发生变化,knockout 应该如何知道如何应用模板?此外,如果数据对象中的键名可能发生变化,我认为这是一种代码异味。如果键名是可变的,它们是 data 而不是键。
    • 你说得有道理。好吧,它们是可变的,因为传入的 JSON 数据可能具有诸如“Name”、“Date”、“Foo”、“Bar”等键名,但在 JSON 对象之前我们不知道它可能是什么进来。共同点是敲除只需要显示JSON对象内部的所有值。我也希望可以修复密钥,但是由于业务逻辑,可能会出现各种密钥名称。
    • @Wei:见我的其他答案。我保留了这个答案,因为我觉得这是解决此类问题的正确方法。由于您无法对 JSON 做任何事情,因此它不适用于您。但其他人可能会觉得它有帮助。
    猜你喜欢
    • 2012-09-24
    • 1970-01-01
    • 2022-08-03
    • 1970-01-01
    • 1970-01-01
    • 2021-11-19
    • 2017-11-19
    • 2012-04-16
    • 2019-09-15
    相关资源
    最近更新 更多