2

Hi I have a button which when it gets clicked triggers a function. The function does some stuff (reverse geocodes a latitude/longitude) and then fills a hidden form input with a value.

I need the input to have the correct value before the rest of the code I need gets executed, is there a way to do this? At the moment I have

$('.addButton').click(function() { 
    //first run the reverse geocode to update the hidden location input with the readable address
    reversegeocode();
    var location = $("#location").val();//the value I need
    $.post("<?php echo $this->webroot;?>locations/add", {location:location})
        .done(function (data) {
            $("#locationsHolder").html(data);
        });
});

So basically I don't want to get the value from the input and post it via AJAX until I know that the reversegeocode() function has finished

Can anyone please explain how I can go about this. I've read some stuff about deferment but I'm absolutely useless at figuring out Javascript and I'm really struggling.

Thanks

EDIT:

Here's my reversegeocode funciton

function reversegeocode(){
    var lat = $('#lattitude').val();
    var lng = $('#longitude').val();
    var latlng = new google.maps.LatLng(lat, lng);
    geocoder.geocode({'latLng': latlng}, function(results, status) {
        if (status == google.maps.GeocoderStatus.OK) {
          if (results[0]) {//http://stackoverflow.com/questions/8082405/parsing-address-components-in-google-maps-upon-autocomplete-select
              var address_components = results[0].address_components;
              var components={}; 
                jQuery.each(address_components, function(k,v1) {jQuery.each(v1.types, function(k2, v2){components[v2]=v1.long_name});})
            var output = '';
            var needAcomma = false;
            if(components.route != undefined) {
                output += components.route;
                needAcomma = true;
            }
            if(components.locality != undefined) {
                if(needAcomma) {
                    output += ', ';
                }
                output += components.locality;
                needAcomma = true;
            }
            if(components.administrative_area_level_1 != undefined) {
                if(needAcomma) {
                    output += ', ';
                }
                output += components.administrative_area_level_1;
                needAcomma = true;
            }else if(components.administrative_area_level_2 != undefined) {
                if(needAcomma) {
                    output += ', ';
                }
                output += components.administrative_area_level_2;
                needAcomma = true;
            }else if(components.administrative_area_level_3 != undefined) {
                if(needAcomma) {
                    output += ', ';
                }
                output += components.administrative_area_level_3;
                needAcomma = true;
            }
            $("#location").val(output);
          } else {
            alert('No results found');
          }
        } else {
          alert('Geocoder failed due to: ' + status);
        }
    });
}
crazy sarah
  • 611
  • 4
  • 13
  • 29
  • 1
    looks like `reversegeocode` is an asynchronous method, you need to share that method for us to see what can be done. Any way the `reversegeocode` method need to provide some kind of callback facility to let us know when it is completed – Arun P Johny Feb 19 '14 at 23:35
  • Hi, thanks for the response. I've stuck my function in my question. it just gets the address from google and sticks the result in an input. – crazy sarah Feb 20 '14 at 00:01

4 Answers4

2

Since reversegeocode is a asynchronous method, you need to use a callback based solution. reversegeocode should receive a callback method as a argument and then invoke the callback once the geocoding is completed.

$('.addButton').click(function () {
    //pass a callback to reversegeocode which will get called once the geocoding is completed
    reversegeocode(function (location) {
        //the callback receives the location as a parameter
        $.post("<?php echo $this->webroot;?>locations/add", {
            location: location
        })
            .done(function (data) {
            $("#locationsHolder").html(data);
        });
    });
});

function reversegeocode(callback) {
    var lat = $('#lattitude').val();
    var lng = $('#longitude').val();
    var latlng = new google.maps.LatLng(lat, lng);
    geocoder.geocode({
        'latLng': latlng
    }, function (results, status) {
        if (status == google.maps.GeocoderStatus.OK) {
            if (results[0]) { //http://stackoverflow.com/questions/8082405/parsing-address-components-in-google-maps-upon-autocomplete-select
                var address_components = results[0].address_components;
                var components = {};
                jQuery.each(address_components, function (k, v1) {
                    jQuery.each(v1.types, function (k2, v2) {
                        components[v2] = v1.long_name
                    });
                })
                var output = '';
                var needAcomma = false;
                if (components.route != undefined) {
                    output += components.route;
                    needAcomma = true;
                }
                if (components.locality != undefined) {
                    if (needAcomma) {
                        output += ', ';
                    }
                    output += components.locality;
                    needAcomma = true;
                }
                if (components.administrative_area_level_1 != undefined) {
                    if (needAcomma) {
                        output += ', ';
                    }
                    output += components.administrative_area_level_1;
                    needAcomma = true;
                } else if (components.administrative_area_level_2 != undefined) {
                    if (needAcomma) {
                        output += ', ';
                    }
                    output += components.administrative_area_level_2;
                    needAcomma = true;
                } else if (components.administrative_area_level_3 != undefined) {
                    if (needAcomma) {
                        output += ', ';
                    }
                    output += components.administrative_area_level_3;
                    needAcomma = true;
                }
                $("#location").val(output);
                //call the callback
                callback(output);
            } else {
                alert('No results found');
            }
        } else {
            alert('Geocoder failed due to: ' + status);
        }
    });
}
Arun P Johny
  • 384,651
  • 66
  • 527
  • 531
1

Change reversegeocode to take a callback parameter (also known as a continuation).

Encapsulate all the stuff that needs to wait for reversegeocode to finish, putting it into an in-place, nameless function.

(Note the similarity to what you're already doing for the click handler.)

With this approach you are also free to add parameters to the callback, which you can use to pass data directly through.

$('.addButton').click(function() { 
    reversegeocode(function(some_data) {
        var location = $("#location").val();//the value I need
        //...stuff...
    });
});

function reversegeocode(callback){
    //...stuff...
    geocoder.geocode({'latLng': latlng}, function(results, status) {
        if (status == google.maps.GeocoderStatus.OK) {
          //...stuff...
        } else {
          alert('Geocoder failed due to: ' + status);
        }
        callback(some_data);
    });
}
Sean Gugler
  • 779
  • 8
  • 18
0

You need to use a callback function in the reversegeocode function.

iii
  • 383
  • 1
  • 11
0

The same exact way as you do ajax :)

$('.addButton').click(function() { 
    reversegeocode().done(function(location) {
        $.post("<?php echo $this->webroot;?>locations/add", {location:location})
            .done(function (data) {
                $("#locationsHolder").html(data);
            });
    });
})

To do this you will have reversegeocode return a jquery deferred promise

function reversegeocode() {
  return $.Deferred(function(d) {
    //do stuff and when it succeeds
    d.resolve(location);
    //or if it fails
    d.reject("something went wrong");
  }).promise();
}
George Mauer
  • 117,483
  • 131
  • 382
  • 612
  • Hi George - cool mustache by the way - I'm not sure I understand the return bit of this. I don't actually need it to return anything, is it not possible to do this without having to return anything? – crazy sarah Feb 20 '14 at 00:10
  • @crazysarah You could, you certainly don't have to return anything (and then you wouldn't get a (location) parameter. However I recommend separating the component that does the reverse geocoding and the process of actually setting it in the UI. Return the location, and in one `done` handler set the `#locationsHolder` html and in another `done` handler do what I did above (read the deferred documentation if this is confusing to you). You shouldn't use html in the UI to communicate between functions - there's already a mechanism for that: parameters and return values. – George Mauer Feb 20 '14 at 04:22