web-dev-qa-db-fra.com

Abonnez-vous au tableau observable pour une entrée nouvelle ou supprimée uniquement

Alors oui, je peux m'abonner à un tableau observable:

vm.myArray = ko.observableArray();
vm.myArray.subscribe(function(newVal){...});

Le problème est que le newVal transmis à la fonction est l'ensemble du tableau. Y at-il de toute façon je ne peux obtenir que la partie delta? Dites l'élément ajouté ou supprimé?

74
Gelin Luo

Depuis KnockoutJS 3.0, il existe un option d'abonnement arrayChange sur ko.observableArray.

var myArray = ko.observableArray(["Alpha", "Beta", "Gamma"]);

myArray.subscribe(function(changes) {

    // For this example, we'll just print out the change info
    console.log(changes);

}, null, "arrayChange");

myArray.Push("newitem!");

Dans le rappel ci-dessus, l'argument change sera un tableau d'objets change comme celui-ci:

[ 
   { 
      index: 3, 
      status: 'added', 
      value: 'newitem!' 
   }
]

Pour votre problème spécifique , vous souhaitez être averti des éléments nouveaux ou supprimés . Pour implémenter cela en utilisant Knockout 3, cela ressemblerait à ceci:

myArray.subscribe(function(changes) {

    changes.forEach(function(change) {
        if (change.status === 'added' || change.status === 'deleted') {
            console.log("Added or removed! The added/removed element is:", change.value);
        }
    });

}, null, "arrayChange");
122

Comme je ne pouvais trouver aucune information à ce sujet ailleurs, je vais ajouter une réponse sur la façon de l'utiliser avec TypeScript.

La clé ici était d'utiliser l'interface KnockoutArrayChange en tant que TEvent pour subscribe. Si vous ne le faites pas, il essaiera d'utiliser l'autre abonnement (non générique) et se plaindra du statut, de l'index et de la valeur inexistants.

class ZoneDefinition {
    Name: KnockoutObservable<String>;
}

class DefinitionContainer
{
    ZoneDefinitions: KnockoutObservableArray<ZoneDefinition>;
    constructor(zoneDefinitions?: ZoneDefinition[]){
        this.ZoneDefinitions = ko.observableArray(zoneDefinitions);
        // you'll get an error if you don't use the generic version of subscribe
        // and you need to use the KnockoutArrayChange<T> interface as T
        this.ZoneDefinitions.subscribe<KnockoutArrayChange<ZoneDefinition>[]>(function (changes) {
            changes.forEach(function (change) {
                if (change.status === 'added') {
                    // do something with the added value
                    // can use change.value to get the added item
                    // or change.index to get the index of where it was added
                } else if (change.status === 'deleted') {
                    // do something with the deleted value
                    // can use change.value to get the deleted item
                    // or change.index to get the index of where it was before deletion
                }
            });
        }, null, "arrayChange");
}
7
MPavlak

Afin de détecter uniquement les événements Push() et remove(), et non les éléments en mouvement, j'ai placé un wrapper autour de ces fonctions de tableau observables.

var trackPush = function(array) {
    var Push = array.Push;
    return function() {
        console.log(arguments[0]);
        Push.apply(this,arguments);
    }
}
var list = ko.observableArray();
list.Push = trackPush(list);

La fonction Push d'origine est stockée dans une fermeture, puis est superposée avec un wrapper qui me permet de faire tout ce que je veux avec l'élément poussé avant ou après, il est poussé sur le tableau.

Modèle similaire pour remove().

1
Hal Noyes

J'utilise une approche similaire mais différente. Gardez une trace si un élément a été instrumenté dans l'élément lui-même:

myArray.subscribe(function(array){
  $.each(array, function(id, el) {
    if (!el.instrumented) {
      el.instrumented = true;
      el.displayName = ko.computed(function(){
        var fn = $.trim(el.firstName()), ln = $.trim(el.lastName());
        if (fn || ln) {
          return fn ? (fn + (ln ? " " + ln : "")) : ln;
        } else {
          return el.email();
        }
      })
    }
  });
})

Mais c'est vraiment fastidieux et le motif répété à travers mon code

0
Gelin Luo