Использование Knockout mapping для сложного JSON

Большая часть нокаута кажется очень интуитивной. Одна вещь, которая для меня странна, это то, как работает подключаемый модуль. Я ожидал / надеюсь, что смогу передать JSON из вызова ajax, и у меня будет своего рода «динамическая» модель представления, доступная для ссылки в моем HTML.

описание картографического плагина даже звучит так, как это работает:

«Если ваши структуры данных становятся более сложными (например, они содержат дочерние элементы или содержат массивы), это становится очень трудоемким для обработки вручную. Плагин сопоставления позволяет вам делать это, создавая сопоставление из обычного объекта JavaScript (или структуры JSON) с наблюдаемым посмотреть модель. "

Но, похоже, вам действительно нужно сначала определить модель представления в своем коде, а затем заполнить ее после факта, используя подключаемый модуль отображения и некоторые данные JSON. Это правильно?

Конкретный пример того, что я пытался сделать.

Я пытаюсь использовать Knockout с Solr (поисковая система, которая возвращает результаты поиска JSON). Каркасная структура данных JSON, возвращаемых Solr:

  {
      "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 этого документа, я хочу заменить выделенный заголовок, а не заголовок по умолчанию. (В приведенном ниже коде «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']

Не имеет смысла для меня! Мой предыдущий тест показал, что доступные данные содержат клавишу «Заголовок», почему я не могу получить доступ к этим данным?

редактировать я создал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, и первые два не сопоставляются ни с чем в моем объектеlights (), поэтому они возвращают неопределенное значение, и поэтому моя попытка получить свойство .Title не удалась.

Чтобы подтвердить это, я завернулko if: $root.Results.highlighting()[Id()] вокруг этого блока и, наконец, смог получить доступ к свойству .Title во время цикла foreach без ошибки JS.

Это все еще оставляет меня с вопросом о том, почему / как есть 3 объекта Results.response.docs, которые зацикливаются. Возможно, привязка foreach запускается 3 раза, и первые 2 раза подсвечивающий объект пуст, а в третий раз он наконец заполняется? Но мне трудно понять, почему это так.

Другая возможная подсказка: если я вызову ajax-вызов второй раз, не перезагружая страницу, я вижу, что все эти 3 «прохода» возвращают действительный объект каждый раз в журнале консоли. Так что вместо двухundefineds и объект, это три объекта все подряд.

Однако в моем выводе HTML я вижу только одну строку данных. Кажется, это доказывает, что он не зацикливается на 3 элементах, а фактически запускается 3 раза. Вопрос остается ... ПОЧЕМУ?

Ответы на вопрос(1)

Ваш ответ на вопрос