【问题标题】:Using Knockout mapping for complex JSON对复杂 JSON 使用 Knockout 映射
【发布时间】:2012-04-16 15:33:27
【问题描述】:

大部分 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": {}
  }

事实上,这就是我在第一次设置映射视图模型时为其提供的结构。

只是为了让您了解一点关于如何从 Solr 返回 JSON 数据: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 字段的匹配项,我想替换突出显示的标题而不是默认标题. (在下面的代码中,“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>

这会返回多个&lt;pre&gt; 标签,现在看起来像这样:

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

对我来说毫无意义!我之前的测试表明,可用的数据确实包含“标题”键,为什么我不能访问该数据?

编辑我创建了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() 对象中的任何内容,因此它们返回未定义 - 这就是为什么我的尝试拉取 .Title 属性失败。

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

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

另一个可能的线索:如果我第二次触发 ajax 调用,而不重新加载页面,我可以看到这 3 个“通过”每次都在控制台日志中返回一个有效的对象。所以不是两个undefineds 和一个对象,而是三个连续的对象。

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

【问题讨论】:

  • 也许你可以使用这个小提琴:jsfiddle.net/rniemeyer/ZrY5R 并让它与你的代码相似(更新或分叉它),这样我们就可以更好地了解你的结构。
  • @Mason - 我不认为你的 jsfiddle 像你想象的那样工作。您在上面发布的链接对我不起作用。我检查了同一个小提琴的最新版本,您分配了 self.Results 两次,因此映射插件结果被覆盖。
  • @RPNiemeyer 感谢您的帮助。正如您在上面的编辑中看到的那样,我已经取得了更多进展,但不幸的是,仍然无法在 jsfiddle 中说明这个问题。

标签: knockout.js knockout-mapping-plugin


【解决方案1】:

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

$root.Results.highlighting[Id()]

我相信您可能会感到困惑,因为您的一个小提琴分配了 self.Results 两次,这使它看起来以一种方式工作,而实际上问题被掩盖了。

这是工作版本

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

希望这会有所帮助。

【讨论】:

  • 嘿@madcapnmckay,感谢您的回复!您可能在我的问题底部错过了我的更新(编辑)。我最终发现我需要将突出显示定义为 ko.observable({})。我的 jsfiddle 搞砸了,但我两次分配 self.Results 的原因是我试图说明初始设置,然后是我的 ajax 触发的第二个调用。我有一个更新的 jsfiddle 更正确地显示了这一点(我认为)。当然它并不能说明我的问题,因为它工作正常。 jsfiddle.net/dWQfP/5
  • @MasonG.Zhwiti - 我看到了那个编辑,但我认为不需要它。我的解决方案不需要它来工作。对标记进行简单的更正就足够了。如果您想影响为特定属性创建的内容,您应该使用create 映射选项。我对你现在的问题有点迷茫。一切都解决了吗?
  • 即使在实施了您的修复之后,我仍然无法在此特定 JSON 数据上使用映射。我永远无法让它正常工作,它最终似乎多次触发了我的 if 块,并且只有在“最终”触发器上,所有数据都填充到映射对象上。我现在已经放弃了使用插件映射对象,而只是自己做,这极大地简化了我的 javascript 代码,以及......实际工作。 :-)
  • 这里对您的数据模型稍作修改,可能更容易使用...jsfiddle.net/beauxjames/ps75V
猜你喜欢
  • 1970-01-01
  • 2018-10-27
  • 2021-08-15
  • 2019-02-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-02-22
  • 2020-03-08
相关资源
最近更新 更多