I have two controllers
app.controller('TestCtrl1', ['$scope', function ($scope) {
$scope.save = function () {
console.log("TestCtrl1 - myMethod");
}
}]);
app.controller('TestCtrl2', ['$scope', function ($scope) {
$scope.var1 = 'test1'
$scope.save = function () {
console.log("TestCtrl1 - myMethod");
}
}]);
Then i have two services
.service('Service1', function($q) {
return {
save: function(obj) {
}
}
})
.service('Service2', function($q) {
return {
save: function(obj) {
}
}
})
For my 60% of stuff i just call save on ctrl1 which then called service save method
Now There are cases where before saving i need to do some stuff like chnaging some object parameters different than genral case there i check e,g
if(model == 'User'){
//Here i do this (sample of code)
var service = $injector.get('Service2');
service.save()
Now my problem is in Service 2 i need access to var1. How can i do that
Use the service(s) itself to share the variable as part of the service object as well as methods of each service
.service('Service2', function($q) {
var self = this;
this.var1 = 'test1';
this.save = function(obj) {
}
});
app.controller('TestCtrl2', ['$scope','Service1','Service2', function ($scope, Service1, Service2, ) {
// bind scope variable to service property
$scope.var1 = Service2.var1;
// add a service method to scope
$scope.save = Service1.save;
// now call that service method
$scope.save( $scope.var1 );
}]);
You can also inject a service into another service if needed
injecting services into other services (one possible method) :
html:
<div id="div1" ng-app="myApp" ng-controller="MyCtrl">
<!--to confirm that the services are working-->
<p>service three: {{serviceThree}}</p>
</div>
js:
angular.module('myApp',[])
.service('s1', function() {
this.value = 3;
})
.service('s2', function() {
this.value = 10;
})
.service('s3', function(s1,s2) { //other services as dependencies
this.value = s1.value+s2.value; //13
})
.controller('MyCtrl', function($scope, $injector) { //$injector is a dependency
$scope.serviceThree = $injector.get('s3').value; //using the injector
});
here's the fiddle: https://jsfiddle.net/ueo9ck8r/
Related
I have the next 'problem' with Angular 1.
I have this Factory that I use to get the data for the current logged user:
angular.module('myModule')
.factory('authFactory', function ($http, $rootScope, Session, api, backend_url) {
var authFactory = this;
var user = {};
authFactory.init = function(){
// This API returns the information of the current user
api.current_user.get({}).$promise.then(function(res){
user = res;
});
}
// I use this function to return the user
authFactory.user = function () {
return user;
};
}
This is a basic Controller example where I'm trying to access the information retrieved by the above factory:
angular.module('myModule.mypage')
.controller('PageCtrl', function ($scope, authFactory) {
$scope.user = authFactory.user();
authFactory.init();
angular.element(document).ready(function () {
// This will return {} because it's called
// before the factory updates user value
console.log(authFactory.user());
console.log($scope.user);
});
});
The problem is that $scope.user = myFactory.user(); is not being updated once the Factory retrieve the user value.
I think my issue is related with myFactory.user();. I'm using a function, so the value returned by the function is not updated after myFactory.user has changed, I think that's why on PageCtrl the variable $scope.user is not getting any value.
My questions are:
Which is the best approach on my controller to wait until the user info is loaded by authFactory ?
Should I use a service instead ?
Problem with your implementation is that user is being initialized when authFactory.init() is invoked using presumably asynchronous API.
I would suggest you to return promise from authFactory.user method.
angular.module('myModule')
.factory('authFactory', function ($http, $rootScope, Session, api, $q, backend_url) {
var authFactory = this;
var user = {};
authFactory.init = function () {
// This API returns the information of the current user
return api.current_user.get({}).$promise.then(function (res) {
user = res;
});
}
//Return promise from the method
authFactory.user = function () {
var deferred = $q.defer();
if (angular.isDefined(user)) {
deferred.resolve(user);
} else {
authFactory.init().then(function () {
deferred.resolve(user);
});
}
return deferred.promise;
};
});
Then modify controller
angular.module('myModule.mypage')
.controller('PageCtrl', function ($scope, authFactory) {
authFactory.user().then(function (user) {
$scope.user = user;
})
});
angular.module('myModule')
.factory('authFactory', function ($http, $rootScope, Session, api, backend_url) {
var authFactory = this;
authFactory.user = {}
// I use this function to return the user
authFactory.getUser() = function () {
return api.current_user.get({}).$promise.then(function(res){
authFactory.user = res;
});
};
}
angular.module('myModule.mypage')
.controller('PageCtrl', function ($scope, authFactory) {
authFactory.getUser().then(function() {
$scope.user = authFactory.user;
});
});
Provide us a JSFiddle, I tried to help you without any testing environment.
I want to run the same line of code to each function a specific service is injected. For instance:
app.service('sameInitService', function ($rootScope) {
this.init = function (scope) {
scope.foo = $rootScope.foo;
}
});
app.controller('oneController', function ($scope, sameInitService) {
sameInitService.init($scope); // <- This line of code
});
app.controller('twoController', function ($scope, sameInitService) {
sameInitService.init($scope); // <- Is the same
});
Is there an angular-ish way to avoid having to write the same line of code when this service is injected?
It can be done by decorating the $controller service but in this case all your controllers will be initialized this way.
angular.module('App', [])
.config(['$provide', function($provide) {
$provide.decorator('$controller', [
'$delegate', 'sameInitService',
function controllerDecorator($delegate, sameInitService) {
return function(constructor, locals) {
sameInitService.init(locals.$scope);
return $delegate(constructor, locals, true);
}
}
]);
}])
See jsfiddle here.
I want to extend on #janusz 's answer and present the code that I actually ended up using, which takes care of the "apply behavior only to controllers with injected dependency":
angular.module('App').decorator('$controller', function ($delegate, sameInitService) {
return function (constructor, locals) {
var controller = $delegate.apply(null, arguments);
return angular.extend(function () {
var controllerInstance = controller()
var DIList = controllerInstance .__proto__.constructor.$inject;
console.log(DIList);
if (DIList.indexOf('sameInitService') !== -1) {
sameInitService.init(locals.$scope);
}
return controllerInstance;
}, controller);
};
});
Here is a modified fiddle
Say I want more than one instance of a user service (selectedUser & currentUser), they provide the same functionality. Currently I just get around the problem by creating two services that pull their definition from a local function:
angular.module('myUserModule', [])
.factory('userFactory', ['$http', '$q', function ($http, $q) {
return function getUser(userId) {
return new $q(function (resolve) {
//Gets the user with the provided userId
}
}
}])
.value('currentUserId', '')
.value('selectedUserId', '')
.service('currentUserService', ['userFactory', 'currentUserId', User])
.service('selectedUserService', ['userFactory', 'selectedUserId', User]);
function User(userFactory, userId) {
var self = this;
var promise = userFactory(userId);
promise.then(function setUserSuccess(result) {
self.user = result;
}
}
Just wondering how other people have approached this problem.
Updated:
Just to clarify that I am making use of these services as singletons too.
In an unrelated question the solution ended up using a factory that creates multiple instances:
what is the scope of a service in angularjs?
Use the Factory Pattern.
In essence, if you have a User service, you can use a factory to return a unique instance of the service.
function User(userId, $http) {
var self = this;
$http.get('/api/user/', {userId: userId}).success(function(result) {
self.details = result.data;
}):
}
userFactory.$inject = ['$http'];
function userFactory($http) {
return function(user) {
return new User(user, $http);
}
}
angular.module('app').factory('user', userFactory);
But for this example, you can just use $resource. This is a built in angular factory that returns unique Resource instances.
You can use use your factory as an API to return a collection of objects with getter and setter methods.
You can declare new Factory by using the new keywords. However, it is preferable to encapsulate the creation of an instance into a getter method.
Service
(function(){
function userFactory($http, $q){
//Create our userFactory
function userFactory(num){
this.num = num;
};
//Retrieve some user data for example
function getUser(id){
return new $q(function(resolve){
resolve({id: id, data: [1,2,3,4]});
});
}
//Another method
function awesome(){
console.log('Awesome method called by : ' + this.num);
}
//Set method by prototyping
userFactory.prototype.getUser = getUser;
userFactory.prototype.awesome = awesome;
return {
//Use get method to return a new instance of our factory
get: function(){
//Pass a random id for our factory
return new userFactory(Math.ceil(Math.random() * 100));
}
}
}
angular
.module('app')
.factory('UserFactory', userFactory);
})();
The you can create your own instance by using the .get() method :
Controller
(function(){
function Controller($scope, UserFactory) {
var factoryA = UserFactory.get()
var factoryB = UserFactory.get();
console.log('A num instance ' + factoryA.num);
console.log('B num instance ' + factoryB.num);
factoryA.getUser(1).then(function(data){
console.log(data);
});
factoryB.awesome();
console.log('Equality = ' + angular.equals(factoryA, factoryB));
}
angular
.module('app', [])
.controller('ctrl', Controller);
})();
(function(){
function Controller2($scope, UserFactory) {
var factoryC = UserFactory.get()
console.log('C num instance ' + factoryC.num);
}
angular
.module('app')
.controller('ctrl2', Controller2);
})();
I want to call a function or change the value of variable/s which is there inside another controller. I looked online for the solution and understood that I have to create a service and use it inside both the controller, however I am not able to understand that how service will have access to $scope.home_main and $scope.home_main variables as they are in different scope.
JS
app.controller('Controller1', function ($scope, $window) {
$scope.toggle = function() {
$scope.home_main = !$scope.home_main;
$scope.full_page_place_holder = !$scope.full_page_place_holder;
};
});
app.controller('Controller2', function ($scope, $window) {
$scope.onTabSelect=function(){
// here I want to call toggle function which is inside another controller.
};
});
Updated HTML
<div ng-controller="Controller1">
<div ng-hide="home_main"></div>
</div>
<div ng-controller="Controller2">
<div ng-hide="full_page_place_holder"></div>
</div>
Looked here: SO-Q1, SO-Q2. Thanks.
you can create a service as follows,
angular.module('someApp').service('shareDataService', function() {
var popup;
var setDetails = function(param) {
popup = param;
};
var getDetails = function() {
return popup;
};
return {
setDetails: setDetails,
getDetails: getDetails,
};
});
This service will not have access to the $scope variables of the two controllers, instead you can call getDetails and setDetails to get and set the variable in the service.
suppose you want to send the value of 'home_main' from controller1 to controller2, in controller1, you call the service function setDetails
app.controller('Controller1', function ($scope, $window, shareDataService) {
$scope.toggle = function() {
$scope.home_main = !$scope.home_main;
$scope.full_page_place_holder = !$scope.full_page_place_holder;
shareDataService.setDetails($scope.home_main);
};
});
and in controller2, you get the value by calling the service
app.controller('Controller2', function ($scope, $window) {
var home_main_value = shareDataService.getDetails();
});
You can do a similar thing for functions also.....
Hope it helps
You misunderstood the concept service will have a single variable that is going to be shared by two controllers:-
$scope is local for controller and cannot accessed by another controller:-
FOR Example:-
myApp.factory('Data', function () {
var data = {
home_main : ''
};
return {
gethome_main : function () {
return data.home_main ;
},
sethome_main : function (home_main ) {
data.home_main = home_main ;
}
};
myApp.controller('FirstCtrl', function ($scope, Data) {
$scope.home_main= '';
$scope.$watch('home_main', function (newValue, oldValue) {
if (newValue !== oldValue) Data.sethome_main(newValue);
});
});
myApp.controller('SecondCtrl', function ($scope, Data) {
$scope.$watch(function () { return Data.gethome_main(); }, function (newValue, oldValue) {
if (newValue !== oldValue) $scope.home_main= newValue;
});
});
I have tens of AngularJS Factories which have a lot in common. So I'm trying to make a base class and subclass it.
But I've noticed that the subclasses are sharing member variables of the base class.
I've made an example on http://jsbin.com/doxemoza/2/edit, the code is also posted here:
HTML:
<!DOCTYPE html>
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.3/angular.min.js"></script>
<meta charset=utf-8 />
<title>JS Bin</title>
</head>
<body ng-app="demo" ng-controller="MainCtrl">
<p>ChildService: {{value1}}</p>
<p>AnotherChildService : {{value2}}</p>
</body>
</html>
JavaScript:
angular.module('demo', []);
var demo = angular.module('demo').controller('MainCtrl', function ($scope, ChildService, AnotherChildService) {
$scope.value1 = ChildService.getPrivateVar();
$scope.value2 = AnotherChildService.getPrivateVar();
});
var Base = function () {
var Service = {};
Service.privateVar = 0;
Service.setPrivateVar = function (value) {
Service.privateVar = value;
}
Service.getPrivateVar = function () {
return Service.privateVar;
}
return Service;
};
demo.factory('BaseService', Base)
demo.factory('ChildService', function (BaseService) {
var ChildService = Object.create(BaseService);
ChildService.setPrivateVar(1);
return ChildService;
});
demo.factory('AnotherChildService', function (BaseService) {
var AnotherChildService = Object.create(BaseService);
AnotherChildService.setPrivateVar(2);
return AnotherChildService;
});
My expected output is:
ChildService: 1
AnotherChildService : 2
But instead I get:
ChildService: 2
AnotherChildService : 2
I think ChildService and AnotherChildService are sharing the same privateVar, so I got the same value for them.
How should I change the code to make them use different instance of privateVar?
Thanks
I had that same problem, and was solved when a declared my BaseService this way:
demo = angular.module('demo', []);
demo.factory('BaseService', function(){
return {
privateVar: 0,
setPrivateVar: function (value) {
this.privateVar = value;
},
getPrivateVar: function () {
return this.privateVar;
}
}
});
My "child" services are like yours. Everything works very fine.
I use something like this:
angular.module('app')
.controller('ParentController', ['$scope', 'dataService', function ($scope, dataService) {
//controller logic here
}])
.controller('ChildController', ['$scope', '$controller', 'SomeDataService', function ($scope, $controller, SomeDataService) {
//extend your parentcontroller
$scope.init = function () {
//do something
}
angular.extend(this, $controller('ParentController', {
$scope: $scope,
dataService: SomeDataService
}));
}])
.factory('BaseDataService', ['logger', function (logger) {
var privateArray = [];
return {
publicFunction: function(){ return privateArray; },
publicVar: "some var"
}
}])
.factory('SomeDataService', ['BaseDataService', function (dataService) {
var service = angular.extend({}, dataService);
service.privateFunction = function () {
//some code
}
service.privateVar= "some value";
return service;
}]);
I combined all of them in this example.
Hope this helps.
Edit: this uses the mixin pattern described in this post