0

I'm trying to create a basic weather app based on the user's location, which I take using javascript geolocation service. Everything regarding the location and weather works as expected.
My problem is when the user reject the location request, what I want to do is hide the weather data (which I can't get) and show an appropriate message.
HTML:

<body ng-app="weatherApp">
  <div class="container text-center" style="margin-top: 15vh" ng-controller="weatherCtrl">
    <h1>Local Weather App</h1>
    <h3>{{error}}</h3>
    <div class="row" ng-hide="showError">
      <div id="loc-col" class="col-xs-3 col-xs-offset-1">
        <h3>{{data.name}}</br>
        <small>{{data.sys.country}}</small></h3>
      </div>
      <div id="temp-col" class="col-xs-4">
        <h3>{{data.main.temp}} {{degree}}&deg;</h3>
      </div>
      <div id="weather-col" class="col-xs-3">
        <h3>{{data.weather[0].main}} </br><small>{{data.weather[0].description}}</small> </h3>
      </div>
      <div id="icon-col" class="col-xs-6 col-xs-offset-3">
        <img id="icon" class="img-responsive" ng-src="{{icon}}">
      </div>
    </div>
  </div>
</body>

The error message should be in the <h3> below the h1.

JS:

'use strict';
var app = angular.module('weatherApp', []);

app.factory('weatherFactory', ['$http', function($http){
  var weather = {};

  weather.getWeather = function(lat, lon){
    return $http.jsonp("http://api.openweathermap.org/data/2.5/weather?lat="+lat+"&lon="+lon+"&units=metric&callback=JSON_CALLBACK");
  };
  return weather;
}]);

app.controller('weatherCtrl', ['$scope', 'weatherFactory', '$http', function($scope, weatherFactory, $http){
  $scope.degree = "C";

  $scope.error = "";
  $scope.showError = false;

  var icons = { };

  function getWeatherFromPos(position){
    var lat = position.coords.latitude;
    var lon = position.coords.longitude;
    weatherFactory.getWeather(lat,lon).success(
            function(data){
              $scope.data = data;
              $scope.icon = icons[data.weather[0].icon];
            }
          ).error(function(error){
            setError(error);
          });
  }

  function setError(error){
    $scope.showError = true;
    $scope.error = error;
  }

  //THIS IS THE RELEVANT CODE:
  if (navigator.geolocation) {
      navigator.geolocation.getCurrentPosition(
        function(position) {
          getWeatherFromPos(position);
        }, 
        function() {
          //EXECUTED UPON REJECTION
          setError("Geolocation service failed.");
        }
      );
    }
    else {
      setError("Your browser doesn't support geolocation.");
    }
}]);

When I open up the consoler and I execute the following:

angular.element($(".row")).scope().error
"Geolocation service failed."

So somehow the variable do get updated, but the error message is not shown and the weather data is not hidden.

2
  • angular.element($(".row")) is a collection of elements unless there is only one with that class. Commented Mar 9, 2016 at 18:10
  • @charlietfl as you see in the html code, there is only one element with that class Commented Mar 9, 2016 at 19:07

2 Answers 2

1

Your setError function execute out of $digest cycle of angular. You can do follow:

  function setError(error){
    $scope.showError = true;
    $scope.error = error;
    $scope.$apply();
  }

or

function setError(error){
    $scope.$apply(function () {
       $scope.showError = true;
       $scope.error = error;
   }
}

P.S.: It is concerned about setError("Geolocation service failed."); and setError("Your browser doesn't support geolocation.");.

But for setError(error) it is not ok to use $apply because it is already inside $digest loop (there is no need to call $apply method). Remember you need to call $apply only after you made some changes outside of angular digest loop. See more about it https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$apply

Sign up to request clarification or add additional context in comments.

5 Comments

Thank you, any chance you can explain why said expressions are outside the $digest loop and how can I identify and prevent that?
Implement all your function as $scope methods, and all your code will be executed inside $digest loop. For example: $scope.setError = ... and you will be able to call it as $scope.setError(...)
Adding $scope doesn't work, using $apply like you said in the answer helped, though so thanks.
I'd still like to know how can I know how can I know which function is executed within the $digest loop. Why there was only a problem with the setError() function but not with the getWeatherFromPos()?
navigator.geolocation.getCurrentPosition execute asynchronously, so angular dont know what happening after it is finished (we need to call $scope.$apply() to tell about it). getWeatherFromPos uses getWeather method which returns $http. $http has two methods success and error which is already executed inside $digest loop. So there is no need to call $scope.$apply()
0

I think thank you missed ng-bind:

<h3 ng-bind="error">{{error}}</h3>

6 Comments

ng-bind="error" and {{error}} do the same thing
I seen in my own project that it will make difference
difference in what? it is redundant to use them together. Read the docs...they say the same thing I am saying: docs.angularjs.org/api/ng/directive/ngBind
That's not what it says...it says scope is not updating
it was the same - it was {{arg}}, update from scope - no result. After adding ng-bind it worked.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.