"Failure to update the $scope object within an AngularJS service results in no changes being reflected in

I am currently working on integrating Google maps into an Angular / IonicFramework project. The implementation involves a directive, a service, and a controller. Within the scope ($scope), I have objects for the map, marker, and geolocation. While the map and marker objects update in the view as expected, the geolocation object does not.

Below is the template/view:

<div class="item item-input item-stacked-label item-icon-right">
  <div>
    Location
    <a ng-click="centerOnMe()">
      <i class="icon ion-android-locate" style="font-size:24px"></i>
    </a>
  </div>
  <textarea style="margin-top: 0px; margin-bottom: 0px; height: 45px;" placeholder="Location not found." disabled>{{geoloc.addr}}</textarea>
</div>
<div class="item item-input">
  <div class="input-label">Map</div>
</div>
<div class="item item-input" style="height:15em;">
  <ion-content scroll="false">
    <map on-create="mapCreated(map, geoloc, marker)"></map>
  </ion-content>
</div>

The map directive code:

angular.module('starter.directives', [])

.directive('map', function(MapService) {
  // Directive implementation here...
});

The map controller code:

angular.module('starter.controllers', [])

.controller('MapCtrl', function($scope, $ionicLoading, MapService) {
  // Controller implementation here...
});

The map service code:

angular.module('starter.services', [])

.factory('MapService', function() {
  // Service implementation here...
})
;

The approach I followed was inspired by this resource:

Despite attempting solutions from sources like Stack Overflow, specifically regarding Angular directives and Google Maps integration, the issue with the $scope.geoloc object persists:

Angular directive scope between google maps and a controller

Phonegap not firing GoogleMaps v3 domListener function inside AngularJS directive

Answer №1

Consider enclosing

$scope.geoloc = MapService.getCurrentLocation($ionicLoading,$scope.map, $scope.geoloc, $scope.marker);

within a $timeout function

angular.module('starter.controllers', [])

.controller('MapCtrl', function($scope,$timeout, $ionicLoading, MapService) {
    $scope.mapCreated = function(map, geoloc, marker) {
    $scope.map = map;       //assigning the directive's map to $scope
    $scope.geoloc = geoloc; //assigning the directive's geolocation to $scope
    $scope.marker = marker; //assigning the directive's marker to $scope
  };

  $scope.centerOnMe = function () {
     console.log('Centering..');
     if (!$scope.map && !$scope.marker && !$scope.geoloc) {
       return;
     }
     $scope.loading = $ionicLoading.show({
       template: 'Getting current location...',
       noBackdrop: true
     });
    $timeout(function(){
         $scope.geoloc = MapService.getCurrentLocation($ionicLoading, $scope.map, $scope.geoloc, $scope.marker);
         // ^^^ The update of $scope.geoloc may not happen immediately. It might need to be invoked twice for proper updating.
    });
  }
});

Additional Note: While not the ideal method, this should trigger the correct updation of your $scope.geoloc.

I recommend checking out Gajotres's helpful tutorial as well.

Trust this information was beneficial!

Answer №2

After revising my directive, service, and controller, everything is finally functioning as intended. The data is now managed within the service, which I then injected into both the directive and the controller.

Here is the updated map service:

.factory('MapService', function() {
  var service = {};
  service.map = null;
  service.marker = null;
  service.geoloc = {
      lat: 0.0,
      lng: 0.0,
      str: "",
      brgy: "",
      muni: "",
      reg: "",
      addr: ""  
  };

  service.init = function(map, marker) {
    this.map = map;
    this.marker = marker;
  }

  service.getCurrLoc = function($ionicLoading) {

    navigator.geolocation.getCurrentPosition(function (pos) { //callback if get location succeeds
      service.geoloc.lat = pos.coords.latitude;
      service.geoloc.lng = pos.coords.longitude;

      var latlngpos = new google.maps.LatLng(pos.coords.latitude, pos.coords.longitude);
      var geocoder = new google.maps.Geocoder();

      service.marker = new google.maps.Marker({
        position: latlngpos,
        map: service.map
      });

      //get location
      geocoder.geocode({'latLng': latlngpos}, function(results, status) {
        if (status == google.maps.GeocoderStatus.OK) {
          if (results[0]) {
            service.map.setZoom(16);
            service.marker.setOptions({
                position: latlngpos,
                map: service.map
            });

            service.geoloc.str = results[0].address_components[0].short_name; //type:route
            service.geoloc.brgy = results[0].address_components[1].short_name; //type:neighborhood
            service.geoloc.muni = results[0].address_components[2].short_name; //type:locatlity
            service.geoloc.reg = results[0].address_components[3].short_name; //type:admnistrative_area_leveservice
            service.geoloc.addr = results[0].formatted_address;


            service.map.setCenter(latlngpos);
            $ionicLoading.hide(); //hide loading prompt
          } else {
            console.log('No results found');
          }
        } else {
          console.log('Geocoder failed due to: ' + status);
        }
      });                     
    },
    function (error) { //callback if get location fails
    },
    { enableHighAccuracy: true }); //geolocation options
  }
  return service;
})

Next, we have the revised map directive:

.directive('map', ["MapService", function(MapService) {
  return {
    restrict: 'E',
    scope: {
      onCreate: '&'
    },
    link: function ($scope, $element, $attr) {
      function initialize() {
        var mapOptions = {
          //set to Philippines
          center: new google.maps.LatLng(14.6839606, 121.0622039),
          zoom: 16,
          mapTypeId: google.maps.MapTypeId.ROADMAP
        };

        MapService.map = new google.maps.Map($element[0], mapOptions);

        MapService.marker = new google.maps.Marker({
          map: MapService.map
        });

        $scope.onCreate(
          {
            map: MapService.map,         //link map to map in controller
            marker: MapService.marker,   //link marker to marker in controller
            geoloc: MapService.geoloc    //link geoloc to geoloc in controller
          }
        );

        // Stop the side bar from dragging when mousedown/tapdown on the map
        google.maps.event.addDomListener($element[0], 'mousedown', function (e) {
          e.preventDefault();
          return false;
        });
      }

      if (document.readyState === "complete") {
        initialize();
      } else {
        google.maps.event.addDomListener(window, 'load', initialize);
      }
    }
  }
}])

Lastly, we implemented changes to the map controller:

.controller('MapCtrl', ["$scope", "$ionicLoading", "MapService", function($scope, $ionicLoading, MapService) {
  $scope.mapCreated = function(map, marker, geoloc) {
    $scope.map = map;       //sets the map from the directive to the $scope
    $scope.marker = marker; //sets the marker from the directive to the $scope
    $scope.geoloc = geoloc; //sets the geoloc from the directive to the $scope
    console.log('$scope.geoloc in $scope.mapCreated', $scope.geoloc);


    $scope.centerOnMe();
  };

  $scope.centerOnMe = function () {
    console.log("Centering");
    if (!$scope.geoloc && !$scope.map && !$scope.marker) {
      console.log($scope.map);
      console.log($scope.marker);
      console.log($scope.geoloc);

      return;
    }
    $scope.loading = $ionicLoading.show({
      template: 'Getting current location...',
      noBackdrop: true
    });

    MapService.getCurrLoc($ionicLoading);

  }
}])

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Dimensions of HTML Container on Google Website

I'm attempting to incorporate a collapsible table using HTML Box in a Google site. The code for the collapsible table can be found at http://tutorials.seowebpower.com/google-sites-advanced/collapsible-table. Here is the code: <html> <head> ...

Creating dynamic values in data-tables using Vuetify

As I work with JSON data, my current task involves formatting it using Vuetify's Data Tables. The official documentation provides guidance on defining table headers as shown below: import data from './data.json' export default { data ...

Looking to leverage iframes in your Javascript code?

Currently, I am implementing a JSP popup window using JavaScript with an iframe to display a table of multiple records. However, when I disable the table of multiple records, it does not prevent the onclick function (hyperlink). The code snippet is provid ...

Customizing the styling of a TextField component in ReactJS using material-ui

I am currently working with Reactjs and material-ui. I am looking to apply some custom styles to a TextField using css. Specifically, I would like to change the color of the TextField underline and label when the input is clicked. Although I know it can b ...

What is the most effective method for implementing a fallback image in NextJS?

Lately, I've been immersed in a NextJS project that involves utilizing the YoutubeAPI to retrieve video details, such as thumbnail URLs. When it comes to fetching a full resolution image, the thumbnail URL typically follows this format: https://i.yti ...

Unable to insert a JSON object into an array using JavaScript and MongoDB

I'm encountering an issue when trying to push data into my Student model with the following schema: var StudentSchema = new Schema({ firstName: { type: String, trim: true, default: '' //validate: [validat ...

Node: permit the event loop to run during lengthy tasks and then return

Currently, I am facing an issue with a function that works like this: function longFunc(par1,par2) { var retVal = [], stopReq = false; function evtLs() { stopReq = true; } something.on("event", evtLs); for(var i=0; ...

Enhance Your Text Areas with Vue.js Filtering

Currently, I am working with a textarea that has v-model: <textarea v-model="text"></textarea> I am wondering how to filter this textarea in Vue. My goal is to prevent the inclusion of these HTML quotes: &amp;amp;#039;id&amp;amp;#039 ...

What is the procedure for generating a mouse event for clicking a tab in Selenium WebDriver?

As I work with Selenium WebDriver and Java, there is a tab named PR Per Product. Under the PR Reports tab, there are multiple tabs. In the PR tab, I used: WebElement menuHoverLink = driver.findElement(By.id("ext-pr")); actions.moveToElement(menuHoverLink) ...

Using VueMultiselect with Vue 3: A guide for beginners

I'm currently experimenting with the vue multiselect component, but when I include it in the template, I am encountering a series of warnings and errors. <script src="https://unpkg.com/<a href="/cdn-cgi/l/email-protection" class="__cf_email ...

How can you continuously calculate and show the total quantity of items in a list?

Currently, I have lists set up in my sidebar and I'm looking to include a label displaying the number of items within each list. To provide a better understanding of what I'm aiming for, I've put together a JSFiddle demonstration at the fol ...

Obtaining the referring URL after being redirected from one webpage to another

I have multiple pages redirecting to dev.php using a PHP header. I am curious about the source of the redirection. <?php header(Location: dev.php); ?> I attempted to use <?php print "You entered using a link on ".$_SERVER["HTTP_REFERER"]; ?> ...

I am interested in altering the color of table rows using either JQuery or CSS

Looking to update row colors based on grouping. For example: Color the TR accordingly for every 5 rows: First 5 rows in green (0-4 Rows), Next five rows in red (5-9 Rows), Following five rows in yellow (10-14 Rows) and repeat the pattern... ...

Buttons for concealment and revelation are unresponsive

I have the following code snippet for uploading an image, which is taken from a template utilizing bootstrap 2. <div class="span6"> <div class="control-group"> <label class="control-label">Imagen</label> <div cla ...

What is the process of performing numerical calculations using jQuery?

I need to deduct certain input values from the total price. Here's the code snippet: $('.calculate-resterend').click(function(e) { e.preventDefault(); var contant = $('.checkout-contant').val(); var pin = $('.che ...

Can anyone tell me what js stands for?

I stumbled upon this snippet of code and am curious about what it does. Is it some sort of array? test = {a: [1,0,0], b:[0,1,0], c:[0,0,1]}; If I wanted to access the array for A specifically, how would I do that? console.log(bases[a]); This results in ...

Exploring ways to programmatically include questions in the meta.js file of a Vue Webpack template

I have made a customized version of the Vue Webpack template and am currently modifying the meta.js file. I am attempting to figure out how to include a new property in prompts as shown below: "pages": { "type": "input", "required": true, ...

How can I use JavaScript to find a keyword on a webpage that is not located within an <a> tag or its href attribute?

I'm on a mission to locate a specific keyword within a webpage. Sounds simple, right? Well, here's the tricky part - I need to disregard any instances of this keyword that are nested within an <a> tag. For example: <p>Here is some s ...

Issue encountered while using Typescript with mocha: Unable to utilize import statement outside a module

Exploring the world of unit testing with mocha and trying to create a basic test. Project Structure node_modules package.json package-lock.json testA.ts testA.spec.ts tsconfig.json tsconfig.json { "compilerOptions": { "target&qu ...

Unable to transfer function to directive

I have set up a controller and directive as follows: angular.module('cms', ['TestMod']) .controller('MainCtrl', function() { this.refreshPage = function() { alert('Hello World'); }; }); angular.module(&apos ...