AngularJS应用中如何让控制器内的HTTP请求仅执行一次
解决AngularJS控制器中HTTP请求仅执行一次的问题
你的代码之所以没生效,核心原因是控制器每次初始化时,局部变量contadorInicioCtr都会被重新声明并重置为0——也就是说,每次进入这个视图,控制器都会从头执行,contadorInicioCtr永远会变成1,自然每次都会触发请求。
下面是几种可靠的解决思路,你可以根据自己的场景选择:
方案1:利用$rootScope存储全局标记(简单快速)
$rootScope是AngularJS的全局作用域,只要应用不刷新,存储的状态就会保留。我们可以在里面加一个标记,用来判断请求是否已经执行过:
aldro_app.controller('ctrInicio', function ($scope, $http, $rootScope) { // 初始化全局标记(如果还没设置的话) if (!$rootScope.hasFetchedBills) { $rootScope.hasFetchedBills = false; } if (!$rootScope.hasFetchedBills) { var id = parseInt(getVariable("idcontrato")); var url = "http://api/url"; $http({ url: url, method: 'POST', // 记得补全请求方法 data: { id: id } // 根据API需求传递参数 }).then(function(response) { // 请求成功后的业务处理 console.log('账单数据获取成功'); // 更新标记为已执行 $rootScope.hasFetchedBills = true; }).catch(function(error) { // 处理错误:如果需要允许重试,这里不要更新标记 console.error('请求失败:', error); }); } });
注意:尽量不要滥用$rootScope避免全局污染,如果只是这个控制器需要该逻辑,方案2会更优雅。
方案2:使用单例服务存储状态(推荐,符合AngularJS最佳实践)
AngularJS的服务是单例模式,整个应用中只会实例化一次,非常适合存储这种全局状态,还能复用数据:
第一步:创建单例服务
aldro_app.service('BillDataService', function($http) { var hasFetched = false; var cachedBillData = null; this.fetchLatestBill = function(contractId) { if (!hasFetched) { var url = "http://api/url"; return $http({ url: url, method: 'POST', data: { id: contractId } }).then(function(response) { cachedBillData = response.data; hasFetched = true; return cachedBillData; }); } // 如果已请求过,直接返回缓存的数据 return Promise.resolve(cachedBillData); }; });
第二步:在控制器中依赖该服务
aldro_app.controller('ctrInicio', function ($scope, BillDataService) { var id = parseInt(getVariable("idcontrato")); // 调用服务方法,请求只会执行一次 BillDataService.fetchLatestBill(id).then(function(data) { // 将数据绑定到视图 $scope.billData = data; }).catch(function(error) { console.error('获取账单失败:', error); }); });
这种方式不仅能保证请求仅执行一次,还能缓存结果,其他控制器需要相同数据时也可以直接复用,完全符合模块化思想。
方案3:使用本地存储(适合页面刷新后仍保持状态的场景)
如果希望即使页面刷新,请求也不会重复执行,可以用localStorage或者sessionStorage:
aldro_app.controller('ctrInicio', function ($scope, $http) { // 加上合同ID作为键的后缀,避免不同合同的状态冲突 var requestFlagKey = 'hasFetchedBill_' + getVariable("idcontrato"); var hasFetched = localStorage.getItem(requestFlagKey) === 'true'; if (!hasFetched) { var id = parseInt(getVariable("idcontrato")); var url = "http://api/url"; $http({ url: url, method: 'POST', data: { id: id } }).then(function(response) { $scope.billData = response.data; // 标记请求已完成 localStorage.setItem(requestFlagKey, 'true'); // 可选:缓存数据到本地,下次直接读取 localStorage.setItem('cachedBill_' + id, JSON.stringify(response.data)); }).catch(function(error) { console.error('请求失败:', error); }); } else { // 从本地缓存读取已获取的数据 var id = parseInt(getVariable("idcontrato")); $scope.billData = JSON.parse(localStorage.getItem('cachedBill_' + id)); } });
方案4:路由resolve(如果用路由管理视图)
如果你用的是ngRoute或者ui-router,可以在路由配置中通过resolve预先加载数据,这样数据只会在路由首次激活时获取一次:
以ngRoute为例:
aldro_app.config(function($routeProvider) { $routeProvider.when('/inicio', { templateUrl: 'inicio.html', controller: 'ctrInicio', resolve: { latestBill: function($http) { var id = parseInt(getVariable("idcontrato")); var url = "http://api/url"; // 这里的请求只会在首次进入该路由时执行 return $http({ url: url, method: 'POST', data: { id: id } }).then(function(response) { return response.data; }); } } }); }); // 控制器中直接注入resolve返回的结果 aldro_app.controller('ctrInicio', function ($scope, latestBill) { $scope.billData = latestBill; // 后续视图逻辑直接使用数据即可 });
这种方式的好处是,数据加载完成后才会渲染视图,避免页面出现空数据的情况。
内容的提问来源于stack exchange,提问作者Jaime Cuellar
相关产品推荐
相关产品推荐

