对复杂的JSON使用Knockout映射 [英] Using Knockout mapping for complex JSON

查看:51
本文介绍了对复杂的JSON使用Knockout映射的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

大多数淘汰赛似乎都非常直观.不过,令我感到奇怪的是,映射插件的工作方式.我期望/希望我能够从ajax调用中馈入JSON,并拥有一种可在HTML中引用的动态"视图模型.

Most of Knockout seems very intuitive. One thing that is strange to me though is how the mapping plugin works. I was expecting/hoping I would be able to feed it JSON from an ajax call, and have a sort of "dynamic" view model that is available to reference in my HTML.

映射插件的描述甚至听起来像是这样的:

The description of the mapping plugin even makes it sound like this is how it works:

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

"If your data structures become more complex (e.g. they contain children or contain arrays) this becomes very cumbersome to handle manually. What the mapping plugin allows you to do is create a mapping from the regular JavaScript object (or JSON structure) to an observable view model."

但是看来您实际上确实需要先在代码中定义视图模型,然后可以使用映射插件和一些JSON数据在事后填充视图模型.是这样吗?

But it appears you actually need to define the view model first in your code, and then you can populate it after the fact using the mapping plugin and some JSON data. Is this right?

我正在尝试做的一个具体例子.

A concrete example of what I was trying to do.

我正在尝试将淘汰赛与Solr(返回JSON搜索结果的搜索引擎)一起使用. Solr返回的JSON数据的框架结构为:

I am trying to use Knockout with Solr (a search engine that returns JSON search results). The skeleton structure of the JSON data returned by Solr is:

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

事实上,这就是我第一次设置映射视图模型时要使用的结构.

In fact, that's the structure I am feeding into my mapped view model when I first set it up.

因此,您对从Solr返回JSON数据的方式有所了解:response.docs数组包含一个哈希数组,其中哈希数据由索引文档数据的键/值组成.数组中的每个哈希都是在搜索结果中返回的一个文档.

Just so you understand a bit about how the JSON data comes back from Solr: The response.docs array contains an array of hashes, where the hash data is comprised of key/values for your indexed document data. Each hash in the array is one document being returned in your search results.

该部分似乎映射得很好.

That part seems to map over just fine.

JSON的突出显示"部分是导致我遇到问题的原因.当我尝试引用HTML中的突出显示字段时,出现ReferenceErrors.这是JSON中突出显示字段的外观示例:

The "highlighting" part of the JSON is what causes me problems. When I try to reference the highlighting fields in my HTML, I get ReferenceErrors. Here's an example of what the highlighting field might look like in the 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 . (在下面的代码中,结果"是我要将JSON映射到的视图模型的名称.)

I have a foreach in my HTML that tries to parse through each response.docs element, and if the highlighting part of the object contains a match for that document's Id field, I want to substitute the highlighted Title rather than the default Title. (In the code below, "Results" is the name of the viewmodel I am mapping the JSON over to.)

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

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

When I try to use this, I always get this error:

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}

我已经尝试过各种如何引用数据的方法,但是似乎无法访问它.

I've tried variations on how I am referencing the data, but I just can't seem to access it.

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

Edit I'm making a bit of headway. In my mapping definition, I now specify "highlighting" like this:

"highlighting": ko.observable({})

不仅仅是将突出显示设置为{}.现在,当我进行映射时,至少可以对突出显示的数据进行一些观察.但是我仍然看到奇怪的错误.

Rather than just setting highlighting to {}. Now I am at least able to peer a bit into the highlighting data when I do my mapping. Yet I'm still seeing strange errors.

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

I've simplified my test HTML code to just spit out the highlighting data for each search result:

<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>标签,如下所示:

This returns multiple <pre> tags now that look like this:

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

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

However, if I change that HTML code to this:

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

我继续收到这样的错误:

I continue to get errors like this:

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

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

Makes no sense to me! My previous test shows that the data available does contain a "Title" key, why can't I access that data?

编辑,我创建了一个jsfiddle ,但是当然...它按预期工作.我无法在jsfiddle上重现我的问题. :-(

Edit I created a jsfiddle, but of course... it works as expected. I am unable to reproduce my issue on jsfiddle. :-(

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

Edit OK I am making some headway here, but am still very confused as to what is going on. First I changed my debugging HTML to this:

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

然后我提交了ajax调用,并且在Chrome控制台中注意到了以下输出:

I then submitted my ajax call, and I noticed in the Chrome console this output:

undefined
undefined
> Object

因此出于某种原因,foreach循环正在3个Results.response.docs中循环,而前两个未映射到我的Highlights()对象中的任何内容,因此它们返回的是未定义的-这就是为什么尝试拉出.Title属性失败.

So for some reason, the foreach loop is looping over 3 Results.response.docs, and the first two are not mapping to anything in my highlights() object, so they are returning undefined -- and that's why my attempt to pull the .Title property was failing.

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

To confirm this, I wrapped a ko if: $root.Results.highlighting()[Id()] around that block, and was finally able to access the .Title property during the foreach loop without a JS error.

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

This still leaves me with the question of why/how there are 3 Results.response.docs objects being looped over. Perhaps the foreach binding is being run 3 times, and the first 2 times the highlighting object is empty, and the third time, it's finally filled in? But I am having a hard time figuring out why that would be.

另一个可能的线索:如果我第二次触发ajax调用而没有重新加载页面,则每次在控制台日志中都可以看到这3个"pass"都返回一个有效的Object.因此,代替了两个undefined和一个Object,而是连续三个对象.

Another possible clue: if I trigger the ajax call a second time, without reloading the page, I can see that those 3 "passes" all return a valid Object each time in the console log. So instead of two undefineds and an Object, it's three Objects all in a row.

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

On my HTML output, though, I only see one row of data. So that seems to prove that it's not looping over 3 elements, but is in fact being run 3 times. The question remains... WHY?

推荐答案

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

The mapping plugin is working as you expect. Your problem is simply that you are expecting the plugin to create observables at every level in the object. This is not how the plugin works. It will only create observables for "leaf" properties. So in your case $root.Results.highlighting is not created as an observable. The id properties on docs are however created as observables so the solution is.

$root.Results.highlighting[Id()]

我相信您可能会感到困惑,因为您的小提琴之一正在分配自我.结果两次,这实际上在掩盖问题时似乎以一种方式起作用.

I believe you may have been confused because one of your fiddles was assigning self.Results twice which made it appear to work one way when in fact the problem was being masked.

这是工作版本

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

希望这会有所帮助.

这篇关于对复杂的JSON使用Knockout映射的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆