0

I'm currently experiencing very unusual behavior when using the each method of a Backbone collection. In my app, I allow users to edit existing models, which includes editing and adding a number of time constraints to that model. The model and its time constraints are linked with Backbone Relational. Every time the user clicks to add a new time constraint, I add a TimeConstraint model to the relationship without saving it. If a user then decides to cancel the edit, all new/unsaved TimeConstraints should be removed from the relationship. However, when I'm looping over my collection, it doesn't seem to perform the final iteration.

I constructed a jsfiddle to demonstrate this behavior here: http://jsfiddle.net/richardstokes/ZzFgZ/9/

The steps to follow are:

  1. Click "Edit Policy"
  2. Add 2 or more new time constraints using the "New Time Constraint" button
  3. Cancel the edits by clicking "Cancel Edits"

You'll notice the console prints the starting length of the time_constraints collection and the finishing length, as well as intermediate lengths after removing unsaved models. It always seems to stop short and leave one item in the collection, even though they're all new/unsaved.

I would greatly appreciate if someone would help me with this problem, it's had me stuck literally all day.

Richard Stokes
  • 3,532
  • 7
  • 41
  • 57

1 Answers1

1

You're removing items as the collection of timeConstraints is being iterated, which might be causing undefined behavior. Try first getting the list of new timeConstraints, then passing them as an array to remove():

var toRemove = this.model.get('time_constraints').filter(function (timeConstraint) {
    return timeConstraint.isNew();
});
this.model.get('time_constraints').remove(toRemove);
Yossarian21
  • 149
  • 1
  • 4
  • Just as a matter of interest, why would removing the items as the collection is being iterated be causing this undefined behaviour? Surely if the length of the collection at the end of an iteration is still greater than zero, the iteration should continue? Just want to understand the problem more fully. – Richard Stokes Nov 02 '12 at 11:30
  • I don't have a specific answer. According to the documentation, the forEach implementation loops over the elements that are present at the beginning of the call to forEach (see https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/forEach) and the underscore source code. The mozilla doc does state that changing the contents of the array while iterating means that some elements might not be visited. In general, this is not a safe thing to do (and some languages like .NET will proactively raise an error). – Yossarian21 Nov 02 '12 at 15:55
  • Ok thanks for the insight. Your solution worked perfectly for me. Will be more careful not to do this in future. – Richard Stokes Nov 03 '12 at 10:21