2013-02-12 9 views
33

Ich habe ein Problem mit Knockout-JS und Anzeigen eines beobachtbaren Array als Liste; Wenn ein Element hinzugefügt wird, während die beforeRemove-Animation ausgeführt wird, wird das entfernte Element an den unteren Rand der Liste verschoben, anstatt an seiner Position zu verbleiben, bis die Animation beendet und das Element entfernt wurde.Knockout js - beforeRemove Animation beim Hinzufügen von Elementen zu beobachtbaren Array

ist hier ein jsfiddle das Problem ein wenig besser zu erklären: http://jsfiddle.net/bPP5Q/8/

Wer weiß, wie ich dieses Problem lösen könnte?

javascript:

jQuery(function ($) { 
    var ViewModel = function (data) { 
     var self = this; 
     self.data = ko.observableArray(data); 
     self.removeLine = function (elem) { 
      if (elem.nodeType === 1) $(elem).fadeOut(3000, function() { 
       $(elem).remove(); 
      }); 
     } 

     self.addLine = function (elem) { 
      if (elem.nodeType === 1) 
       $(elem).hide().fadeIn(3000); 
     } 

     self.removeItem = function() { 
      self.data.remove(function(item) { return item.test && item.test == 2; }); 
     } 

     self.addItem = function() { 
      self.data.splice(1, 0, { test: 9 }); 
     } 

     self.addremove = function() { 
      self.removeItem(); 
      var id = setInterval(function() { 
       self.addItem(); 
       clearInterval(id); 
      },1000); 
     } 
    } 

    var vm = new ViewModel([{ test: 9 }, { test: 2 }, { test: 1 }, { test: 1 }, { test: 1 }, { test: 1 }, { test: 1 }]); 

    ko.applyBindings(vm); 
}); 

HTML:

<button id="button" data-bind="click: addremove">Click</button> 
<table id="grid"> 
    <tbody data-bind='template: { foreach: data, afterAdd: addLine, beforeRemove: removeLine }'> 
     <tr> 
      <td data-bind="text: test"></td> 
      <td> 

      </td> 
     </tr> 
    </tbody> 
</table> 
+9

Dies funktioniert ordnungsgemäß in 2.1. In 2.2 haben wir die Funktionalität hinzugefügt, um zu erkennen, dass ein Objekt verschoben wurde, anstatt es zu entfernen und wieder hinzuzufügen. Es sieht so aus, als ob dies ein Problem für den Fall verursacht, mit dem Sie arbeiten. Ich werde es ein bisschen genauer betrachten. –

+1

Ich habe auf 2.1 heruntergestuft und es funktioniert super, aber mit ein paar anderen Problemen, die es in 2.2 nicht gab, also wenn das auf andere Weise gelöst werden könnte, wäre das großartig! – Cyriuz

+1

Irgendwelche Updates zu diesem Thema? Ich habe das gleiche Problem mit Knockout 3.0, Sie können mein Beispiel hier sehen: http://jsfiddle.net/davertron/3ucVg/1/ – davertron

Antwort

-1

können Sie die beforeAdd Bindung verwenden Sie das Element zu entfernen, da das hinzugefügte Element angezeigt

** EDIT **

Hier ist die Geige: http://jsfiddle.net/janarde/U6Um5/5/

<button id="button" data-bind="click: addremove">Click</button> 
<table id="grid"> 
    <tbody data-bind='template: { foreach: data, afterAdd: addLine, beforeAdd: removeLine }'> 
     <tr> 
      <td data-bind="text: test"></td> 
      <td> 

      </td> 
     </tr> 
    </tbody> 
</table> 
+0

Ich denke nicht, dass dies die Frage beantwortet. Cyriuz möchte wissen, wie das entfernte Objekt an seinem Platz angezeigt werden kann, während es die Ausgangsanimation spielt. – aaaidan

-1

Ihr Beispiel aktualisiert, dass ich denke, wie erwartet ist aumeti Zeit nur das Einzelteil zu 1 Sekunde bis 3

self.removeItem(); 
var id = setInterval(function() { 
    self.addItem(); 
    clearInterval(id); 
},3000); 

http://jsfiddle.net/bPP5Q/15/

+0

Diese Antwort löst das Problem nicht. Der Punkt des Codes, den Sie geändert haben, bestand darin, ein Szenario zu demonstrieren, in dem das Problem auftritt. Ihre Änderung hat das Szenario so geändert, dass das Problem nicht aufgetreten ist. – aaaidan

0

Als Abhilfe kann hinzufügen, wenn möglich, wollen Sie tun alle Ihre Änderungen an dem beobachtbaren Array in einer einzigen Operation. Anstatt beispielsweise einzelne Elemente hinzuzufügen und/oder zu entfernen, möchten Sie das gesamte beobachtbare Array auf einmal aktualisieren, indem Sie beispielsweise ko.utils.arrayFilter verwenden, um eine gefilterte Version des ObservableArray auf sich selbst zurück zuweisen.

self.data(ko.utils.arrayFilter(self.data(), function(x) { 
    return shouldKeepElement(x); 
})); 

Wenn dies nicht möglich ist (wie Sie wahrscheinlich der Fall ist), müssen Sie das Hinzufügen des Elements in das Array verzögern, bis der Entfernen-Animations abgeschlossen ist. Dies wird wahrscheinlich ein ausgeklügeltes System von "Hebeln und Riemenscheiben" beinhalten, so dass der resultierende Code ziemlich unerwünscht sein wird. Die Benutzererfahrung wird möglicherweise auch leiden, da "schnelle" Benutzeraktionen in die Warteschlange eingereiht oder nicht zugelassen werden müssen.

Offensichtlich ist eine ideale Lösung, dass dies in der nächsten Version von Knockout behandelt wird. Bis dahin können Sie entweder auf 2.1 "downgraden" oder a small patch zu der bestehenden KO-Basis machen. Ich kann nicht für diesen Patch bürgen, und es kann unerwünschte Folgen haben, aber es sollte eine gute Lücke für dieses Problem sein.

0

Das Problem ist mit dem Timing von addItem - Sie müssen warten, bis die Animation endet, bevor Sie mit dem Hinzufügen des neuen Elements beginnen.

Arbeits Geige: http://jsfiddle.net/bPP5Q/26/

jQuery(function ($) { 
// this runs after the window loads 
var ViewModel = function (data) { 
    var self = this; 
    self.data = ko.observableArray(data); 
    self.removeLine = function (elem) { 
     if (elem.nodeType === 1) $(elem).fadeOut(3000, function() { 
      $(elem).remove(); 
      self.addItem(); 
     }); 
    } 

    self.addLine = function (elem) { 
     if (elem.nodeType === 1) 
      $(elem).hide().fadeIn(3000); 
    } 

    self.removeItem = function() { 
     self.data.remove(function(item) { return item.test && item.test == 2; }); 
    } 

    self.addItem = function() { 
     self.data.splice(1, 0, { test: 9 }); 
    } 

    self.addremove = function() { 
     self.removeItem(); 
    } 
} 


var vm = new ViewModel([{ 
    test: 9 
}, { 
    test: 2 
}, { 
    test: 1 
}, { 
    test: 1 
}, { 
    test: 1 
}, { 
    test: 1 
}, { 
    test: 1 
}]); 

ko.applyBindings(vm); 

}); 
1

Ihre Funktion "addItem" nach 1 Sekunde (setInterval 1000 ms) ausgeführt wird, so dass die "self.data" enthält ein neues Element, während die Animation fadeout nicht abgeschlossen ist (das ist benötigen 3000 ms). Das ist dein Problem.

Um dieses Problem zu beheben, müssen Sie das gleiche Intervall für "addItem" als "Ausblendung" machen, für Ihr Beispiel ist es 3000. Der Code wird:

jQuery(function ($) { 
    var ViewModel = function (data) { 
     var self = this; 
     self.data = ko.observableArray(data); 
     self.removeLine = function (elem) { 
      if (elem.nodeType === 1) $(elem).fadeOut(3000, function() { 
       $(elem).remove(); 
      }); 
     } 

     self.addLine = function (elem) { 
      if (elem.nodeType === 1) 
       $(elem).hide().fadeIn(3000); 
     } 

     self.removeItem = function() { 
      self.data.remove(function(item) { return item.test && item.test == 2; }); 
     } 

     self.addItem = function() { 
      self.data.splice(1, 0, { test: 9 }); 
     } 

     self.addremove = function() { 
      self.removeItem(); 
      var id = setInterval(function() { 
       self.addItem(); 
       clearInterval(id); 
      },3000); // the same interval as the "fadeout" 
     } 
    } 

    var vm = new ViewModel([{ test: 9 }, { test: 2 }, { test: 1 }, { test: 1 }, { test: 1 }, { test: 1 }, { test: 1 }]); 

    ko.applyBindings(vm); 
}); 
0

Mixing 'Splice' und 'array.remove' Knockout zu unerwartetem Verhalten führen kann, würden Sie es besser konsequent Handhabung, beispielsweise nur verwenden 'Splice' wie folgt:

   self.addItem = function() { 
        var index = $.map(self.data(), function (item, index) { 
         if (item.test && item.test == 2) { 
          return index; 
         } 
        })[0]; 
        self.data.splice(index + 1, 0, { test: 9 }); 
        self.data.splice(index, 1); 
       } 

       self.addremove = function() { 
        //self.removeItem(); 
        var id = setInterval(function() { 
         self.addItem(); 
         clearInterval(id); 
        }, 1000); 
       } 
0

Das hier beschriebene Problem wurde in 3.4.0 behoben. siehe https://github.com/knockout/knockout/issues/790

+0

Da diese Frage viele Ansichten und ziemlich viele Stimmen angezogen hat, wäre diese Antwort vielleicht besser als ein Edit für die Frage, um zukünftige Leute wissen zu lassen, dass es jetzt nicht anwendbar ist. Ansonsten sollte es ein Kommentar zur ursprünglichen Frage sein. Wenn Sie genug Reputation haben, können Sie Fragen kommentieren. – Mike

Verwandte Themen