Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

Send correct 'at' when firing 'add' event #3039

Merged
merged 1 commit into from

4 participants

@fastest963

If you add an array of models to a collection and specify at in options when the add event is fired on the collection for each model the at sent in the options is incorrect for that specific model.

The extend is only necessary when the at is not undefined, otherwise we're not modifying it.

Downside of the extend is if someone was wrongly modifying options object in an add event and expecting to get those changes in other events, that is now broken. I had considered changing the original options but that generally doesn't seem to be what any other functions are doing.

@fastest963

I didn't want to include the overhead of extend in each iteration so I opted against doing:

addOpts = at != null && i > 0 ? _.extend({}, options, {at: at + i}) : options;

inside the loop. Let me know if anyone prefers that instead.

@caseywebdev caseywebdev added the change label
@caseywebdev
Collaborator

Perhaps rather than changing at, you should add an index property. That would ensure this change isn't breaking. We used to always pass index in the event, but indexOf was too expensive for every model. With the at option we get index for free, so I don't see the harm in adding it.

@fastest963

Won't it seem a little weird if there's a random index property that gets returned only when the at option is passed in? I was favoring updating at only because you passed it in so it would make sense to get it back in the event. The only problem being that the event fires for each model so the at is wrong.

I'm fine with changing it just thought I'd bring that up. Let me know what you think.

@caseywebdev
Collaborator

Personally, I think it would be weirder to change at than to add an index property. An index would give you the info you're looking for, as well as retain the value of the original at.

@fastest963

That's correct, but you wouldn't always get an index property. Are we okay with that? Would we want to default at to the collection.length by default then we could provide index in the case where they don't pass that in?

I also just realized that sort is fired BEFORE firing off these events so I should really only provide the index prop when sort is not used, otherwise the data would be incorrect :/

@caseywebdev
Collaborator

You can't sort when at is set, see https://github.com/jashkenas/backbone/blob/f814b85db65cae8715c67d33606b1352d1373f7f/backbone.js#L701, so that's not an issue.

I don't think providing index when at isn't set is a good idea because it won't be correct. We'll need to get some more input on this, but I think index only being a byproduct of using the at option is not unreasonable.

@fastest963

Ah missed that, thanks for the clarification. I'll update the PR to pass along index for now and monitor the conversation.

@fastest963

Also added a test for index being undefined when at is not specified.

@fastest963

Any updates on this?

backbone.js
@@ -771,8 +771,10 @@
// Unless silenced, it's time to fire all appropriate add/sort events.
if (!options.silent) {
+ var addOpts = at != null ? _.extend({}, options) : options;
@akre54 Collaborator
akre54 added a note

conditionally cloneing seems weird. Just clone and be done with it.

If we don't need to clone because we're not modifying anything then why do it? The majority of the time they're not going to passing at so we might as well optimize for that case and not do any unnecessary cloning.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@fastest963

I'd like to see this get merged. It's pretty small and all the tests still pass. I commented on @akre54's comment about conditionally cloneing but I don't care too much either way. Let me know if you just want to always clone or if you're fine with the small performance gain vs weirdness.

@akre54
Collaborator

@caseywebdev how do you feel about this one?

@caseywebdev
Collaborator

While it saves an indexOf for handlers needing the position after an add, it worries me a little that index will only be available sometimes (when at is set). Seems kinda gross that an event handler would have to var i = options.index == null ? options.index : this.indexOf(model); to make sure it always has an index. That's my only concern.

@akre54
Collaborator

Yeah I see your point (though it'd only be var i = options.index || this.indexOf(model);, just test for the falsey value!). I'm not sure it harms anything by adding it here; your old indexOf checks would still work.

Is there any reason we couldn't always just send the correct index? Like index: this.length + i if at isn't set?

@fastest963

You can't just check for the falsy value because the index could be 0. We also can't just assume the index is at the end because they might have a comparator which sorts them and so it wouldn't always be at the end.

@akre54
Collaborator

Urg. Do we agree it's worth the potential extra check in your handlers? (You'd be doing the indexOf check anyways before.) Or is it ugly enough that we should prefer you run indexOf each time yourself?

@fastest963

I'd prefer to have the index there and optionally run indexOf than ALWAYS have to run indexOf. Especially if someone adds 500 songs to their queue on Grooveshark ;)

@akre54
Collaborator

Alright, I'm with it. Want to rebase and I'll merge?

@fastest963

Updated. Thanks!

@akre54 akre54 merged commit b966584 into jashkenas:master
@akre54 akre54 referenced this pull request
Merged

Draft changelog for Backbone 1.2.0 #3285

3 of 3 tasks complete
@jashkenas jashkenas added fixed bug and removed change labels
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Sep 11, 2014
  1. @fastest963
This page is out of date. Refresh to see the latest.
Showing with 19 additions and 1 deletion.
  1. +3 −1 backbone.js
  2. +16 −0 test/collection.js
View
4 backbone.js
@@ -760,8 +760,10 @@
// Unless silenced, it's time to fire all appropriate add/sort events.
if (!options.silent) {
+ var addOpts = at != null ? _.clone(options) : options;
for (var i = 0, length = toAdd.length; i < length; i++) {
- (model = toAdd[i]).trigger('add', model, this, options);
+ if (at != null) addOpts.index = at + i;
+ (model = toAdd[i]).trigger('add', model, this, addOpts);
}
if (sort || (order && order.length)) this.trigger('sort', this, options);
}
View
16 test/collection.js
@@ -1426,4 +1426,20 @@
equal(collection.at(1), collection.get('b-1'));
});
+ test("#3039: adding at index fires with correct at", 3, function() {
+ var col = new Backbone.Collection([{at: 0}, {at: 4}]);
+ col.on('add', function(model, col, options) {
+ equal(model.get('at'), options.index);
+ });
+ col.add([{at: 1}, {at: 2}, {at: 3}], {at: 1});
+ });
+
+ test("#3039: index is not sent when at is not specified", 2, function() {
+ var col = new Backbone.Collection([{at: 0}]);
+ col.on('add', function(model, col, options) {
+ equal(undefined, options.index);
+ });
+ col.add([{at: 1}, {at: 2}]);
+ });
+
})();
Something went wrong with that request. Please try again.