2011-08-02 16 views
12

¿Hay alguna manera de mapear desde/hacia un POCO y knockoutjs observable?mapeo de knockoutjs desde/hacia el objeto POCO

tengo una clase Nota:

public class Note 
{ 
    public int ID { get; set; } 
    public string Date { get; set; } 
    public string Content { get; set; } 
    public string Category { get; set; } 
    public string Background { get; set; } 
    public string Color { get; set; } 
} 

y este es mi javascript:

$(function() { 
    ko.applyBindings(new viewModel()); 
}); 

function note(date, content, category, color, background) { 
    this.date = date; 
    this.content = content; 
    this.category = category; 
    this.color = color; 
    this.background = background; 
} 

function viewModel() { 
    this.notes = ko.observableArray([]); 
    this.newNoteContent = ko.observable(); 

    this.save = function (note) { 
     $.ajax({ 
       url: '@Url.Action("AddNote")', 
       data: ko.toJSON({ nota: note }), 
       type: "post", 
       contentType: "json", 
       success: function(result) { } 

      }); 
    } 

    var self = this; 
    $.ajax({ 
     url: '@Url.Action("GetNotes")', 
     type: "get", 
     contentType: "json", 
     async: false, 
     success: function (data) { 
      var mappedNotes = $.map(data, function (item) { 
       return new note(item.Date, item.Content, item.Category, item.Color, item.Background); 
      }); 
      self.notes(mappedNotes); 
     } 
    }); 
} 

ignorar el hecho de que la función de ahorro no se utiliza (para simplificar el código aquí).

Entonces, cuando cargo la página llamo al servidor y recupero una lista de objetos Note y la asigno en javascript. Observe cómo ID no está asignado porque no lo necesito en mi opinión.

Hasta ahora, bien, veo las notas en la pantalla, pero ¿cómo puedo guardar las notas en el servidor?

Intenté convertir la nota (estoy guardando solo la nota nueva y no toda la colección) en JSON y la envío a mi controlador, pero no sé cómo acceder a la nota en el controlador. Intenté:

public string AddNote(string date, string content, string category, string background, string color) 
    { 
     // TODO 
    } 

pero no funciona. Quiero tener algo como:

public string AddNote(Note note) {} 

(Por cierto, ¿cuál es el mejor retorno de un método que acaba de guardar los datos en la base de datos vacío??)

Así que, ¿Cómo puedo hacer esto? Probé el plugin knockout.mapping pero es bastante confuso y no lo entiendo para mí.

Gracias.

Respuesta

24

El archivador de modelo de ASP.NET MVC buscará propiedades que distingan entre mayúsculas y minúsculas. Debe volver a pasar su objeto JSON al servidor con los nombres de propiedad que coincidan con su objeto poco.

por lo general lo hago 1 de 2 cosas:

  1. Haz de mi nombres de las propiedades objeto de JavaScript de capital (de esa manera en JS, sé que este objeto en algún momento ser un DTO para el servidor)

    function Note(date, content, category, color, background) { 
        this.Date = date; 
        this.Content = content; 
        this.Category = category; 
        this.Color = color; 
        this.Background = background; 
    }; 
    
  2. En mi llamada AJAX i se acaba de crear un objeto anónimo que pasar de nuevo al servidor (tenga en cuenta que esto no requiere ko.toJSON):

    $.ajax({ 
         url: '@Url.Action("AddNote")', 
         data: JSON.stringify({ note: { 
           Date: note.date, 
           Content: note.content, 
           Category: note.category, 
           Color: note.color, 
           Background: note.background 
           } 
          }), 
         type: "post", 
         contentType: "application/json; charset=utf-8", 
         success: function(result) { } 
    }); 
    

    (tenga en cuenta los diferentes parámetros contentType así)

Usted tendrá que hacer su ActionMethod tomar en un (Note note) y no sólo el conjunto de parámetros.

Además, debido a que los encuadernadores observan los valores publicados de dos maneras diferentes.He tenido la suerte publicar objetos JSON con cabo especificando el nombre del parámetro ActionMethod: en lugar de:

{ note: { 
     Date: note.date, 
     Content: note.content, 
     Category: note.category, 
     Color: note.color, 
     Background: note.background 
     } 
    } 

acaba de hacer:

{ 
     Date: note.date, 
     Content: note.content, 
     Category: note.category, 
     Color: note.color, 
     Background: note.background 
    } 

(pero esto puede ser arriesgado con matrices de unión a colecciones y compleja tipos ... etc)

En cuanto a la 'mejor' firma para un retorno en un método que realiza una llamada a base de datos, generalmente preferimos ver booleanos, pero eso también depende de sus necesidades. Obviamente, si se trata de datos triviales, el vacío estará bien, pero si es un poco más crítico, es posible que desee retransmitir un booleano (como mínimo) para que su cliente sepa que podría tener que volver a intentarlo (especialmente si hay una excepción de concurrencia) .

Si realmente necesita que su cliente sepa lo que sucedió en la base de datos, puede incursionar en el mundo de custom error handling y exception catching.

Además, si necesita mostrar información muy específica a su usuario dependiendo de una confirmación de la base de datos exitosa/fallida, puede buscar crear custom ActionResults que redirija a ciertas vistas en función de lo que sucedió en la transacción de la base de datos.

Por último, en cuanto a conseguir los datos desde el servidor y el uso de Knockout ...

  1. de nuevo el plugin mapeo funcionará si sus nombres de propiedades son el mismo caso o se crea una correlación ligeramente más explícito
  2. Mi propio truco con mis objetos JS está a continuación. La función de inicialización es algo que creé que debería ser reutilizable en todos sus objetos, ya que solo dice "si los nombres de las propiedades coinciden (después de haber sido minúsculas), configúrelos llamando a la función (compatible con knockout) o simplemente asigne el valor .:

    .
    function Note(values){ //values are what just came back from the server 
        this.date; 
        this.content; 
        this.category; 
        this.color; 
        this.background; 
    
        initialize(values); //call the prototyped function at the bottom of the constructor 
    }; 
    
    Note.prototype.initialize = function(values){ 
        var entity = this; //so we don't get confused 
         var prop = ''; 
         if (values) { 
          for (prop in values) { 
           if (values.hasOwnProperty(prop.toLowerCase()) && entity.hasOwnProperty(prop.toLowerCase())) { 
            //the setter should have the same name as the property on the values object 
    
            if (typeof (entity[prop]) === 'function') { 
             entity[prop](values[prop]); // we are assuming that the setter only takes one param like a Knockout observable() 
            } else {// if its not a function, then we will just set the value and overwrite whatever it was previously 
             entity[prop] = values[prop]; 
            } 
           } 
          } 
         } 
    }; 
    
+1

impresionante respuesta épica funciona perfecto, pero: ¿me puede explicar el último párrafo Y .. ¿Cómo cargar desde el servidor de asignación manual que estoy haciendo aquí o tengo que probar el plugin de mapeo??? –

+1

Sí, +1 para la gran respuesta. Está muy claro con excelentes ejemplos. –

+0

Guau - excelente trabajo. –