我将如何使用 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));
希望对您有所帮助,谢谢。
这是因为 ng-model-options
指令。
在我们提交表单之前,模型中的城市值不会更新。
因此,在更改城市后,当我们第一次单击时,将获取旧城市的值,这不会反映 UI 中的任何更改。由于这是一个提交操作,这将更新模型。
当我们第二次单击时,将使用新的城市值获取值。
我正在努力将一些 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));
希望对您有所帮助,谢谢。
这是因为 ng-model-options
指令。
在我们提交表单之前,模型中的城市值不会更新。
因此,在更改城市后,当我们第一次单击时,将获取旧城市的值,这不会反映 UI 中的任何更改。由于这是一个提交操作,这将更新模型。
当我们第二次单击时,将使用新的城市值获取值。