Tim
Tim

Reputation: 187

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>

Upvotes: 1

Views: 51

Answers (1)

Alon Eitan
Alon Eitan

Reputation: 12025

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);

Upvotes: 3

Related Questions