2

Here's a jsFiddle that shows what I'm trying to do: http://jsfiddle.net/P3c7c

I'm using the Google Places AutoComplete widget to obtain lat/long coordinates, which I then wish to use in a subsequent search function. It seemed that the proper way to implement this, considering the need to add an event listener to an element, was to use a directive, and to attach the listener using the directive's linking function. However, inside of this listener, I need it to set the location property of the SearchForm controller, which is its parent. And I have not figured out how to make that connection. Here's the relevant chunk of code:

    /* Controllers */
    function SearchForm($scope){
        $scope.location = ''; // <-- this is the prop I wish to update from within the directive

        $scope.doSearch = function(){
            if($scope.location === ''){
                alert('Directive did not update the location property in parent controller.');
            } else {
                alert('Yay. Location: ' + $scope.location);
            }
        };
    }

    /* Directives */
    angular.module('OtdDirectives', []).
        directive('googlePlaces', function(){
            return {
                restrict:'E',
                replace:true,
                transclude:true,
                scope: {location:'=location'}, // <--prob something wrong here? i tried @/& too, no luck
                template: '<input id="google_places_ac" name="google_places_ac" type="text" class="input-block-level"/>',
                link: function($scope, elm, attrs, ctrl){
                    var autocomplete = new google.maps.places.Autocomplete($("#google_places_ac")[0], {});
                    google.maps.event.addListener(autocomplete, 'place_changed', function() {
                        var place = autocomplete.getPlace();
                        // THIS IS THE STRING I WANT TO SAVE TO THE PARENT CONTROLLER
                        var location = place.geometry.location.lat() + ',' + place.geometry.location.lng();
                        // THIS IS NOT DOING WHAT I EXPECT IT TO DO:
                        $scope.location = location;
                    });
                }
            }
        });

Thanks in advance.

YellowShark
  • 2,169
  • 17
  • 17

1 Answers1

3

Two minor corrections and it should work:

<google-places location="location"></google-places>

and when you set location inside your directive you also need to do $scope.$apply()

$scope.$apply(function() {
    $scope.location = location;
});

You have to do $apply() because the event happens outside of angular digest loop, so you have to let angular know that something has changed inside the scope and it needs to "digest" it's bi-directional bindings and other internal async stuff.

Also, I don't think you need transclude:true.

http://jsfiddle.net/P3c7c/1/

pavelgj
  • 2,246
  • 11
  • 13
  • 3
    Working fiddle: http://jsfiddle.net/mrajcok/pEq6X/ You also don't need the `ctrl` argument to the link function. And you can simply say `scope: { location: '=' }` since you are using the same name ("location") inside the directive. – Mark Rajcok Jan 08 '13 at 17:03
  • Great! Thanks very much pavelgj, and Mark too. I knew I was just missing something very simple, and the explanation on $apply is appreciated. In my working code, I'd actually sort of "remembered" that i needed it, but the final key I think was in adding the location attribute to the `` directive, which made it all work. And that really opens up the whole idea then I believe, of how to pass data in & out of directives, right? The attribute name will be the "scope-dot" name inside of the directive, and the value will be likewise - is that about right? – YellowShark Jan 08 '13 at 17:28
  • 1
    Ex: `scope: { foo: '=bar' }`. `foo` will be property in the scope of the directive and `bar` will be the attribute name in the HTML: `` and the bi-directional binding will be established with expression `baz`. It's described in the "Directive Definition Object" section [here](http://docs.angularjs.org/guide/directive). – pavelgj Jan 08 '13 at 17:44
  • And indeed, I did read that page. Many, many visits! :) I totally get it now though, you're right that it's highly-documented, I just needed a bit more context to get my own gears spinning with it. Thanks much again! – YellowShark Jan 08 '13 at 18:10
  • 1
    @YellowShark, there are numerous ways to get data in and out of directives: '=', '@', and '&' all work with isolated scopes, but you can also create a "normal" child scope (i.e., one that prototypically inherits from the parent scope) or don't create a new scope -- use the parent's scope in the directive. (If interested, see the "directives" section on [this page](http://stackoverflow.com/questions/14049480/what-are-the-nuances-of-scope-prototypal-prototypical-inheritance-in-angularjs)). Yeah... that directives page is not an easy read. – Mark Rajcok Jan 08 '13 at 21:09
  • Mark thanks for that SO question you linked... wow, made my brain hurt, in a good way! I'm definitely going to take the time to digest that all. – YellowShark Jan 08 '13 at 22:48