unmuse
unmuse

Reputation: 687

Ember: Iterating through childViews

I need to iterate through a collection of child views (Which are UI Widgets) and tell them to remove themselves from the app. I have ensured that all the childViews are present in the collection, however this code will only call removeIfSelected() on a few of the childViews/widgets at a time:

 widgets = container.get('childViews')
 widget.removeIfSelected() for widget in widgets

For example, if there are 3 widgets, the first time this is run (via delete button) 2 widgets are removed. I have to click the button again to remove the last widget.

If there are 2 it will remove one widget. If there are 4 or more, all but 2 widgets are removed, and I have to click delete 2 more times to remove the last two.

My initial solution was to run the loop 3 times which guarantees all widgets are removed, however this did not pass code review and I have to find a real solution. I am not sure if this is a coffeescript problem or an Ember.js problem. How can I ensure that the loop executes completely?

Upvotes: 0

Views: 168

Answers (1)

mu is too short
mu is too short

Reputation: 434615

Sounds like widget.removeIfSelected() is altering the widgets array behind your back so as soon as remove something, the lengths and indexes in your for loop get all screwed up. Consider this loop:

a = [ 0, 1, 2, 3, 4, 5 ]
for e, i in a
    console.log(i) if(i % 2 == 0)

That will obviously produce 0, 2, 4 in the console. However, this:

a = [ 0, 1, 2, 3, 4, 5 ]
for e, i in a
    a.splice(i, 1) if(i % 2 == 0)
console.log(a)

leaves you with [1, 2, 4, 5] in a because a is being modified while it is being looped over.

There are two common solutions to this:

  1. Iterate backwards so that changes don't affect anything that's coming up, they only affect things that you've already been through.
  2. Make a copy of what you're iterating over so that you're iterating over one thing and changing something else.

The first would look like this:

for i in [widgets.length - 1 .. 0] by -1
    widget.removeIfSelected()

The second would look like this:

widgets = clone(container.get('childViews'))
widget.removeIfSelected() for widget in widgets

Where clone is whatever you have available to make (shallow) copies of an array. If you have Underscore kicking around then you could use _.clone:

widgets = _(container.get('childViews')).clone()

You could also do it by hand:

clone = (a) -> e for e in a

Upvotes: 2

Related Questions