使用Knockout映射来处理复杂的JSON

时间:2012-04-03 00:08:58

标签: knockout.js knockout-mapping-plugin

Knockout的大多数看起来非常直观。但有一件事我很奇怪,映射插件的工作原理。我期待/希望我能够从ajax调用中提供JSON,并且有一种"动态"查看可在我的HTML中引用的模型。

description of the mapping plugin甚至会让它听起来像它的工作方式:

  

"如果您的数据结构变得更加复杂(例如,它们包含   儿童或包含数组)这变得非常麻烦   手动。映射插件允许您执行的操作是创建映射   从常规JavaScript对象(或JSON结构)到   可观察的视图模型。"

但是看起来你实际上需要首先在代码中定义视图模型,然后你可以使用映射插件和一些JSON数据在事后填充它。这是对的吗?

我尝试做的一个具体例子。

我正在尝试使用Knockout和Solr(一个返回JSON搜索结果的搜索引擎)。 Solr返回的JSON数据的骨架结构是:

  {
      "responseHeader": {
          "status": 0,
          "QTime": 0,
          "params": {
              "facet": "true",
              "facet.field": "System",
              "q": "testphrase",
              "rows": "1",
              "version": "2.2"
          }
      },
      "response": {
          "numFound": 0,
          "start": 0,
          "maxScore": 0.0,
          "docs": []
      },
      "facet_counts": {
          "facet_queries": {},
          "facet_fields": {
              "System": []
          },
          "facet_dates": {},
          "facet_ranges": {}
      },
      "highlighting": {}
  }

事实上,当我第一次设置它时,我正在为我的映射视图模型提供结构。

让您了解一下JSON数据如何从Solr返回:response.docs数组包含一个哈希数组,其中哈希数据由索引文档数据的键/值组成。数组中的每个哈希都是在搜索结果中返回的一个文档。

这部分似乎很好。

"突出显示" JSON的一部分是导致我出现问题的原因。当我尝试在HTML中引用突出显示字段时,我得到了ReferenceErrors。以下是JSON中突出显示字段的示例:

"highlighting": {
    "2-33-200": {
        "Title": ["1992 <b>Toyota</b> Camry 2.2L CV Boots"]
    },
    "2-28-340": {
        "Title": ["2003 <b>Toyota</b> Matrix 2.0L Alignment"]
    },
    "2-31-2042": {
        "Title": ["1988 <b>Toyota</b> Pickup 2.4L Engine"]
    }
}

我的HTML中有一个foreach试图解析每个response.docs元素,如果对象的突出显示部分包含该文档的Id字段的匹配,我想替换突出显示的Title而不是比默认的标题。 (在下面的代码中,&#34; Results&#34;是我将JSON映射到的viewmodel的名称。)

<div id="search-results" data-bind="foreach: Results.response.docs">
    <div data-bind="attr: { id: 'sr-' + Id }" class="search-result">
        <h3 class="title"><a data-bind="html: (($root.Results.highlighting[Id]['Title'] != undefined) ? $root.Results.highlighting[Id]['Title'] : Title), attr: {href: Link}"></a></h3>
        <span class="date" data-bind="text: DateCreated"></span>
        <span class="snippet" data-bind="html: Snippet"></span>
    </div>
</div>

当我尝试使用它时,我总是会收到此错误:

Uncaught Error: Unable to parse bindings.
Message: TypeError: Cannot read property 'Title' of undefined;
Bindings value: html: (($root.Results.highlighting[Id]['Title'] != undefined)  ? $root.Results.highlighting[Id]['Title'] : Title), attr: {href: Link}

我已尝试过如何引用数据,但我似乎无法访问它。

修改我取得了一些进展。在我的映射定义中,我现在指定&#34;突出显示&#34;像这样:

"highlighting": ko.observable({})

而不是仅仅将突出显示设置为{}。现在,当我进行映射时,我至少能够对应突出显示的数据。然而,我仍然看到奇怪的错误。

我已将我的测试HTML代码简化为只为每个搜索结果吐出突出显示数据:

<div id="search-results" data-bind="foreach: Results.response.docs">
    <pre data-bind="text: JSON.stringify(ko.toJS($root.Results.highlighting()[Id()]), null, 2)"></pre>
</div>

现在返回多个<pre>标签,如下所示:

{
  "Title": [
    "1992 <b>Toyota</b> Camry 2.2L CV Boots"
  ]
}

但是,如果我将HTML代码更改为:

<pre data-bind="text: $root.Results.highlighting()[Id()]['Title']"></pre>

我继续遇到这样的错误:

Message: TypeError: Cannot read property 'Title' of undefined;
Bindings value: text: $root.Results.highlighting()[Id()]['Title']

对我毫无意义!我之前的测试显示,可用的数据确实包含一个&#34;标题&#34;关键,为什么我无法访问该数据?

修改我创建了a jsfiddle,但当然......它按预期工作。我无法在jsfiddle上重现我的问题。 : - (

编辑好的我在这里取得了一些进展,但我仍然对发生的事情感到困惑。首先,我将调试HTML更改为:

<div id="search-results" data-bind="foreach: Results.response.docs">
    <pre data-bind="text: console.log($root.Results.highlighting()[Id()])"></pre>
</div>

然后我提交了我的ajax电话,我在Chrome控制台中注意到这个输出:

undefined
undefined
> Object

所以出于某种原因,foreach循环遍历3个Results.response.docs,前两个没有映射到我的highlight()对象中的任何内容,所以它们返回undefined - 而且& #39;为什么我试图拉动.Title属性失败。

为了确认这一点,我在该块周围包裹了一个ko if: $root.Results.highlighting()[Id()],并且最终能够在foreach循环期间访问.Title属性而没有JS错误。

这仍然让我想到为什么/如何有3个Results.response.docs对象被循环的问题。也许foreach绑定正在运行3次,突出显示对象的前2次是空的,第三次,它最终被填入?但我很难搞清楚为什么会这样。

另一个可能的线索:如果我第二次触发ajax调用,而不重新加载页面,我可以看到那些3&#34;传递&#34;每次在控制台日志中都返回一个有效的Object。因此,它不是两个undefined和一个对象,而是连续三个对象。

但是,在我的HTML输出中,我只看到一行数据。所以这似乎证明它没有循环3个元素,但实际上是运行3次。问题仍然是......为什么?

1 个答案:

答案 0 :(得分:3)

映射插件正如您所期望的那样工作。您的问题很简单,您希望插件在对象的每个级别创建可观察对象。这不是插件的工作方式。它只会为“叶子”属性创建可观察对象。因此,在您的情况下,$root.Results.highlighting不会被创建为可观察对象。然而,docs上的id属性被创建为observables,因此解决方案是。

$root.Results.highlighting[Id()]

我相信你可能会感到困惑,因为你的一个小提琴正在分配自我。结果两次使得它似乎以一种方式工作,而事实上问题被掩盖了。

这是工作版

http://jsfiddle.net/madcapnmckay/UaBKe/

希望这有帮助。