web-dev-qa-db-fra.com

KnockoutJS - Tableau observable d'objets observables

Je voudrais afficher une liste modifiable d'éléments, dont chaque élément est modifiable (un peu comme une grille modifiable, en quelque sorte). J'utilise KnockoutJS. Je ne peux pas utiliser simplement un tableau observable simple parce que, comme l'indique la documentation "Un tableau observable suit quels objets sont dans le tableau, pas l'état de ces objets"

J'ai donc créé un tableau observable d'objets observables (en utilisant utils.arrayMap) et les ai liés à la vue. Cependant, le problème est que si je modifie les données à l'écran, les modifications de saisie de données que l'utilisateur effectue à l'écran ne semblent pas prendre effet. Voir http://jsfiddle.net/AndyThomas/E7xPM/

Qu'est-ce que je fais mal?

<script src="http://cdnjs.cloudflare.com/ajax/libs/knockout/2.0.0/knockout-min.js" type="text/javascript"></script>

<table>
   <tbody data-bind="template: { name:'productListJavascriptTemplate', foreach: products}">
   </tbody>
</table>


<script type="text/html" id="productListJavascriptTemplate">
<tr>
    <td>Name: <input data-bind="value: Name"/></td>
    <td>Name: <span data-bind="text: Name"/></td>
    <td><select data-bind="options: this.viewModel.categories, 
        optionsText: 'Name', optionsValue: 'Id', value: CategoryId,
        optionsCaption: 'Please select...'"></select></td>
    <td>CategoryId: <input data-bind="value: CategoryId"/></td>

</tr>

</script>​

var categoryList= [
{
   Name: "Electronics",
   Id: "1"},
{
   Name: "Groceries",
   Id: "2"}
];

var initialData= [
{
   Name: "Television",
   CategoryId: "1"},
{
   Name: "Melon",
   CategoryId: "2"}
];

var viewModel = {
    products: ko.observableArray(
        ko.utils.arrayMap(initialData, function(product) { 
                                return ko.observable(product); 
        })),
    categories: ko.observableArray(categoryList)       
};


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

});

La

29
Andy Thomas

ko.utils.arrayMap ne mappe pas les propriétés de votre viewmodel comme observables, et c'est pourquoi vous ne les voyez pas mises à jour dynamiquement.

Si vous définissez votre CategoryId comme observable, vous le verrez se mettre à jour comme prévu:

var initialData = [
    {
        Name: "Television",
        CategoryId: ko.observable("1")
    },
    {
        Name: "Melon",
        CategoryId: ko.observable("2")
    }
];

Voir ce jsfiddle mis à jour: http://jsfiddle.net/tuando/E7xPM/5/

19
Tuan

Pour donner suite à la réponse de Tuan, je devais remplir mes objets en fonction des données renvoyées par une méthode serveur à partir d'un contrôleur ASP.Net MVC, où la liste des produits est contenue dans le modèle de la vue et la liste des catégories pour la liste déroulante se trouve dans le ViewBag. J'ai utilisé le code suivant (voir aussi http://www.knockmeout.net/2011/04/utility-functions-in-knockoutjs.html ):

var initialData = @Html.Raw( new JavaScriptSerializer().Serialize(Model));
var categoryList = @Html.Raw( new JavaScriptSerializer().Serialize(ViewBag.CategoryList));

var ObservableProduct = function(name, description, categoryId) {         
    this.Name = ko.observable(name);         
    this.Description = ko.observable(description);
    this.CategoryId = ko.observable(categoryId);
};  

var viewModel = {
    products: ko.observableArray(ko.utils.arrayMap(initialData, function(product) { 
            return new ObservableProduct(product.Name, product.Description, product.CategoryId); 
        })),
    categories: ko.observableArray(categoryList)       
};

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

});

Merci, Tuan!

16
Andy Thomas

J'utilise des observables calculés inscriptibles qui sont initialisés dans l'appel à ko.utils.arrayMap

Cela peut être exagéré dans votre cas, mais cela pourrait aider quelqu'un d'autre. Voir ceci exemple jsFiddle

  // Writeable computed observables
  function Customer(id, firstName, lastName, preferred) {
     var self = this;
     self.id = id;
     self.firstName = firstName;
     self.lastName = lastName;
     // Non-Writeable computed observable
     self.fullName = ko.computed(function() {
        var fn = self.firstName;
        var ln = self.lastName;
        return ln ?  fn + ' ' + ln : fn;
     }, self);
     self.preferred = ko.observable(preferred);
     // Writeable computed observable
     self.isPreferred = ko.computed({
        read: function() {
           var preferredStr = self.preferred() || '';
           var isPreferredComputed = preferredStr.toUpperCase();
           return (isPreferredComputed === 'Y') ?  true : false;
        },
        write: function(value) {
           self.preferred( (!value) ? '' : (value ? 'Y' : ''));            
        },
        owner: self        
    });    
 }
  var mappedData = ko.utils.arrayMap(dataFromServer, function(customer) {
      return new Customer(customer.id, customer.firstName, customer.lastName, customer.preferred);
  });
1
mitaka