对复杂的JSON使用Knockout映射



大部分Knockout似乎都非常直观。不过,有一件事对我来说很奇怪,那就是映射插件是如何工作的。我期望/希望能够通过ajax调用为其提供JSON,并拥有一种可在HTML中引用的"动态"视图模型。

映射插件的描述甚至让它听起来像是它的工作方式:

"如果您的数据结构变得更加复杂(例如子对象或包含数组),这将变得非常麻烦手动。映射插件允许您创建映射从常规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,而不是默认的Title。(在下面的代码中,"Results"是我将JSON映射到的视图模型的名称。)

<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}

我已经尝试了各种各样的数据引用方式,但我似乎无法访问它。

编辑我正在取得一些进展。在我的映射定义中,我现在指定"突出显示"如下:

"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']

对我来说毫无意义!我之前的测试显示,可用的数据确实包含"Title"键,为什么我不能访问该数据?

编辑我创建了一个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上循环,前两个没有映射到我的highlights()对象中的任何内容,所以它们返回了未定义的内容——这就是我尝试提取.Title属性失败的原因。

为了证实这一点,我在该块周围封装了一个ko if: $root.Results.highlighting()[Id()],并最终能够在foreach循环期间访问.Title属性,而不会出现JS错误。

这仍然给我留下了一个问题,为什么/如何有3个Results.response.docs对象被循环。也许foreach绑定运行了3次,前2次高亮显示对象为空,第三次,它终于被填充了?但我很难弄清楚为什么会这样。

另一条可能的线索是:如果我第二次触发ajax调用,而不重新加载页面,我可以看到这3次"通过"都在控制台日志中每次返回一个有效的Object。因此,它不是两个undefined和一个Object,而是三个Object都在一行中。

不过,在我的HTML输出中,我只看到一行数据。因此,这似乎证明了它不是在3个元素上循环,而是实际上运行了3次。问题仍然存在。。。为什么?

映射插件正在按预期工作。您的问题很简单,您希望插件在对象的每个级别都创建可观察的对象。插件不是这样工作的。它只会为"叶子"属性创建可观测值。因此,在您的情况下,$root.Results.highlighting并不是作为可观察对象创建的。然而,文档上的id属性被创建为可观察的,因此解决方案是。

$root.Results.highlighting[Id()]

我相信你可能会感到困惑,因为你的一个小提琴手在分配自我。两次的结果使它看起来是单向的,而事实上问题被掩盖了。

这是的工作版本

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

希望这能有所帮助。

最新更新