Search code examples
jquerygoogle-mapsbackbone.jsjquery-deferreddeferred

Execute and complete google maps geocode before allowing submit to continue


I have a backbone view with some form validation. The form validation uses preventDefault if the form has errors. If it doesn't it should take the value in the input field, retrieve lat,lng and populate two hidden field before submit is allowed to continue. I am trying to use a deferred, but not sure what I've got wrong. I've cut out some unnecessary bits here and there to make it cleaner to post here.

I've tried using preventDefault with the deferred (which I shouldn't have to anyway correct?), but submitting again is a bit of a challenge using the submit event.

Without preventdefault the lat,lng fields are not updated before submit. Perhaps wrong implementation of deferred/promise or when using preventdefault the submit triggers the validation again and I end up in a loop. I don't want to use setTimeout because I don't want to delay the user any longer than getting the response back.

events: {
  'submit': 'validateForm'
},
validateForm: function(e){

//lotsa validation then

  var loc = $('#location').length;
  if ( loc ){  
    var parent = $(e.currentTarget).closest('form');
    e.preventDefault();
    $.when(this.getCoordinates(e))
    .done(function(){
      parent.submit();
    });
  }
},

getCoordinates : function(e) {
    var deferred = $.Deferred();
    var that = this;
    var parent = $(e.currentTarget).closest('form');
    var addr =  parent.find('#location').val();

    if ($.trim(addr).length) {
        var apiurl = '//maps.googleapis.com/maps/api/geocode/json?address=' + addr '&bounds=' + bounds + '';
        $.ajax({
            url : apiurl,
            dataType : 'json'
        })
        .done(function (data) {

            try {
                if(!data.results.length){
                    return;
                }
                var coord = data.results[0].geometry.location;
                var result = data.results[0].formatted_address;
                if (coord) {
                    var lat = coord.lat;
                    var lon = coord.lng;

                    parent.find("input[data-geo-type = 'lat']").val(lat);
                    parent.find("input[data-geo-type = 'lng']").val(lon);
                    deferred.resolve();
                }

            } catch (e) {
                console.error(e);
            }
        }).fail(function () {
             $(e.currentTarget).val(addr);
        });
    } else {
       deferred.reject(); 
    }
return deferred.promise();
}, 

Any help or pointers appreciated!


Solution

  • It looks like you're likely running into an infinite loop due to your inner parent.submit(). Since parent is a jQuery object, it's re-triggering all of the events, thus causing the ajax to happen again, infinitely. Fortunately, the solution is simple. Instead of calling .submit() on the jQuery object, call it on the form itself. You'll also have to make sure that you don't have id="submit" anywhere. By calling it directly on the form, you'll just submit the form without triggering any events.

    validateForm: function(e){
    
    //lotsa validation then
    
      var loc = $('#location').length;
      if ( loc ){  
        var parent = $(e.currentTarget).closest('form');
        e.preventDefault();
        $.when(this.getCoordinates(e))
        .done(function(){
          //parent.submit();
          parent[0].submit();/* modified line */
        });
      }
    },