Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Success/Error callbacks not called on Model.destroy() if model isNew(). #1365

Closed
edwardmsmith opened this issue May 31, 2012 · 8 comments
Closed

Comments

@edwardmsmith
Copy link

Should they?

I ran into a situation where I had a collection (think of it as a garbage can of models to destroy) of mixed new (not persisted to server) and not-new (persisted to server) models.

I wanted to destroy them, and was using async.parallel (https://github.com/caolan/async/ ):

  jobset = []
  myCollection.each  (pd) ->
    jobset.push (cb) ->
      pd.destroy
        success: (model,response) ->
           cb null, model
        error: (model, response) ->
           cb response, model
  async.parallel jobset, (err, results) ->
    cb err, results

and my jobset was not completing as the new models never called the success callback.

I was able to easily work around it, but I'm still mostly thinking that the destroy() method on a new model should still execute the success callback for consistency's sake.

On the other hand, I recognize that the success/error callbacks are mostly ajax-related...

@braddunbar
Copy link
Collaborator

Thanks for the issue @edwardmsmith! What are you doing in your success callback? Is this something that's easily worked around by filtering the new models out? Also, this appears to be a server-side project. Could you describe briefly how you're using Backbone models?

@edwardmsmith
Copy link
Author

In async.parallel, each a callback function is passed to each job, and you execute that callback to inform async that job has completed (either successfully or with an error, depending on whether you pass an error or not). The callbacks from each "job" is collected, and when they have all returned, the final callback (the 2nd option to async.parallel in the code snippet) is executed, passing the results of each job as an array.

Async can be used either on the server or on the in the browser - in our case, we're attempting to write our code so that it runs in both environments.

It is easily worked around (yet, not as elegant as it could be) by testing whether the model is new or not:

                jobset = []
                myCollection.each (pd) ->
                    jobset.push (cb) ->
                        # if this has never been saved, then "success" is not called
                        # on destroy, so we just destroy it and trigger the cb.
                        if pd.isNew()
                            pd.destroy()
                            cb null, pd
                        else
                            pd.destroy
                                success: (model,response) ->
                                    cb null, model
                                error: (model, response) ->
                                    cb response, model
                async.parallel jobset, (err, results) ->
                    cb err, results

In this specific case, think of the collection as a garbage can - The user is working with a set of models retrieved from the server, and additional models created on the client - but not yet persisted. The end result is a couple of collections - things to delete and things to create/update. This collection is the set of things to delete - some of them are "new" in that they have not been persisted and some of them were fetched from the server - but in each case, we don't want them any more.

So, as I said, I can see both sides of the argument, and the 'workaround' is not onerous, but its not as elegant as it could be.

@braddunbar
Copy link
Collaborator

Thanks for the use case. I don't see any reason why the success callback shouldn't be called, but I haven't really done extensive work with destroying new models.

@jashkenas Is there any reason not to execute the success callback for new models?

@jashkenas
Copy link
Owner

Yep -- it's silly to call destroy on a new model in the first place, and probably indicates a bug in your application logic. But still, I don't see the harm in triggering the success callback, if one is passed.

@braddunbar
Copy link
Collaborator

After looking at the code, it actually makes destroy a bit more straightforward (obviates the need for triggerDestroy), besides being nicely consistent. I'll submit a patch for it later on.

@edwardmsmith
Copy link
Author

@jashkenas - "it's silly to call destroy on a new model in the first place, and probably indicates a bug in your application logic" - can you explain more? It makes perfect sense to me.

You really find it hard to conceive of a situation where an app may generate a number of un-persisted models, which get added to various collections, and are used in various views, and at some point the app might want to dispose of them in an orderly way?

You make a lot of these opinionated statements about a library that you've worked so hard to be "non-opinionated".

@jashkenas
Copy link
Owner

You make a lot of these opinionated statements about a
library that you've worked so hard to be "non-opinionated".

I'm just trying to be helpful with the semantics here. destroy means: delete a model from the server -- just like save means: persist a model to the server. If your application knows that a model does not exist on the server (yet), then trying to delete it from the server is an error.

To remove an un-persisted model, just call remove.

@edwardmsmith
Copy link
Author

That works if all I want to do is remove a model from a collection, but to me, "remove this model from a collection" and "destroy this model" are semantically VERY different, and, in my opinion, 'destroy' is just as conceptually valid on a non-persisted model as it is on a persisted model.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants