I am going through a series o documentation to understand services and factories.
Came across this working code.
var app = angular.module('plunker', []);
app.value('cnt', 7);
app.service('foo', function(cnt) {
this.cnt = ++cnt;
this.inc = function(quan) {
this.cnt += quan;
};
});
app.controller('MainCtrl', function($scope, cnt, foo) {
$scope.cnt = cnt;
$scope.foo = foo;
$scope.inc = function() {
$scope.foo.inc(1); // why $scope used here
};
});
app.controller('TangentCtrl', function($scope, foo) {
$scope.jump = function() {
foo.inc(5); // Why $scope not used here and why it doesnot work when I add scope
};
});
In TangentCtrl controller $scope.jump function its not using $scope to access foo.inc as you can see in the code I have commented.
I think there is some concept here that I dont understand , can anyone enlighten me to this.
var app = angular.module("myApp",[]);
function constructorFunction() {
this.getData = function() {
//bussiness logic
};
}
/*
* your are registering service called myService
* service/factory uses singleton design pattern
* i.e. you have an object called myService in app
*/
app.service('myService', constructorFunction);
/*
* Here, you are passing sevice name (myService) and
* constructor Function (constructorFunction) to service
* provider
* which creates singleton object (myService)
*/
/*
* angular uses injector to resolve dependencies
* your are actully tells injector to
* add these dependencies to your controller function
*/
app.controller('myCtrl',function($scope, myService){
/*
* Here, you get $scope and myService
* $scope and myService these are two different objects.
* It is not yet compulsory to inject $scope if you
* use controllerAs syntax, good practice
*/
//view specific logic
});
/*
* Note: array syntax is useful in case of minification
* ['$scope', 'foo', function($scope, foo){}]
* angular maps minified variables with strings provided
* to resolve dependencies.
*/
That is because you have injected foo while declaring the controller
app.controller('TangentCtrl', function($scope, foo) {...}
In the controller function, you get instance of the foo service.
Ideally you should write the controller as below. So when you get instance of the service within controller itself, why you need $scope to access the inc function?
app.controller('TangentCtrl', ['$scope', 'foo', function($scope, foo) {
....
}]);
Look this code
app.service('foo', function(cnt) {
this.cnt = ++cnt;
this.inc = function(quan) {
this.cnt += quan;
};
});
Image you have a class 'foo', in this class 'inc' is a function, you are exporting 'foo' as a service, in the above 'foo' can be used as a connection between two controllers to pass some data between them.
So you are just Injecting foo in 'TangentCtrl' via this line
app.controller('TangentCtrl', function($scope, foo) { ...... });
So since you can use 'foo' without $scope in front them, so foo.inc(5); will call the method inc inside foo service and thus foo can be called by other controllers to get the value.
Related
I have recently posted a similar question, but this is not a duplicate.
Apologies for the code heavy post but I wanted to provide as much context as possible. I am having an issue with defining the analytics tool, 'Amplitude' as a service in my Angular.js application. Services are supposed to act as singletons throughout an application (source), so I am confused to be getting the following behavior.
In my app.js file, I call AmplitudeService.logEvent('EVENT_NAME') in a .run function which successfully logs the event to Amplitude. Note: Console.log(AmplitudeService) returns an object with all the correct functions here.
However, when I call AmplitudeService.logEvent('EVENT_NAME') within any other controller, such as header.js I do not ever see any data in my Amplitude dashboard. Note: Console.log(AmplitudeService) returns an identical object within header.js to the one returned from app.js
Would appreciate any and all insight!
P.S. The official AmplitudeJS SDK is here. I am trying to implement it through this wrapper.
AmplitudeService.js (source)
Note: If you check the author's syntax, he returns an object at the end of his service. In my research, I've read to use the "this" keyword when defining Service functions (source), and that you don't need to return an object as you would with a Factory, so I have updated it accordingly.
angular.module('AmplitudeService', [])
.service('AmplitudeService',
['$amplitude', '$rootScope', 'amplitudeApiKey', '$location',
function ($amplitude, $rootScope, amplitudeApiKey, $location) {
this.init = function() {
$amplitude.init(amplitudeApiKey, null);
}
this.identifyUser = function(userId, userProperties) {
$amplitude.setUserId(userId);
$amplitude.setUserProperties(userProperties);
}
this.logEvent = function(eventName, params) {
$amplitude.logEvent(eventName, params);
}
}]);
angular-amplitude.js (source)
This allows access to "$amplitude" throughout the application
(function(){
var module = angular.module('angular-amplitude', ['ng']);
module.provider('$amplitude', [function $amplitudeProvider() {
this.$get = ['$window', function($window) {
(function(e,t){
var r = e.amplitude || {};
var n = t.createElement("script");
n.type = "text/javascript";
n.async = true;
n.src = "https://d24n15hnbwhuhn.buttfront.net/libs/amplitude-2.2.0-min.gz.js";
var s = t.getElementsByTagName("script")[0];
s.parentNode.insertBefore(n,s);
r._q = [];
function a(e){
r[e] = function(){
r._q.push([e].concat(Array.prototype.slice.call(arguments,0)));
}
}
var i = ["init","logEvent","logRevenue","setUserId","setUserProperties","setOptOut","setVersionName","setDomain","setDeviceId","setGlobalUserProperties"];
for(var o = 0; o < i.length; o++){
a(i[o])
}
e.amplitude = r
}
)(window,document);
return $window.amplitude;
}];
}]);
return module;
}());
App.js
angular.module('app', [
'ngRoute',
'angular-amplitude',
'AmplitudeService',
])
.run(['AmplitudeService', function(AmplitudeService){
console.log(AmplitudeService); // Outputs 'Object {}'
AmplitudeService.init();
*AmplitudeService.logEvent('LAUNCHED_SITE'); // This logs the event*
console.log(AmplitudeService); // Outputs 'Object {}'
}])
Header.js
angular.module('app.common.header', [])
.controller('HeaderCtrl', [ '$rootScope', '$scope', '$location','$route', '$window', 'AmplitudeService', function($rootScope, $scope, $location, $route, $window, AmplitudeService){
$scope.goToSearch = function(term) {
$location.path('/search/' + term);
console.log(AmplitudeService); // Outputs 'Object {}'
*AmplitudeService.logEvent('SEARCHED');* // This does not log the event
};
}]);
Update: I have tried switching the Service to a Factory and that did not generate any new results.
Found the solution and hope this is helpful to anyone that comes across this. My solution was to initialize the SDK by calling AmplitudeService.init() within a .run() function within app.js, which initialized an amplitude object within my window. From there forward, I included $window as a service in each of my controllers and called $window.amplitude.logEvent('event_name_here');
Feel free to contact if you have questions. Thanks.
We had similar issues on a large project and we created a wrapper providing a directive to init Amplitude and a service to provide the logEvent and sendUserId.
how to inject a $rootScope into the factory definition for the following code (so the file can be minified):
(function() {
var signalRService = function($rootScope) {
// ...
};
var app = angular.module("App");
app.factory("signalRService", signalRService);
}());
The way to go about this is by supplying service definition as an array:
var signalRService = ['$rootScope', function($rootScope) {
// ...
}];
Argument will be minifed, but the injector will be based off the string in the array.
That said I'd suggest to revise whether this is really the way to go, as generally speaking relying on $rootScope is inadvisable, and so is using any kind of scopes directly in services/factories.
I don't know what it is about injecting factories, but I am having the most difficult time.
I've simulated what I'm attempting to do via this sample plunk http://plnkr.co/edit/I6MJRx?p=preview, which creates a kendo treelist - it works fine.
I have an onChange event in script.js which just writes to the console. That's also working.
My plunk loads the following:
1) Inits the app module, and creates the main controller myCtrl (script.js)
2) Injects widgetLinkingFactory int myCtrl
3) Injects MyService into widgetLinkingFactory
The order in which I load the files in index.html appears to be VERY important.
Again, the above plunk is NOT the real application. It demonstrates how I'm injecting factories and services.
My actual code is giving me grief. I'm having much trouble inject factories/services into other factories.
For example,
when debugging inside function linking() below, I can see neither 'CalculatorService' nor 'MyService' services. However, I can see the 'reportsContext' service.
(function () {
// ******************************
// Factory: 'widgetLinkingFactory'
// ******************************
'use strict';
app.factory('widgetLinkingFactory', ['reportsContext', 'MyService', linking]);
function linking(reportsContext, MyService) {
var service = {
linkCharts: linkCharts
};
return service;
function linkCharts(parId, widgets, parentWidgetData) {
// *** WHEN DEBUGGING HERE, ***
// I CANNOT SEE 'CalculatorService' AND 'MyService'
// HOWEVER I CAN SEE 'reportsContext'
if (parentWidgetData.parentObj === undefined) {
// user clicked on root node of grid/treelist
}
_.each(widgets, function (wid) {
if (wid.dataModelOptions.linkedParentWidget) {
// REFRESH HERE...
}
});
}
}
})();
A snippet of reportsContext'service :
(function () {
'use strict';
var app = angular.module('rage');
app.service('reportsContext', ['$http', reportsContext]);
function reportsContext($http) {
this.encodeRageURL = function (sourceURL) {
var encodedURL = sourceURL.replace(/ /g, "%20");
encodedURL = encodedURL.replace(/</g, "%3C");
encodedURL = encodedURL.replace(/>/g, "%3E");
return encodedURL;
}
// SAVE CHART DATA TO LOCAL CACHE
this.saveChartCategoryAxisToLocalStorage = function (data) {
window.localStorage.setItem("chartCategoryAxis", JSON.stringify(data));
}
}
})();
One other point is that in my main directive code, I can a $broadcast event which calls the WidgetLinking factory :
Notice how I'm passing in the widgetLinkingFactory in scope.$on. Is this a problem ?
// Called from my DataModel factory :
$rootScope.$broadcast('refreshLinkedWidgets', id, widgetLinkingFactory, dataModelOptions);
// Watcher setup in my directive code :
scope.$on('refreshLinkedWidgets', function (event, parentWidgetId, widgetLinkingFactory, dataModelOptions) {
widgetLinkingFactory.linkCharts(parentWidgetId, scope.widgets, dataModelOptions);
});
I am wasting a lot of time with these injections, and it's driving me crazy.
Thanks ahead of time for your assistance.
regards,
Bob
I think you might want to read up on factories/services, but the following will work:
var app = angular.module('rage')
app.factory('hi', [function(){
var service = {};
service.sayHi = function(){return 'hi'}
return service;
}];
app.factory('bye', [function(){
var service = {};
service.sayBye = function(){return 'bye'}
return service;
}];
app.factory('combine', ['hi', 'bye', function(hi, bye){
var service = {};
service.sayHi = hi.sayHi;
service.sayBye = bye.sayBye;
return service;
}];
And in controller...
app.controller('test', ['combine', function(combine){
console.log(combine.sayHi());
console.log(combine.sayBye());
}];
So it would be most helpful if you created a plunk or something where we could fork your code and test a fix. Looking over your services it doen't seem that they are returning anything. I typically set up all of my services using the "factory" method as shown below
var app = angular.module('Bret.ApiM', ['ngRoute', 'angularFileUpload']);
app.factory('Bret.Api', ['$http', function ($http: ng.IHttpService) {
var adminService = new Bret.Api($http);
return adminService;
}]);
As you can see I give it a name and define what services it needs and then I create an object that is my service and return it to be consumed by something else. The above syntax is TypeScript which plays very nice with Angular as that is what the Angular team uses.
I don't really understand how to inherit controller objects in angular (not using $scope), when we're surrounding them in a IIFE.
So assume we have 2 controller files:
Animal.js
(function() {
'use strict';
angular.module('myApp').controller('animalCtrl', function() {
this.strength = 3;
this.speed = 3;
});
})();
Beaver.js
(function() {
'use strict';
angular.module('myApp').controller('beaverCtrl', function() {
//How do I inherit the parent controllers stats?
});
})();
How can I inherit from animalCtrl using my beaverCtrl?
The best way to share data on any angularjs site is by using services. In this case I would create a factory that will store strength and speed and share it with beaverCtrl.
angular.module('<yourappname>', [])
.factory('dataService', function() {
$scope = this;
$scope.strength = null;
$scope.speed = null;
$scope.setStrength = function(value) {
$scope.strength = value;
}
$scope.setSpeed = function(value) {
$scope.speed = value;
}
return $scope;
})
Then in the controllers you would call the service like this
.controller('animalCtrl', function(dataService) {
this.strength = 3;
this.speed = 3;
dataService.setStrength(this.strength);
dataService.setSpeed(this.speed);
});
.controller('beaverCtrl', function(dataService) {
this.strength = dataService.strength;
this.speed = dataService.speed;
});
It's important to realize that an AngularJS controller is just a normal javascript "class". The only catch is invoking the base constructor with dependency injection.
// Create some base class.
BaseController = function($scope, $route) {
// Do some stuff here.
};
// The base class has some behavior.
BaseController.prototype.someMethod = function() { /* do something */ };
// Create some child class.
ChildController = function($scope, $injector) {
// Invoke the base constructor.
$injector.invoke(this, BaseController, {$scope: $scope});
};
// Inherit from the base class.
ChildController.prototype = Object.create(BaseController.prototype);
// Add more specific behavior.
ChildController.prototype.someChildMethod = function() { /* do something */ };
Then you can register your controller as
angular.module('myApp').controller('ChildController', ChildController);
Keep in mind that using inheritance like this will be problematic if overused. It should only be used for sharing behavior. Additionally, using composition of different "classes" is generally going to be much more flexible than using inheritance.
From a code organization standpoint, I would also recommend that you declare your controllers in one place (one file per controller) and register them onto modules in another place (one file per module).
I am working on an angularJS app and I am trying to stick with the most efficient and widely accepted styles of development in AngularJs.
Currently, I am using this way of declaring my services like so:
app.factory('MyService', function() {
/* ... */
function doSomething(){
console.log('I just did something');
}
function iAmNotVisible(){
console.log('I am not accessible from the outside');
}
/* ... */
return{
doSomething: doSomething
};
});
However, there are numerous examples out there and I am not quite sure which design style to follow. Can someone with extensive knowledge about services explain the reason why a certain style is more relevant than another?
Is what I am doing useful in any way other than restricting the access to certain functions in my service?
I would suggest you layout your factory or service as they do in the angular-seed app, except that app annoyingly only uses value in the services.js boilerplate. However you can adapt the layout they use for controllers, directives and filters.
'use strict';
/* Filters */
angular.module('myApp.filters', []).
filter('interpolate', ['version', function(version) {
return function(text) {
return String(text).replace(/\%VERSION\%/mg, version);
}
}]);
Which means for a service you would do:
'use strict';
/* Services */
angular.module('myApp.filters', []).
service('myservice', ['provider1', 'provider2', function(provider1, provider2) {
this.foo = function() {
return 'foo';
};
}]).
factory('myfactoryprovider', ['myservice', function(myservice) {
return "whatever";
}]);
This has more boilerplate than you absolutely need, but it is minification safe and keeps your namespaces clean.
Than all you have to do is decide which of const, value, factory or service is most appropriate. Use service if you want to create a single object: it gets called as a constructor so you just assign any methods to this and everything will share the same object. Use factory if you want full control over whether or not an object is created though it is still only called once. Use value to return a simple value, use const for values you can use within config.
I don't believe there's an accepted standard but I myself follow this convention:
var myServiceFn = function (rootScope, http) { ... };
...
app.factory('MyService', ['$rootScope', '$http', myServiceFn]);
I feel like this is cleaner than defining the function inline and also allows for proper injection if I ever decide to minify my files. (see http://docs.angularjs.org/tutorial/step_05).
As an example, I've been defining services within modules like so:
angular.module('userModule', [])
.service('userService', function() {
this.user = null;
this.getUser = function() {
return this.user;
};
this.userIsNull = function() {
return (this.user === null);
};
this.signout = function() {
this.user = null;
};
})
I think it is more clear to use the .service rather than the .factory?