2011-06-22 18 views
34

me gusta mucho la espina dorsal, pero estoy teniendo el momento más difícil hacer lo que parece ser cosas simples. Agradezco cualquier ayuda con el siguiente ejemplo.Backbone.js manejo de atributos que son matrices

que tienen un modelo, criterios, que quiero usar para almacenar el estado de algunos artículos en mi interfaz de usuario. hay un par de atributos simples, y un atributo que es una matriz de ID utilizados para almacenar los ID de las etiquetas que el usuario ha seleccionado en la interfaz de usuario.

Por lo tanto, crear una nueva instancia. Agregué algunos elementos a la matriz de etiquetas. Entonces, quiero comenzar de nuevo, crear una nueva instancia, asignada a la misma variable. Sin embargo, mi matriz de etiquetas continúa conservando la información que agregué como parte de la primera instancia de Criteria.

me han documentado el caso de prueba a continuación.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 
<html xmlns="http://www.w3.org/1999/xhtml"> 
<head> 
    <title>Test</title> 
    <script src="Scripts/Libraries/jquery-1.6.1.js" type="text/javascript"></script> 
    <script src="Scripts/Libraries/underscore.js" type="text/javascript"></script> 
    <script src="Scripts/Libraries/backbone.js" type="text/javascript"></script> 

    <script language="javascript" type="text/javascript"> 

     $(function() { 

      // Simple model to hold some state about my UI. 
      var Criteria = Backbone.Model.extend({ 

       defaults: { 
        "status": "Normal", 
        "priority": "Normal", 
        "tags": new Array() 
       } 

      }); 

      // Create new criteria. 
      window.criteria = new Criteria(); 

      // The length of the tags array should be 0. PASSES 
      console.log("Expect 0: Actual " + window.criteria.get("tags").length); 

      // Add a tag id to the tags array. 
      window.criteria.get("tags").push(5); // Tag with ID of 5. 

      // The length of the tags array should be 1. PASSES 
      console.log("Expect 1: Actual " + window.criteria.get("tags").length); 

      // Create a new instance of criteria. 
      window.criteria = new Criteria(); 

      // The length of the tags array should be 0. FAILS 
      // CONFUSED. I thought this is now a new instance with a new set of attributes. 
      // Why does the tags collection still have an item in it. 
      console.log("Expect 0: Actual " + window.criteria.get("tags").length); 

      // OK. So, I will call the clear method on the model. This is supposed to remove all attributes 
      // from the model. 
      // Then, I will create it again. 
      window.criteria.clear(); 
      window.criteria = new Criteria(); 

      // The length of the tags array should be 0. FAILS. Still 1. 
      console.log("Expect 0: Actual " + window.criteria.get("tags").length); 

      // ARGH! 
      console.log("HELP!"); 

     }); 

    </script> 

</head> 
<body> 
    <h1>Test</h1> 
    <p>Backbone test page.</p> 
</body> 
</html> 

¿Estoy lejos de la marca aquí? ¿Estoy tratando de usar Backbone para cosas que no fue intencional? ¿O me falta algo más general en la programación javascript OO?

P.S. Originalmente utilicé una colección de etiquetas Backbone, pero eso presentaba un conjunto diferente de problemas relacionados con tener un modelo Tag referenciado en múltiples colecciones y cómo el método remove de Backbone desata la referencia de "colección" cuando un elemento se elimina de cualquier colección. Otro día, otro problema.

+0

Hola Kevin, tal vez usted podría cambiar su respuesta aceptada a la de abajo? Que es mucho más legible y el enfoque recomendado. La respuesta aceptada es solo una idea inflada de lo mismo de todos modos. Gracias. – SuperDuperApps

Respuesta

34

Thom Blake tiene razón sobre por qué se está manteniendo los mismos valores para la matriz Una opción para resolver esto es establecer el valor predeterminado en el inicializador

 var Criteria = Backbone.Model.extend({ 

      defaults: { 
       "status": "Normal", 
       "priority": "Normal" 
      }, 

      initialize: function(){ 
       if(!this.get('tags')){ 
       this.set({tags: new Array()}); 
       } 
      } 

     }); 
+8

+1 para mostrar la forma básica de manejar esto. –

+0

Gracias. Terminé siguiendo este enfoque y ahora tiene mucho sentido. Entonces, solo para asegurarse, los valores primitivos (estado, prioridad) no necesitan ser reiniciados como la matriz de etiquetas? ¿Correcto? – Kevin

+0

@Kevin Correcto. –

13

Cuando se define 'etiquetas' bajo '' por defecto, se crea una nueva matriz y establece lo que el valor predeterminado para esa clase. Luego, cuando crea una nueva instancia, tiene la misma referencia de matriz, que todavía tiene las cosas que introdujo en ella.

En lugar de establecer un valor predeterminado para las etiquetas, usted debería ser capaz de simplemente ponerlo en [] antes de usarlo por primera vez:

window.criteria = new Criteria() 
window.criteria.set({'tags', []}) //you can use new Array() if you want 
window.criteria.get('tags').push(5) 

window.criteria = new Criteria() 
console.log(window.criteria.get('tags')) //should be undefined 
window.criteria.set({'tags', []}) 
+2

Tuve el mismo problema recientemente al almacenar js Date objects como propiedades en Backbone.Model type classes.No estaba creando nuevos objetos de fecha simplemente cambiando una sola referencia. Por lo que respecta a la columna vertebral, la fecha nunca cambió y los eventos nunca se dispararon. La regla con la columna vertebral siempre trata las propiedades como valores inmutables. Si necesita cambiar una propiedad clone un nuevo valor que creará una nueva referencia y alterará la nueva referencia. – bradgonesurfing

+0

Seguí adelante y seguí el enfoque de inicialización a continuación. Buena explicación. ¡Gracias! – Kevin

70

"valores predeterminados" también puede ser una función.

var Criteria = Backbone.Model.extend({ 
    defaults: function() { 
     return { 
      "status": "Normal", 
      "priority": "Normal", 
      "tags": new Array() 
     } 
    } 
}); 

Esto crearía una nueva matriz cuando se crea una instancia de un nuevo Criterio. Ver: http://backbonejs.org/#Model-defaults

+0

¿Cuál es el propósito de la devolución? – Tjorriemorrie

+10

La clave es que la propiedad "defaults" es ahora una función en lugar de un objeto. El retorno denota un objeto que se devuelve de la función y se utiliza como el valor para "valores predeterminados". Como han explicado otros, si "valores predeterminados" es un objeto, cuando se crea un nuevo modelo, cada propiedad en "valores predeterminados" se copia en los atributos del objeto. Cuando las propiedades son Cadenas o Números, esto funciona como se esperaba, pero para Objetos y Arrays, esta copia se realiza por referencia. Para que cada instancia tenga su propia copia de los atributos predeterminados, debe usar una función. – btford

+7

Prefiero esta sintaxis, que la de la respuesta elegida – Pablote

3

Para ser claros, la última opción provista por Maksym H. no resolverá el problema. La propiedad predeterminada se proporciona suponiendo que todos los valores establecidos son inmutables. Sin embargo, una matriz es mutable, lo que significa que su valor se puede cambiar (por ejemplo, las etiquetas [0] = "hola" se pueden cambiar con las etiquetas [0] = "hola allí").

Al usar la respuesta de btford, está forzando a crear una nueva instancia de cualquier objeto/propiedad mutable en cada nueva instancia del modelo, por lo que nunca se comparte, porque el objeto se crea con una variable de ámbito funcional.

De manera similar, la respuesta de Derick Bailey es correcta, solo utiliza el método de inicialización en lugar del método predeterminado.

Cuestiones relacionadas