I have a service for handle the menu of my application I dont want call from any controller, where is the best place for call my service
my service has a register method
// sample
menuService.register({name: "Person", label: "Person", url: "/persons"});
menuService.register({name: "Company", label: "Companies", url: "/companies"});
is defined like
app.service('MenuService', ['$rootScope', function($r) { /*...*/ }
Note: my service $rootScope.$emit and is listen by a directive and depends of $rootScope and $location
you should use .run block for that, but keep in mind you cannot inject .provider to run block
yourApp.run(function ($rootScope, $location) {
// your code goes herer
});
from docs
Run blocks are the closest thing in Angular to the main method. A run block is the code which needs to run to kickstart the application. It is executed after all of the services have been configured and the injector has been created. Run blocks typically contain code which is hard to unit-test, and for this reason should be declared in isolated modules, so that they can be ignored in the unit-tests.
You could expose the menus as a model in the MenuService.
app.service('MenuService', ['$rootScope', function($r) {
var svcModel = {
menus: []
};
var registerMenu = function(menu) {
svcModel.menus.push(menu);
};
/*...*/
return {
model: svcModel,
register: registerMenu
/*...*/
};
}
And then access that model directly on the directive:
app.directive('menuDirective', ['MenuService', function(MenuService) {
return {
/*...*/
link: function(scope, element, attrs) {
/*...*/
scope.menus = MenuService.model.menus;
}
}]);
Related
I've been with Angularjs a few days and I'm struggling with a few aspects of it. I'll do my best to try and explain what the issue is, and I'd really appreciate any help anyone can give me about it.
My situation (simplified) is this:
I have a service which loads some info from a json and stores it in an object. It also have some functions to be used for other controllers to retrieve that information.
var particServices = angular.module('particServices', []);
particServices.service('particSrv', function() {
var data = {};
this.updateData = function(scope) {
data = // http call, saves in data
}
this.getName = function(code) {
return data.name;
}
});
I have an html page assisted by a controller, which uses a directive board (no params, really simple). This is the controller:
var bControllers = angular.module('bControllers', []);
bControllers.controller('bController', ['$scope', 'particSrv', function ($scope, particSrv) {
$scope.getName = function(code) {
return particSrv.getName(code);
};
particSrv.updateData($scope);
}]);
As you can see, the controller makes the call to initialize the object in the service. As this is a singleton, I understand once that info is loaded no other call needs to be make to updateData and that info is available to others using the getters in the service (getName in this case).
I have a really simple directive board (which I simplified here), which uses another directive bio.
angular.module('tsDirectives', [])
.directive('board', ['dataSrv', 'particSrv', function(dataSrv, particSrv) {
return {
restrict: 'E',
replace: true,
scope: true,
controller: function($scope) {
$scope.getName = function(code) {
return particSrv.getName(code);
};
dataSrv.updateData($scope, 'board', 'U');
},
templateUrl: '<div class="board"><div bio class="name" partic="getName(code)"/></div></div>'
};
}]);
And this is the bio directive:
angular.module('gDirectives', [])
.directive('bio', function() {
return {
scope: {
partic: '&'
},
controller: function($scope) {
$scope.name = $scope.partic({code: $scope.athid});
},
template: '<a ng-href="PROFILE.html">{{name}}</a>'
};
})
Now, what I expected is that in the bio directive the info retrieved from party was displayed, but apparently this directive is processed before the partic is initialized in the main controller.
I was under the impression that even though this information was still not loaded when the directive is processed, as soon as the service finishes and the info is ready, automagically it would appear in my directive, but that does not seem to work like that. I've been reading about $watch and $digest, but I fail to see why (and if) I would need to call them manually to fix this.
Any hint will be much appreciated. I could provide more technical details if needed.
Directive will initialise when app is loaded and user opens the page where that directive is, if you have some property that is set later (from api for example), it will update that property in directive but that directive will not be reinitialised ($scope.partic({code: $scope.athid}) wont be called).
If you want for directive to wait for initialisation you should use ng-if. Something like this:
<div data-directive-name data-some-property="someProperty" data-ng-if="someProperty"></div>
In this case directive will be initialised when (if) you have some value in $scope.someProperty. But this is not very good if you can have false values for someProperty.
In that case you would need to use some kind of loaded flag.
You have not included "particServices" as a dependency in other modules which use the services of "particServices". Your modules should look like:
var bControllers = angular.module('bControllers', ['particServices']);
angular.module('tsDirectives', ['particServices']);
angular.module('gDirectives', ['particServices']);
I'm looking for a way to access both controllers inside the directive.
Currently it's not working and I don't know why or if it's even possible.
If I use both require and controller option, inside the link function ctrl property refers to whatever I requested via the require option.
I can't think of a way to access the controller inside the link function when the require option is present.
It seems that these two properties are mutually exclusive ?
angular.module('moduleName').directive('directiveName', [function () {
return {
controller: 'MediaController',
require:'ngController',
link: function (scope, element, attributes, ctrl) {
// I need access to both controllers here
}
}
}]);
If you want both controllers, then require both controllers:
angular.module('moduleName').directive('directiveName', [function () {
return {
controller: MediaController,
require:['directiveName', 'ngController'],
In this case ctrl is an array containing the two controllers.
Without really knowing why you need to access both controllers, I can only offer minimal advice here. My suggestion would be to create a service to handle cross controller needs. Services are singletons and they support data binding. Services are my preference for cross controller work every day. For example:
App.controller('Ctrl1', function Ctrl1 ($scope, TestService) {
$scope.someValue = TestService.getValue();
});
App.controller('Ctrl2', function Ctrl2 ($scope, TestService) {
$scope.someValue = TestService.getValue();
});
App.factory('TestService', function() {
var myVal = "I Bound";
return {
getValue: function() {
return myVal;
}
}
});
This method allows you to abstract a controllers need to directly access another controller. Your services can be pulled into these directives or other services too. I hope this helps a bit.
Thanks,
Jordan
I'm building a pretty simple app where I have a GlobalController (on body element), and will have another sub-controller below. This is a templated site with multiple, physical pages such that the sub-controller will be different, but at most there will only be a top-level Global one and a single sub-one.
I'm trying to make global functions that any sub-controller can use to run code that each needs to run without having to duplicate the functionality in each sub-controller.
One way I could do this would be to include $rootScope and then emit() messages to the GlobalController who is listening for them using $on().
I gather this is not a "good" way to do this. Rather, I've learned that it's better to use a service for this. I'm now stuck on how to implement this service.
I currently have a Global Controller like so:
var globalModule = angular.module('DoSquareStuff', ["ngRoute","list", "savings-video"]);
// there will be a long list of modules that will be added after "savings-video"
globalModule.factory('squareMgr', function() {
var squares = SUYS.squares; // global obj with earned[] and placed[]
return {
getSquaresEarned: function() {
return squares.earned;
},
getSquaresPlaced: function() {
return squares.placed;
},
setThisSquareEarned: function(value) {
squares.earned.push(value);
},
setThisSquarePlaced: function(value) {
squares.placed.push(value);
},
earnedThisSquare: function(squareNum) {
return ~squares.earned.indexOf(squareNum);
},
placedThisSquare: function(squareNum) {
return ~squares.placed.indexOf(squareNum);
}
}
});
globalModule.controller('GlobalController', function($scope, $squareMgr){
// this would be easy... but it doesn't work
// $rootScope.$on('signal.missionComplete', function (event, missionId) {
// console.log("parentScope receive notice that mission " + missionId + " is complete.");
// });
log('GlobalController loaded');
// log($squareMgr.getSquaresEarned()); //broken
});
Then, reading the docs, I tried:
globalModule.controller('GlobalController', ['squareMgr', function($squareMgr){
// but then how do I get $scope in there?
log('GlobalController loaded');
// log($squareMgr.getSquaresEarned());
}]);
In your last code block, you need to inject $scope as well. You can inject any number of services that you need:
globalModule.controller('GlobalController', ['squareMgr', '$scope',
function($squareMgr, scope){
// but then how do I get $scope in there?
log('GlobalController loaded');
// log($squareMgr.getSquaresEarned());
}]);
And a minor point, I wouldn't put a $ in front of squareMgr, the $ implies it is a built in angular service.
Try
globalModule.controller('GlobalController', ['squareMgr', '$scope', function($scope, squareMgr){ .....
The $ sign is used to differentiate between Angular services and your own
I have two controller in an angularApp with following codes
Controller:Devicectr
function Devicectr($scope, $http) {
$scope.Devices = [{ Id: 1, Devicename: "MasterDeviceA" },
{ Id: 1, Devicename: "MasterDeviceB" },
{ Id: 1, Devicename: "MasterDeviceC" }];
$scope.ActiveDevice = $scope.Devices[0] //
};
Controller:PreviewCtr
function Devicectr($scope, $http) {
$scope.ViewDevice=null; // Here i want to read the $scope.ActiveDevice from **Devicectr** Controller
};
I want read the $scope.ActiveDevice value of Devicectr controller from PreviewCtr controller. How i can do it
Usually you use services when you want to share data between controllers.
Like:
yourModule.factory('yourService', function() {
var sharedData = ...
var getData = function() {
return sharedData;
}
return {
getData : getData
}
}
and you then use this in both your controllers:
function Devicectr($scope, $http, yourService) {...}
function Previewctr($scope, $http, yourService) {...}
You could also nest the controllers to include the same scope, but I would say that is a worse idea.
Create a DeviceService which keeps track of the currently selected device. It should expose setActive(device) and getActive() method.
In your PreviewCtr inject the service to determine the active device. You need to watch for the Device assignment or change
If you assign the service to the scope like
$scope.deviceService=deviceService; you can do something like
$scope.$watch(function(){ return $scope.deviceService.getActive();},function(newValue,oldValue) {
//This gets called when the device change
});
You and also use scope $broadcast method (using $rootScope) in the service to raise an event when the Device changes in setDevice(device) function. You can catch that event any where using $scope.$on handler. See documentation here.
i have been able to get controller to use the $on listener
with $scope.$on.
but i don't see any documentation on how to get services to listen for events.
I tried $rootScope.$on, but that only allows one listener. i want listeners in multiple services regardless of whether their parent controllers are in scope or not.
after experimenting a fair bit it turns out that getting events to the service can be done with minimal code.
sample service code follows in case anyone else runs into this.
The sample saves and restores the service model to local storage when it gets the respective broadcasts
app.factory('userService', ['$rootScope', function ($rootScope) {
var service = {
model: {
name: '',
email: ''
},
SaveState: function () {
sessionStorage.userService = angular.toJson(service.model);
},
RestoreState: function () {
service.model = angular.fromJson(sessionStorage.userService);
}
}
$rootScope.$on("savestate", service.SaveState);
$rootScope.$on("restorestate", service.RestoreState);
return service;
}]);
Since $on is a scope method, you could create a scope in your service, then listen for events on it:
app.factory('myService', function($rootScope) {
var scope = $rootScope.$new(); // or $new(true) if you want an isolate scope
scope.$on('testEvent', function() {
console.log('event received');
})
return {}
});
function MyCtrl($scope, myService, $rootScope) {
$rootScope.$broadcast('testEvent');
}
fiddle
However, I would not recommend this approach, since scopes are not normally associated with services.