我将如何使用 AngularJS promises 来确保获取的数据更新范围?

How would I use AngularJS promises to ensure that fetched data updates the scope?

我正在努力将一些 JSON 请求从控制器中分离出来并放入 angular 中的工厂服务中,并使其正常工作。目前,当我更新数据时(就像请求新数据一样),我必须发出两次请求才能真正看到更新后的数据。根据我目前对 Angular 的有限了解,我认为我错过了 promise 方面。但是,我不确定如何最好地进行。代码如下:

(function(angular) {
  'use strict';

  var myApp = angular.module('weatherApp', [])
    
    .controller('MyWeatherController', ['$scope', '$http', '$log', 'weather', function($scope, $http, $log, weather) {
      $scope.city = 'Cincinnati';
      $scope.units = 'imperial';

      $scope.updateData = function() {
        weather.updateData($scope, $http, $log);
      };

      // Initial run to fetch weather data
      $scope.updateData();
    }])


    // Set up a service factory to fetch current and forecast data
    .factory('weather', ['$http', function($http) {
      var services = {};

      services.updateData = function(scope, http, log) {
        this.updateCurrent(scope, http, log);
      }

      // Get current data
      services.updateCurrent = function(scope, http, log) {
        // Fetch current weather data from api
        var url = 'http://api.openweathermap.org/data/2.5/weather';

        http.jsonp(url, {params: {
          q: scope.city,
          units: scope.units,
          callback: 'JSON_CALLBACK'
        }})
        .success(function(data, status, headers, config) {
          scope.main = data.main;
          scope.wind = data.wind;
          scope.description = data.weather[0].description;
        })
        .error(function(data, status, headers, config) {
          log.error('Could not retrieve data from '+url);
        });
      };

      return services;
    }]);

} (window.angular));
body {
  width: 50%;
  margin: 2em auto;
}    

h2 {
  background: green;
  color: white;
  padding: 10px;
  margin-bottom: 0;
}

input {
  margin-bottom: 1em;
}

.section {
  margin-bottom: 2em;
  background: #f0f0f0;
  padding: 10px;
}
<!DOCTYPE html>
<html>
  <head>
    <title>Weather checker</title>
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.11/angular.min.js"></script>
    <script src="script.js"></script>

    <link type="text/css" rel="stylesheet" href="style.css" media="all" />
  </head>
  <body>
    <div class="container" ng-app="weatherApp">
      <div ng-controller="MyWeatherController">

        <h2>Current weather in {{city}}</h2>
        <div class="section current">
          <form name="weather-form">
            <label for="city">City, State / Zip code</label>
            <input 
              type="text" 
              ng-model="city" 
              ng-model-options="{updateOn: 'submit'}">
            <br>
            <label for="units">Units</label>
            <input type="radio" ng-model="units" value="metric"> Metric
            <input type="radio" ng-model="units" value="imperial"> Imperial<br>
            <button ng-click="updateData()">Update</button>
            <h3>{{data.name}}</h3>
            <p>{{description}}</p>
            <p>Temperature: {{main.temp}}</p>
            <p>Wind speed: {{wind.speed}}</p>
          </form>
        </div>
      </div>
    </div>
  </body>
</html>

如您所见,如果您 运行 这个,您必须更新两次才能让新数据显示在视图中。非常感谢对某些示例的任何建议或对后续步骤的反馈!

谢谢

(function(angular) {
  'use strict';

  var myApp = angular.module('weatherApp', [])
    
    .controller('MyWeatherController', ['$scope', '$http', '$log', 'weather', function($scope, $http, $log, weather) {
      $scope.city = 'Cincinnati';
      $scope.units = 'imperial';

      $scope.updateData = function() {
        weather.updateData($scope, $http, $log);
      };

      // Initial run to fetch weather data
      $scope.updateData();
    }])


    // Set up a service factory to fetch current and forecast data
    .factory('weather', ['$http', function($http) {
      var services = {};

      services.updateData = function(scope, http, log) {
        this.updateCurrent(scope, http, log);
      }

      // Get current data
      services.updateCurrent = function(scope, http, log) {
        // Fetch current weather data from api
        var url = 'http://api.openweathermap.org/data/2.5/weather';

        http.jsonp(url, {params: {
          q: scope.city,
          units: scope.units,
          callback: 'JSON_CALLBACK'
        }})
        .success(function(data, status, headers, config) {
          scope.main = data.main;
          scope.wind = data.wind;
          scope.description = data.weather[0].description;
          $scope.digest();
        })
        .error(function(data, status, headers, config) {
          log.error('Could not retrieve data from '+url);
        });
      };

      return services;
    }]);

} (window.angular));
body {
  width: 50%;
  margin: 2em auto;
}    

h2 {
  background: green;
  color: white;
  padding: 10px;
  margin-bottom: 0;
}

input {
  margin-bottom: 1em;
}

.section {
  margin-bottom: 2em;
  background: #f0f0f0;
  padding: 10px;
}
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" version="XHTML+RDFa 1.0">
  <head>
    <title>Weather checker</title>
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.11/angular.min.js"></script>
    <script src="script.js"></script>

    <link type="text/css" rel="stylesheet" href="style.css" media="all" />
  </head>
  <body>
    <div class="container" ng-app="weatherApp">
      <div ng-controller="MyWeatherController">

        <h2>Current weather in {{city}}</h2>
        <div class="section current">
          <form name="weather-form">
            <label for="city">City, State / Zip code</label>
            <input 
              type="text" 
              ng-model="city" 
              ng-model-options="{updateOn: 'submit'}">
            <br>
            <label for="units">Units</label>
            <input type="radio" ng-model="units" value="metric"> Metric
            <input type="radio" ng-model="units" value="imperial"> Imperial<br>
            <button ng-click="updateData()">Update</button>
            <h3>{{data.name}}</h3>
            <p>{{description}}</p>
            <p>Temperature: {{main.temp}}</p>
            <p>Wind speed: {{wind.speed}}</p>
          </form>
        </div>
      </div>
    </div>
  </body>
</html>

您有两种方式(好吧,可能还有更多)可以到这里。 您可以创建一个承诺和 return 从承诺中的 http 调用返回的数据,以在控制器中解析。查看 $q here 文档中的第二个代码块,了解如何执行此操作。

不过,我建议您在您的服务中创建一个 public 属性 来保存数据。然后您可以从您的控制器访问它。由于 Angular 的数据绑定,当服务器响应最终返回并且数据存储在服务中时,控制器将自动 "see" 更新值。

(function(angular) {
  'use strict';

  var myApp = angular.module('weatherApp', [])
    
    .controller('MyWeatherController', ['$scope', '$http', '$log', 'weather', function($scope, $http, $log, weather) {
      //here you expose the service to your scope.  From here you can
      //get at all your data in the view by using weather.result
      $scope.weather = weather;
      
      $scope.city = 'Cincinnati';
      $scope.units = 'imperial';

      $scope.updateData = function() {
        weather.updateData($scope, $http, $log);
      };

      // Initial run to fetch weather data
      $scope.updateData();
    }])


    // Set up a service factory to fetch current and forecast data
    .factory('weather', ['$http', function($http) {
      var services = {};
      
      //make the server response data part of the service's public API
      services.details = {
          someDetail: "",
          someOtherThing: []
      }

      services.updateData = function(scope, http, log) {
        this.updateCurrent(scope, http, log);
      }

      // Get current data
      services.updateCurrent = function(scope, http, log) {
        // Fetch current weather data from api
        var url = 'http://api.openweathermap.org/data/2.5/weather';

        http.jsonp(url, {params: {
          q: scope.city,
          units: scope.units,
          callback: 'JSON_CALLBACK'
        }})
        .success(function(data, status, headers, config) {
          //here we assign the data to the service's result property.
          services.details.someDetail = data.someDetail;
          services.details.someOtherThing = data.someOtherThing;
        })
        .error(function(data, status, headers, config) {
          log.error('Could not retrieve data from '+url);
        });
      };

      return services;
    }]);

} (window.angular));
<p>Some detail:{{weather.details.someDetail}}</p>
<p>Some other thing: {{weather.details.someOTherThing}}</p>

我从你的 html 中删除了 ng-model-options="{updateOn: 'submit'},它工作正常:)

(function(angular) {
  'use strict';

  var myApp = angular.module('weatherApp', [])
    
    .controller('MyWeatherController', ['$scope', '$http', '$log', 'weather', function($scope, $http, $log, weather) {
      $scope.city = 'Cincinnati';
      $scope.units = 'imperial';

      $scope.updateData = function() {
        weather.updateData($scope, $http, $log);
      };

      // Initial run to fetch weather data
      $scope.updateData();
    }])


    // Set up a service factory to fetch current and forecast data
    .factory('weather', ['$http', function($http) {
      var services = {};

      services.updateData = function(scope, http, log) {
        this.updateCurrent(scope, http, log);
      }

      // Get current data
      services.updateCurrent = function(scope, http, log) {
        // Fetch current weather data from api
        var url = 'http://api.openweathermap.org/data/2.5/weather';

        http.jsonp(url, {params: {
          q: scope.city,
          units: scope.units,
          callback: 'JSON_CALLBACK'
        }})
        .success(function(data, status, headers, config) {
          scope.main = data.main;
          scope.wind = data.wind;
          scope.description = data.weather[0].description;
        })
        .error(function(data, status, headers, config) {
          log.error('Could not retrieve data from '+url);
        });
      };

      return services;
    }]);

} (window.angular));
body {
  width: 50%;
  margin: 2em auto;
}    

h2 {
  background: green;
  color: white;
  padding: 10px;
  margin-bottom: 0;
}

input {
  margin-bottom: 1em;
}

.section {
  margin-bottom: 2em;
  background: #f0f0f0;
  padding: 10px;
}
<!DOCTYPE html>
<html>
  <head>
    <title>Weather checker</title>
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.11/angular.min.js"></script>
    <script src="script.js"></script>

    <link type="text/css" rel="stylesheet" href="style.css" media="all" />
  </head>
  <body>
    <div class="container" ng-app="weatherApp">
      <div ng-controller="MyWeatherController">

        <h2>Current weather in {{city}}</h2>
        <div class="section current">
          <form name="weather-form">
            <label for="city">City, State / Zip code</label>
            <input 
              type="text" 
              ng-model="city">
            <br>
            <label for="units">Units</label>
            <input type="radio" ng-model="units" value="metric"> Metric
            <input type="radio" ng-model="units" value="imperial"> Imperial<br>
            <button ng-click="updateData()">Update</button>
            <h3>{{data.name}}</h3>
            <p>{{description}}</p>
            <p>Temperature: {{main.temp}}</p>
            <p>Wind speed: {{wind.speed}}</p>
          </form>
        </div>
      </div>
    </div>
  </body>
</html>

这是承诺的实现。

你的 promise 代码看起来像这样,你不应该将 $scope、$http、$log 之类的变量传递给服务,它们可以通过注入依赖项在服务中轻松使用。

scope 变量不应使用服务进行修改,您应该 return 来自服务的承诺,并且在承诺成功或错误中您可以修改您的范围。

从服务我 returned $http.jsonp 已经有承诺所以你不需要关心 returning 承诺,并且承诺在 .success 函数,你可以在 .error 函数中得到拒绝承诺

代码

(function (angular) {
    'use strict';

    var myApp = angular.module('weatherApp', [])

        .controller('MyWeatherController', ['$scope', '$http', '$log', 'weather', function ($scope, $http, $log, weather) {
        $scope.city = 'Cincinnati';
        $scope.units = 'imperial';

        $scope.updateData = function () {
            weather.updateData($scope.city, $scope.units).
            then(function (res) {
                var data = res.data;
                $scope.main = data.main;
                $scope.wind = data.wind;
                $scope.description = data.weather[0].description;
            },function (data) {
                $log.error('Could not retrieve data from ' + url);
            })

        };

        // Initial run to fetch weather data
        $scope.updateData();
    }])


    // Set up a service factory to fetch current and forecast data
    .factory('weather', ['$http', '$log', function ($http, $log) {
        var services = {};

        services.updateData = function (city, units) {
            return this.updateCurrent(city, units);
        };

        // Get current data
        services.updateCurrent = function (city, units) {
            // Fetch current weather data from api
            var url = 'http://api.openweathermap.org/data/2.5/weather';

            return $http.jsonp(url, {
                params: {
                    q: city,
                    units: units,
                    callback: 'JSON_CALLBACK'
                }
            }).success(function (data, status, headers, config) {
                return data;
            })
            .error(function (data, status, headers, config) {
                $log.error('Could not retrieve data from ' + url);
                return data;
            });

        };

        return services;
    }]);

}(window.angular));

Fiddle

希望对您有所帮助,谢谢。

这是因为 ng-model-options 指令。

在我们提交表单之前,模型中的城市值不会更新。

因此,在更改城市后,当我们第一次单击时,将获取旧城市的值,这不会反映 UI 中的任何更改。由于这是一个提交操作,这将更新模型。

当我们第二次单击时,将使用新的城市值获取值。