0

I have a form that is being used to both create a new object and edit existing objects. The form contains a few plugins (date picker, jstree) and I'm trying to use jquery deferreds to make the loading cleaner. This is my first real attempt at using deferred objects.

What I want to do is firstly load the form, and then if I am editing an existing form, I also want to populate the form with the existing values. My cost looks like this (it's coffeescript, but I think its perfectly readable)

    $.when(
            # These functions all return jquery.Deferred objects.
            getBookingDetails(id), buildJstree(), buildCalendar()
    ).then(
        # Editing an existing booking
        (bookingResponse, jstreeResponse_ignored, calendarResponse_ignored) =>
            # ... populate form with data from bookingResponse ...
            $("#name").val(bookingResponse[...])
            # etc., Rest is omitted
    ).fail(
        # Creating a new booking, so don't need to do anything more to the form.
        () ->
    )

So if I'm editing, I expect the $.when() to resolve and drop into the then(). If I am creating a new booking, I artificially cause the $.when() to fail like this

getBooking: (id) =>
    if id is null
        return $.Deferred().reject('id is null')

    return $.ajax({
        url        : base + "/api/v1/daily/id/#{id}"
        type       : "GET"
        contentType: "application/json"
        data       : {}
    })

But I get the sense that this is wrong because if any of the other deferred objects in the $.when() fail, e.g. because my plugin didn't load correctly, then I will need to do extra work to check for this error, which just feels incorrect.

If my approach is indeed bad, can you explain to me why?

How do I restructure things to do this properly?

pdc
  • 134
  • 1
  • 9
  • Using `if id is null return $.Deferred().reject(…)` is [totally](http://stackoverflow.com/q/31933675/1048572) [fine](http://stackoverflow.com/q/21887856/1048572). – Bergi Jun 02 '16 at 01:23
  • 1
    "* if any of the other deferred objects in the $.when() fail then I will need to do extra work to check for this error*" - maybe you actually want to handle only the rejection of the booking defails? Then use `getBookingDetails(id).then(null, function(err) { … })` for that (all inside the `$.when`). – Bergi Jun 02 '16 at 01:25
  • Oh! I didn't even think of doing that. Yes that would also be a better way to structure this code. – pdc Jun 02 '16 at 01:58

1 Answers1

0

After thinking about this, I have come up with a solution. The idea is to separate out the $.when() into its main two components: plugins and loading the previous booking data. Firstly, I build a $.when() construct to load the plugins.

$.when( # load plugins
    buildJstree(), buildCalendar()
).fail( # Handle failed plugins
    () -> # Code to handle failed plugin.
)

Now if one of the plugins fails, then execution will stop and I can handle that failed plugin.

If all the plugins load successfully, then I can load my booking and proceed, so I have this:

$.when( # Load plugins.
    buildJstree(), buildCalendar()
).fail(
    () -> # Code to handle failed plugin.
).then( # Load previous booking data.
    () ->
        # If it's a new booking, don't need to load previous data.
        return if id is null

        # Editing an existing booking
        getBookingDetails(id)
        .then(
            (bookingResponse) =>
                # ... populate form with data from bookingResponse ...
                $("#name").val(bookingResponse[...])
                # etc., Rest is omitted
        )
)

So now I have a pipelined approach, load the plugins, then load the data, then populate the form.

And I also do not need to reject a Deferred object as part of my expected control flow since I check for a null string ahead of time. In hindsight, I could have actually done this in my initial code.

pdc
  • 134
  • 1
  • 9