Utilizando mapeo de Knockout para JSON complejo

Most of Knockout parece muy intuitivo. Sin embargo, una cosa que me resulta extraña es cómo funciona el complemento de mapeo. Esperaba / esperaba poder alimentarlo JSON desde una llamada ajax, y tener una especie de modelo de vista "dinámica" que está disponible para referencia en mi HTML.

Losdescripción del complemento de mapeo incluso hace que parezca que así es como funciona:

"Si sus estructuras de datos se vuelven más complejas (por ejemplo, contienen elementos secundarios o matrices), esto se vuelve muy engorroso de manejar manualmente. Lo que le permite hacer el complemento de mapeo es crear un mapeo desde el objeto JavaScript normal (o la estructura JSON) a un modelo de vista observable ".

Pero parece que realmente necesita definir el modelo de vista primero en su código, y luego puede completarlo después del hecho utilizando el complemento de mapeo y algunos datos JSON. ¿Es esto correcto

Un ejemplo concreto de lo que estaba tratando de hacer.

Estoy tratando de usar Knockout con Solr (un motor de búsqueda que devuelve resultados de búsqueda JSON). La estructura esquelética de los datos JSON devueltos por Solr es:

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

e hecho, esa es la estructura que estoy introduciendo en mi modelo de vista mapeada cuando lo configuré por primera vez.

Solo para que entienda un poco sobre cómo los datos JSON vuelven de Solr: La matriz response.docs contiene una matriz de valores hash, donde los datos hash se componen de claves / valores para sus datos de documentos indexados. Cada hash en la matriz es un documento que se devuelve en los resultados de búsqueda.

Esa parte parece mapearse muy bien.

La parte de "resaltado" de JSON es lo que me causa problemas. Cuando intento hacer referencia a los campos resaltados en mi HTML, obtengo ReferenceErrors. Aquí hay un ejemplo de cómo se vería el campo de resaltado en el 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"]
    }
}

Tengo un foreach en mi HTML que intenta analizar cada elemento response.docs, y si la parte resaltada del objeto contiene una coincidencia para el campo Id de ese documento, quiero sustituir el Título resaltado en lugar del Título predeterminado. (En el siguiente código, "Resultados" es el nombre del modelo de vista al que estoy asignando el 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>

Cuando intento usar esto, siempre aparece este 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}

He intentado variaciones sobre cómo estoy haciendo referencia a los datos, pero parece que no puedo acceder a ellos.

Edita Estoy avanzando un poco. En mi definición de mapeo, ahora especifico "resaltar" así:

"highlighting": ko.observable({})

En lugar de solo establecer el resaltado en {}. Ahora al menos puedo mirar un poco en los datos de resaltado cuando hago mi mapeo. Sin embargo, sigo viendo errores extraños.

He simplificado mi código HTML de prueba para escupir los datos de resaltado para cada resultado de búsqueda:

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

Esto devuelve múltiples<pre> etiquetas ahora que se ven así:

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

Sin embargo, si cambio ese código HTML a esto:

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

Sigo recibiendo errores como este:

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

¡No tiene ningún sentido para mí! Mi prueba anterior muestra que los datos disponibles contienen una clave de "Título", ¿por qué no puedo acceder a esos datos?

Edita Yo creéa jsfiddle, pero por supuesto ... funciona como se esperaba. No puedo reproducir mi problema en jsfiddle. : -

Edita OK, estoy avanzando un poco aquí, pero todavía estoy muy confundido sobre lo que está sucediendo. Primero cambié mi HTML de depuración a esto:

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

uego envié mi llamada ajax y noté en la consola de Chrome este resultado:

undefined
undefined
> Object

Así que por alguna razón, laforeach loop se repite en 3 Results.response.docs, y los dos primeros no se asignan a nada en mi objeto resaltado (), por lo que regresan indefinidos, y es por eso que mi intento de extraer la propiedad .Title estaba fallando.

ara confirmar esto, envolví unko if: $root.Results.highlighting()[Id()] alrededor de ese bloque, y finalmente pude acceder a la propiedad .Title durante el bucle foreach sin un error JS.

Esto todavía me deja con la pregunta de por qué / cómo hay 3 objetos Results.response.docs en bucle. ¿Quizás el enlace foreach se ejecuta 3 veces, y las primeras 2 veces el objeto resaltador está vacío, y la tercera vez, finalmente se completa? Pero me cuesta entender por qué sería eso.

Otra pista posible: si disparo la llamada ajax por segunda vez, sin volver a cargar la página, puedo ver que esas 3 "pasadas" devuelven un Objeto válido cada vez en el registro de la consola. Entonces, en lugar de dosundefineds y un objeto, son tres objetos todos en una fila.

En mi salida HTML, sin embargo, solo veo una fila de datos. Así que eso parece demostrar que no está girando sobre 3 elementos, sino que de hecho se está ejecutando 3 veces. La pregunta sigue siendo ... ¿POR QUÉ?

Respuestas a la pregunta(2)

Su respuesta a la pregunta