angular factory dependency injection - angularjs

I'm trying to move some generic navigation formatting code from the controller into a factory.
Do I need to inject $scope into my factory? I've tried six ways from Sunday to inject $scope, but every method I've tried gives me errors.
Or do my stagesHeight, stagesWidth variables in the factory need scoping at all?
controller:
angular.module('sysomos.ads').
controller('LinkController', ['$scope', '$state', '$api', 'LinkFactory',
function($scope, $state, $api, LinkFactory) {
console.log(LinkFactory.make(['twitter', 'ad', 'view']));
}
]);
factory:
angular.module('sysomos.ads').
factory('LinkFactory', function(){
return {
make: function(arrSteps){
$scope.stagesHeight = 30;// what scope does my logic need?
$scope.stagesWidth = 145;
// lots of intervening steps
return arrSteps.join(",");// just return me the array for now
};
}
]);

Try something like this:
.factory('LinkFactory', function(){
return {
make: function(arrSteps){
var stagesHeight = 30;// what scope does my logic need?
var stagesWidth = 145;
// lots of intervening steps
return arrSteps.join(",");// just return me the array for now
};
}
Then call your service/factory from your ctrl like this:
$scope.foo.something = LinkFactory.make();
I hope this helps.

Related

In AngularJS, why isn't a $scope variable accessible from a simple function?

My code is as shown below:
angular.module('xyz', [])
.controller('listController', ['$scope',
function($scope) {
$scope.deliveryOptions = 0;
var vm = this;
function doTransaction() {
console.log('delivery options is ' + vm.$scope.deliveryOptions);
}
}
]);
Here inside console.log(delivery options), it gives me error that it is Unable to get property 'deliveryOptions' of undefined or null reference. So how can I access that variable?
The problem is that the $scope is not defined on the vm object.
To solve this you should either define everything on the $scope object like this
angular.module('xyz', [])
.controller('listController', ['$scope',
function($scope) {
$scope.deliveryOptions = 0;
$scope.doTransaction = function() {
console.log('delivery options is ' + $scope.deliveryOptions);
}
}
]);
Or on the vm object
angular.module('xyz', [])
.controller('listController',
function() {
var vm = this;
vm.deliveryOptions = 0;
vm.doTransaction = function() {
console.log('delivery options is ' + vm.deliveryOptions);
}
}
);
Mixing vm and $scope is not the best way to go
Use either $scope or controller as, not both
The primary issue is you're mixing original AngularJS 1.x $scope usage, with the controller as design pattern that uses vm (meaning "view model"), which started with Angular 1.5.
You need to pick one or the other, and then your code will work just fine.
If you go with the cleaner and less error-prone vm and controller as design pattern, you simply use vm rather than $scope:
angular.module('xyz', [])
.controller('listController', function() {
var vm = this
vm.deliveryOptions = 0
vm.doTransaction = function() {
console.log('delivery options is ' + vm.deliveryOptions)
}
})
And then in your view, you use vm.doTransaction() to call your function and display your variable.

Sharing variable globally

In my main controller I have a user service that returns the current user.
// Curr user
userService.getCurrentUser().then(user => {
mainCtrl.currUser = user;
});
Is there any way to be able to use the currUser variable in other controllers without injecting my service and calling this method over and over again in maybe 50 controllers?
Ex.
// My other awesome controller
console.log(currUser.fullName);
You could go for a LocalStorage or $rootScope. , if you are sure about not using Providers/Services
Sample:
myApp.controller('DemoController1', ['$scope', '$rootScope', function DemoController($scope,$rootScope) {
$rootScope.currUser.fullName ="test";
}]);
Then access it as,
myApp.controller('DemoController2', ['$scope', '$rootScope', function DemoController($scope,$rootScope) {
var fullName = $rootScope.currUser.fullName;
}])
you can use $rootScope like bellow:
mainCtrl.$rootScope.currUser = user;
and the other controller, you can recover this as bellow:
console.log(yourContrl.$rootScope.currUser.fullName);
You don't want you use controller injections? Use injector its the same like injections but also different :D ... like in this runnable fiddle demo. In that way you still can use services, factories, or components.
var myApp = angular.module('myApp',[]);
myApp.controller('MyCtrl', function ($scope) {
$scope.name = angular.injector(['data']).get('user').currUser.username;
});
angular.module('data', []).factory('user', function () {
return {
currUser: {
username: 'Alfred'
}
}
});

AngularJS Amplitude Service Not Acting as Singleton

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.

Angular Variable/Function Global Scope

I have a variable that i use almost everywhere in my app, in many different controllers.
I'm looking for the best possible way of setting that variable knowing that :
-The variable will need to get updated from a controller and resulting in an update on the whole app ( other controllers mainly).
-Some of those function are instance of an object with function in itself that have a callback , does that cause any issue?
So far there seems to be 2 way of doing that :
rootScope but that rarely advised apparently:
myApp.run(function ($rootScope) {
$rootScope.var = "string";
});
And building a custom directive ( but what about setting it's variable ?)
angular.module('myApp').factory('test', function() {
return {
test : 'string'
};
});
Can anyone point me in the right direction and help me choose betweem the two?
I would recommend using a service or factory save that value in someService.variable and the broadcast n event that this value has been changed. I am attaching sample code. It may contain some syntactical error but I want to give you an idea
angular.module("myapp")
.controller('myCtrl', function($scope, $rootScope, myService) {
$scope.change = function() {
myService.var = 'abc';
$rootScope.broadcast('varChanged');
};
})
.controller('myOtherCtrl', function($scope, $rootScope, myService) {
$rootScope.on('varChanged', function(e) {
use(myService.var);
});
})
.service('mySerice', function() {
this.var = '';
});

How to reuse one controller for 2 different views?

I have defined one controller, and apply it to 2 views with small differences.
Angular code:
app.controller('MyCtrl', function($scope) {
$scope.canSave = false;
$scope.demo = {
files : [{
filename: 'aaa.html',
source: '<div>aaa</div>'
}, {
filename: 'bbb.html',
source: '<div>bbb</div>'
}]
}
$scope.newFile = function(file) {
$scope.demo.files.push(file);
}
$scope.$watch("demo.files", function(val) {
$scope.canSave = true;
}, true);
});
View 1:
<div ng-controller="MyCtrl"></div>
View 2:
<div ng-controller="MyCtrl"></div>
The sample code is very simple, but there are a lot of code and logic in my real project.
The View 1 and 2 have almost the same features, only with a few differences, but I do need to write some code for each of them in the controller.
I don't want to create 2 different controllers for them, because they have most of same logic. I don't want to move the logic to a service to share it between the 2 controllers, because the logic is not that common to be a service.
Is there any other way to do it?
Under the given conditions I might be doing something like
function MyCommonCtrl(type){
return function($scope, $http) {
$scope.x = 5;
if(type = 't1'){
$scope.domore = function(){
}
}
....
....
}
}
angular.module('ng').controller('Type1Ctrl', ['$scope', '$http', MyCommonCtrl('t1')]);
angular.module('ng').controller('Type2Ctrl', ['$scope', '$http', MyCommonCtrl('t2')]);
Then
<div ng-controller="Type1Ctrl"></div>
and
<div ng-controller="Type2Ctrl"></div>
I don't know your specific set-up but your 2 controllers could inherit from a common ancestor.
Type1Ctrl.prototype = new MyCtrl();
Type1Ctrl.prototype.constructor = Type1Ctrl;
function Type1Ctrl() {
// constructor stuff goes here
}
Type1Ctrl.prototype.setScope = function() {
// setScope
};
Type2Ctrl.prototype = new MyCtrl();
Type2Ctrl.prototype.constructor = Type2Ctrl;
function Type2Ctrl() {
// constructor stuff goes here
}
Type2Ctrl.prototype.setScope = function() {
// setScope
};
I also faced similar problem and scope inheritance solved my problem.
I wanted to "reuse" a controller to inherit common state/model ($scope) and functionality (controller functions attached to $scope)
As described in the "Scope Inheritance Example" I attach parent controller to an outer DOM element and child controller to the inner. Scope and functions of parent controller "merge" seamlessly into the child one.
Here is another option. Slightly modified from this blog post
app.factory('ParentCtrl',function(){
$scope.parentVar = 'I am from the parent'
};
});
app.controller('ChildCtrl', function($scope, $injector, ParentCtrl) {
$injector.invoke(ParentCtrl, this, {$scope: $scope});
});
here is a plunker

Resources