Tim Tim - 4 months ago 8
AngularJS Question

View not updating upon form submit

This is a basic weather app that grabs info from an open weather API. Upon loading, it gets the weather information for the default city and I'm able to successfully log the returned info to the console, however my view doesn't update until I switch to a different view and then back. I feel like a $scope.$apply needs to go somewhere, but I couldn't get it working anywhere I tried.

App:

var weather = angular.module('weather', ['ngRoute', 'ngResource', 'ui.router']);

weather.config(function($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise('/overview');
$stateProvider
.state('overview', {
url: '/overview',
templateUrl: 'pages/overview.html',
controller: 'overviewController'
})
.state('forecast', {
url: '/forecast',
templateUrl: 'pages/forecast.html'
})
.state('details', {
url: '/details',
templateUrl: 'pages/details.html'
})
});

weather.controller('homeController', ['$scope', '$location', '$resource', 'weatherService', function($scope, $location, $resource, weatherService) {
$scope.txtCity = weatherService.city;
$scope.submit = function() {
weatherService.city = $scope.txtCity;
weatherService.getForecast(weatherService.city, function(x){
weatherService.currentForecast = x;
// TESTING
console.log(x);
});
};

// Run the submit function initially to retrieve weather data for the default city
$scope.submit();

// Function to determine the active tab, sets its class as "active"
$scope.isActive = function (path) {
return ($location.path().substr(0, path.length) === path) ? 'active' : '';
}
}]);

weather.controller('overviewController', ['$scope', '$filter', 'weatherService', function($scope, $filter, weatherService) {
$scope.currentForecast = weatherService.currentForecast;

// Kelvin to Fahrenheit
$scope.convertTemp = function(temp) {
return Math.round((1.8*(temp - 273))+32);
}
$scope.convertToDate = function(dt) {
var date = new Date(dt * 1000);
return ($filter('date')(date, 'EEEE, MMM d, y'));
};
}]);

weather.service('weatherService', function($resource, $http){
this.currentForecast = null;

// default city
this.city = 'Chicago, IL';
this.getForecast = function(location, successcb) {
$http({
method : "GET",
url : "http://api.openweathermap.org/data/2.5/forecast/daily?q="+location+"&mode=json&cnt=7&appid=e92f550a676a12835520519a5a2aef4b"
}).success(function(data){
successcb(data);
}).error(function(){

});
}
});


overview.html (view):

<h4>Daily</h4>
<ul>
<li ng-repeat="w in currentForecast.list">{{ convertToDate(w.dt) }} {{ convertTemp(w.temp.day) }}&deg;</li>
</ul>


Submit form:

<form ng-submit="submit()">
<button type="submit" class="btn btn-primary btn-sm">Get Forecast</button>
<input type="text" ng-model="txtCity">
</form>

Answer

Change your service function to:

this.getForecast =  = function(location) {
    return $http({
        method : "GET",
        url : "http://api.openweathermap.org/data/2.5/forecast/daily?q="+location+"&mode=json&cnt=7&appid=e92f550a676a12835520519a5a2aef4b"
    }).then(
         function(response) {
             return response.data;
         }
    )
};

And in your controller, use it like this in the submit function:

weatherService.getForecast(weatherService.city).then(
    function(data) {
        weatherService.currentForecast = data;
        console.log(data);
    }
);

This allows you to handle the success function of the $http promise directly from the controller. Currently you're passing the function to the service, and it doesn't know what the weatherServiceparameter is, because it outside of the service scope (It's avalable in the controller).

Now, in your overviewController controller, you can watch for changes inside the service like this:

$scope.$watch(function() { return weatherService.currentForecast; }, 
    function(newVal) { 
        if(!newVal) return;
        $scope.currentForecast = newVal;
}, true);