Reputation: 3073
I have two controllers, and a shared service. Controller A is the main view for the page, containing an ng-repeat
filled with data. Controller B contains the filtering options for the ng-repeat
in Controller A. This works fairly well, and I've got a basic stripped down example of how I've got this working below.
My question is: How can I introduce much more complicated filtering in controller A? I know I'll need to use a function to do complicated filtering, and therein is the problem.
angular.module('app').factory('optionService', [/*'stuff'*/, options]);
function options(/*stuff*/){
var someOption = false;
var filters = {};
var service = {
someOption: someOption,
filters: filters
}
return service;
}
angular.module('app').controller('controllera', ['dataSvc', 'optionService', ctrla]);
function ctrla(dataSvc, optionService){
var vm = this;
vm.options = optionService;
vm.items = dataSvc.getItems();
}
angular.module('app').controller('controllerb', ['optionService', ctrlb]);
function ctrlb(optionService){
var vm = this;
vm.options = optionService;
vm.toggleSomeOption = function(){
vm.options.someOption = !vm.options.someOption;
if(vm.options.someOption){
vm.options.filters.someProperty = 'foo';
} else {
delete vm.options.filters['someProperty'];
}
}
}
<div ng-controller="controllera as vm">
<ul>
<li ng-repeat="item in vm.items | filter:vm.options.filters">{{item.bar}}</li>
</ul>
</div>
I've tried putting a filtering method into the service, and adding it as the filter in the ng-repeat
, but it doesn't fire when the options are updated.
Should I be using events or the $rootScope
somehow to instead notify controllera that an option in controllerb has changed and it needs to update (e.g. by manually injecting a custom filter and running that)?
All in all I'm a bit confused as to how to get this working, and the best approach to take.
Upvotes: 2
Views: 60
Reputation: 3073
I've managed to implement this using $rootScope.$broadcast
and $scope.$on
.
In controller B, when the option is selected (it's a checkbox
), I toggle the option stored in the service, and broadcast an event to say that the options have been updated. In controller A, the event is listened for, and the filter which is passed in as a dependency is applied and run against the complex filter method, which is just a bunch of if/elses (although that will get refactored out to somewhere else shortly).
angular.module('app').factory('optionService', [/*'stuff'*/, options]);
function options(/*stuff*/){
var someOption = false;
var service = {
someOption: someOption
}
return service;
}
angular.module('app').controller('controllera', ['dataSvc', '$scope', 'filterFilter', 'optionService', ctrla]);
function ctrla(dataSvc, optionService){
var vm = this;
vm.options = optionService;
vm.items = dataSvc.getItems();
vm.filtereditems = [];
function complexFilter(item){
if(item.foo === 'bar' && vm.options.someOption){
return true;
}
/* etc etc */
}
function runFilter(){
vm.filtereditems = filterFilter(vm.items, complexFilter);
}
$scope.$on('updated', function(){
runFilter();
};
}
angular.module('app').controller('controllerb', ['optionService', '$rootScope', ctrlb]);
function ctrlb(optionService, $rootScope){
var vm = this;
vm.options = optionService;
vm.toggleSomeOption = function(){
vm.options.someOption = !vm.options.someOption;
$rootScope.$broadcast('updated');
}
}
<div ng-controller="controllera as vm">
<ul>
<li ng-repeat="item in vm.filtereditems">{{item.bar}}</li>
</ul>
</div>
Upvotes: 0
Reputation: 6299
Yes, I think the update is not happening because when the scope of ControllerB changes but angular does not know that it should check the scope of ControllerA as well.
You can start messing with $scope.$apply()
or $scope.$digest()
but they are very ugly and far from being "best practice". I'd rather recommend using a $timeout(func, 0)
because that also leads to dirty check of the scope. So the process:
$timeout()
. This way its scope will be aware of changes$timeout()
executes immediately and updates the scope of ControllerAUpvotes: 1