martes, 19 de marzo de 2013

Problemas con save de Backbone.Model

Últimamente he estado cacharreando un poco con Backbone.js y la posibilidad de hacer una aplicación web multiplataforma para móvil. Pero usando Backbone y los servicios Rest me he encontrado con un problema. Y es que cuando iba a hacer una actualización de un modelo, se intentaba realizar un insert en vez de un update. En post posteriores puede que indique como hacer una pequeña aplicación con backbone, pero por ahora nos centraremos en el error.

Tenemos el siguiente modelo, donde parseamos los valores al modelo a través de JSON. Donde el identificador es ident.
define(["Backbone"], function(Backbone){
    var BookModel = Backbone.Model.extend({
     parse: function(item) {
            return {
                ident: item.ident,
                nombre: item.nombre,
                autor: item.autor
            };
        }
    });
    return BookModel;
});
En nuestra aplicación mostramos una lista de libros, y cada uno de ellos tiene asociado un botón 'Detalle' que te permite ir a una página de detalle del libro. La función que permite pintar la vista del detalle del libro es la siguiente:
render: function() {
     console.log('DetailBookView - render');
     var self = this;
     //Obtenemos el identificador del libro a mostrar a traves de parametros de la vista
     var idBook = self.options.idBook; 
     //creamos un objeto del tipo Libro Collection
     var ltBooks = new BookCollection(); 
     //Reseteamos la coleccion  con los valores que antes hemos almacenado en memoria para evitar hacer otra llamada REST
     ltBooks.reset(JSON.parse(sessionStorage.getItem("findBooks")));
     //hacemos una busqueda del objeto del cual vamos a mostrar el detalle
        this.book = _.find(ltBooks.models,function(i) {return i.attributes.ident==idBook;});
        console.log('BOOK ID INTERNAL: '+this.book.id);
     //pintamos el detalle en la pagina html (indicada anteriormente)
        $(self.el).html(self.template({
         book: this.book
        }));
        return self;
    }
Tras pintar el detalle, y modificar el formulario accedemos a la función guardar:
saveBook : function(){
     console.log('BOOK ID INTERNAL: '+this.book.id);
     //Almacenamos los valores de los atributos que han tenido cambios
     var atributos = {
  nombre : $("#nombre").val(),
  autor : $("#autor").val()
     };
     //realizamos el save, y backbone se encargara de hacer la llamada REST
     var retorno = this.book.save(atributos, {success :function(model, response){
         console.log("success save ");
        }, error: function(model, response){
         console.log("error save");
         console.log("model "+JSON.stringify(model));
         console.log("response "+JSON.stringify(response));
        }});
     history.back();
    }
Al realizar la llamada al metodo 'save'. Backbone se debe de encargar de realizar una llamada tipo PUT, que es la indicada para realizar modificaciones de elementos. El problema en nuestro caso es que en vez de realizar la llamada PUT, realiza una POST (Esto lo podemos comprobar facilmente con el firebug). Es decir que Backbone no entiende que sea una modificación de un elemento existente si no que se trata de la creación de un nuevo elemento. Se nos puede dar una pista del mal funcionamiento debido a que cada vez que imprimimos por pantalla el ID interno que maneja Backbone, este es undefined.


Mirando la documentación de Backbone, este mismo indica aquí, que siempre considerará a un modelo como nuevo si no tiene ningún id asociado. Y aunque en principio hay una diferencia entre el atributo id y el identificador propio del modelo, parece que la raiz del problema se encuentra ahí.

¿Cual es la solución?, pues en nuestro caso es bastante sencilla, solo tenemos que indicar que el atributo identificador se pase a llamar 'id' y no 'ident' como esta actualmente en el modelo (Tener en cuenta dicho detalle para modificar el atributo tanto en los ficheros javascript como por parte del servicio REST).
define(["Backbone"], function(Backbone){
    var BookModel = Backbone.Model.extend({
     parse: function(item) {
            return {
                id: item.ident,
                nombre: item.nombre,
                autor: item.autor
            };
        }
    });
    return BookModel;
});
Una vez realizado este cambio, cuando hagamos la operación de modificar, Backbone realizará una llamada PUT y en las trazas podremos observar como el atributo interno ID ya no es undefined.


No hay comentarios:

Publicar un comentario