我们如何在不关闭控制器的情况下从resolve函数访问数据?
我们目前正在开发一个使用angular-ui-router
的项目。
我们有两个单独的视图:左边是父元素列表,右边是元素子数据。
如果选择左侧的父级,我们会将其子级数据解析为右侧的子视图。
目标是不对子控制器(和视图)进行自动加载,在选择不同的父元素时,我们设置notify:false
。
我们设法在不重新加载控制器和视图的情况下“重新解析”子控制器数据,但数据(范围)不会刷新。
我们做了一个小傻瓜来证明我们的问题here
首先单击一个数字以实例化控制器childCtrl
。每次跟随点击都应该更改子范围数据 - 这不起作用。
您可能会注意到alert
输出已经包含我们要显示的刷新数据。
答案 0 :(得分:2)
基于使用特殊服务的sielakos答案,我提出了这个解决方案。 首先,我需要一个额外的服务,保持resovle数据的参考。
<强>服务强>
.service('dataLink', function () {
var storage = null;
function setData(data) {
storage = data;
}
function getData() {
return storage;
}
return {
setData: setData,
getData: getData
};
})
好吧,我必须在我的解析函数中使用该服务,如此
解析功能
resolve: {
detailResolver: function($http, $stateParams, dataLink) {
return $http.get('file' + $stateParams.id + '.json')
.then(function(response) {
alert('response ' + response.data.id);
dataLink.setData(response.data);
return response.data;
});
}
}
注意第dataLink.setData(response.data);
行。它保留了服务中的解析数据,因此我可以从控制器中访问它。
<强>控制器强>
我稍微修改了控制器。我将所有初始化后缀包装在我可以在数据更改时执行的函数中。
第二件事是观察dataLink.getData();
截至https://docs.angularjs.org/api/ng/type/ $ rootScope.Scope#$ watch $ scope。$ watch提供观看函数返回值的功能。
以下是一些Q&amp; D示例:
.controller('childCtrl', function($scope, $log, detailResolver, $interval, dataLink) {
initialise();
/*
* some stuff happens here
*/
$interval(function() {
console.log(detailResolver.id)
}, 1000);
$scope.$watch(dataLink.getData, function(newData) {
detailResolver = newData;
initialise();
});
function initialise() {
$log.info('childCtrl detailResolver.id == ' + detailResolver);
$scope.id = detailResolver;
}
})
行$scope.$watch(dataLink.getData, function(newData) { ... });
可以解决问题。每次dataLink服务中的数据发生更改时,回调就会启动,并用新数据替换旧数据。
我创建了一个plunker,所以你可以尝试https://plnkr.co/edit/xyZKQgENrwd4uEwS9QIM
使用此解决方案您不必担心内存泄漏,因为角度会自动删除观察者。有关详细信息,请参阅https://stackoverflow.com/a/25114028/6460149。
答案 1 :(得分:1)
不太好,但工作解决方案是使用事件。好吧,也许它不是那么糟糕,至少它并不复杂。 https://plnkr.co/edit/SNRFhaudhsWLKUNMFos6?p=preview
angular.module('app',[
'ui.router'
])
.config(function($stateProvider) {
$stateProvider.state('parent', {
views:{
'parent':{
controller: 'parentCtrl',
template: '<div id="parent">'+
'<button ng-click="go(1)">1</button><br>'+
'<button ng-click="go(2)">2</button><br>'+
'<button ng-click="go(3)">3</button><br>'+
'</div>'
},
},
url: ''
});
$stateProvider.state('parent.child', {
views:{
'child@':{
controller: 'childCtrl',
template:'<b>{{ id }}</b>'
}
},
url: '/:id/child',
resolve: {
detailResolver: function($http, $stateParams, $rootScope) {
return $http.get('file'+$stateParams.id+'.json')
.then(function(response) {
alert('response ' + response.data.id);
$rootScope.$broadcast('newData', response.data);
return response.data;
});
}
}
});
})
.controller('parentCtrl', function ($log, $scope, $state) {
$log.info('parentCtrl');
var notify = true;
$scope.go = function (id) {
$state.go('parent.child', {id: id}, {notify:notify});
notify = false;
};
})
.controller('childCtrl', function ($scope, $log, detailResolver, $interval) {
/*
* some stuff happens here
*/
$log.info('childCtrl detailResolver.id == ' + detailResolver);
$scope.$on('newData', function (event, detailResolver) {
$scope.id = detailResolver;
});
$scope.id = detailResolver;
$interval(function(){
console.log(detailResolver.id)
},1000)
})
;
编辑: 一个更复杂的解决方案,需要将promise创建函数更改为observables,但有效: https://plnkr.co/edit/1j1BCGvUXjtv3WhYN84T?p=preview
angular.module('app', [
'ui.router'
])
.config(function($stateProvider) {
$stateProvider.state('parent', {
views: {
'parent': {
controller: 'parentCtrl',
template: '<div id="parent">' +
'<button ng-click="go(1)">1</button><br>' +
'<button ng-click="go(2)">2</button><br>' +
'<button ng-click="go(3)">3</button><br>' +
'</div>'
},
},
url: ''
});
$stateProvider.state('parent.child', {
views: {
'child@': {
controller: 'childCtrl',
template: '<b>{{ id }}</b>'
}
},
url: '/:id/child',
resolve: {
detailResolver: turnToObservable(['$http', '$stateParams', function($http, $stateParams) { //Have to be decorated either be this or $inject
return $http.get('file' + $stateParams.id + '.json')
.then(function(response) {
alert('response ' + response.data.id);
return response.data;
});
}])
}
});
})
.controller('parentCtrl', function($log, $scope, $state) {
$log.info('parentCtrl');
var notify = true;
$scope.go = function(id) {
$state.go('parent.child', {id: id}, {notify: notify});
notify = false;
};
})
.controller('childCtrl', function($scope, $log, detailResolver, $interval) {
/*
* some stuff happens here
*/
$log.info('childCtrl detailResolver.id == ' + detailResolver);
detailResolver.addListener(function (id) {
$scope.id = id;
});
});
function turnToObservable(promiseMaker) {
var promiseFn = extractPromiseFn(promiseMaker);
var listeners = [];
function addListener(listener) {
listeners.push(listener);
return function() {
listeners = listeners.filter(function(other) {
other !== listener;
});
}
}
function fireListeners(result) {
listeners.forEach(function(listener) {
listener(result);
});
}
function createObservable() {
promiseFn.apply(null, arguments).then(fireListeners);
return {
addListener: addListener
};
}
createObservable.$inject = promiseFn.$inject;
return createObservable;
}
function extractPromiseFn(promiseMaker) {
if (angular.isFunction(promiseMaker)) {
return promiseMaker;
}
if (angular.isArray(promiseMaker)) {
var promiseFn = promiseMaker[promiseMaker.length - 1];
promiseFn.$inject = promiseMaker.slice(0, promiseMaker.length - 1);
return promiseFn;
}
}
答案 2 :(得分:1)
1)对于当前任务,不需要ng-view(恕我直言)。如果您需要两个不同的范围,则重新设计ng-views以使用自己的控制器成为指令。这将阻止角度重新加载
2)如果你需要在范围之间共享数据,那么服务可以用来存储数据(参见the following code中的helperService)
3)如果我们谈论当前的代码简化,那么可以这样做:使用2)中的服务并只使用一个控制器:
(function() {
angular.module('app',[
'ui.router'
]);
})();
(function() {
angular
.module('app')
.service('helperService', helperService);
helperService.$inject = ['$http', '$log'];
function helperService($http, $log) {
var vm = this;
$log.info('helperService');
vm.data = {
id: 0
};
vm.id = 0;
vm.loadData = loadData;
function loadData(id) {
vm.id = id;
$http
.get('file'+id+'.json')
.then(function(response) {
alert('response ' + response.data.id);
vm.data = response.data;
});
}
}
})();
(function() {
angular
.module('app')
.controller('AppController', ParentController);
ParentController.$inject = ['helperService', '$log'];
function ParentController(helperService, $log) {
var vm = this;
$log.info('AppController');
vm.helper = helperService;
}
})();
4)不需要间隔,观看,广播等
完整代码在此处:plunker