How to handle a global service for different views in angular? - angularjs

Here is my scenario. I want to refresh my captcha if the form get error message. And I have multi views to use the captcha.
So I write a factory:
Services.factory 'Captcha', ['$rootScope', ($rootScope) ->
service = {}
service.new_captcha = () ->
console.log 'render cap'
$rootScope.captcha_src = "/captcha?action=captcha&i=#{+new Date}"
service
]
And then another factory where handle the $http process will trigger the code below
$http
.error (data) ->
service.signin_err_msg = data.error
Captcha.new_captcha()
$rootScope.$broadcast('new_captcha')
In the view controller, $scope value will listen to the broadcast and change the src value.
SignUpCtrl = App.controller 'SignUpCtrl', ($scope, UserService, $location, $rootScope) ->
$scope.UserService = UserService
$scope.$on 'new_captcha', (val) ->
$scope.captcha_src = $rootScope.captcha_src
$scope.captcha_src = $rootScope.captcha_src
This works. But I dont think this is a good way. I have to write the same code to listen the rootScope broadcast. Is there same method better?

As rtcherry says, you don't need to use $rootScope.
Please have a look at this Plunker: http://embed.plnkr.co/4ppfCi/preview

There is no need to use $rootScope or to broadcast any events. This would also work:
Captcha Service
Services.factory('Captcha', [function() {
var captchaSrc;
return {
get_captcha_src: function() {
if (!captchaSrc) {
this.refresh_captcha();
}
return captchaSrc;
},
refresh_captcha: function() {
captchaSrc = "/captcha?action=captcha&i=" + new Date();
}
}
}]);
HTTP Handler
$http.error(function(data) {
service.signin_err_msg = data.error;
Captcha.refresh_captcha();
});
Controller
var SignUpCtrl = App.controller('SignUpCtrl', function($scope, $location, UserService, Captcha) {
...
$scope.get_captcha_src = function() {
return Captcha.get_captcha_src();
}
// or this: $scope.get_captcha_src = Captcha.get_captcha_src;
});

Related

angularjs using $on and $broadcast to communicate between a master controller to another controller

I am trying to set communication between two angular controllers (service is not an option). and I am failing desperately.
here is some of my code...
i tried using both $emit and $broadcast
invoiceApp.controller('masterReportConrtoller', ['$scope', '$location', 'authService', 'usSpinnerService', 'dateService', 'settingsService','$rootScope',
function ($scope, $location, authService, usSpinnerService, dateService, settingsService, $rootScope )
////Is User Valid
////
//$rootScope.$on("masterReportConrtoller", function () {
// $scope.parentmethod();
// });
//$scope.parentmethod = function () {
// //
$scope.masterReportConrtoller.getUserDetails = function () {
debugger;
settingsService.getUserDetails().then(function (response) {
var loginData = {
userName: response.d.user.Email,
password: response.d.user.UserPassword
};
authService.login(loginData).then(function (response) {
debugger;
$scope.Limit = response.d.organization.Limit;
});
$scope.Limit = response.d.organization.Limit;
$scope.DocumentUsage = response.d.organization.DocumentUsage;
$scope.ExpirationDate = $scope.DateConvertfromJson(response.d.organization.ExpirationDate);
var fullDate = new Date();
if (fullDate <= $scope.ExpirationDate) {
$scope.ISvalidUser = false;
$rootScope.$broadcast('masterReportConrtoller', false);
}
else {
$rootScope.$broadcast('masterReportConrtoller', true);
}
});
}
}]);
invoiceApp.controller('InvoiceController', ['$scope', '$location', '$cookieStore', 'documentService', 'dialogs', 'usSpinnerService', 'settingsService', 'associatedEmailsService', '$rootScope',
function ($scope, $location, $cookieStore, documentService, dialogs, usSpinnerService, settingsService, associatedEmailsService, $rootScope) {
$rootScope.$on('masterReportConrtoller');}
Based on your parent - child controller relationship, you can use $scope.$broadcast and $scope.$on in your code.
Try something like this:
//masterReportConrtoller
$scope.$broadcast("myCustomEvent", { isValidUser: false });
//InvoiceController
$scope.$on("myCustomEvent" , function(event, data){
//do something with data
});
Please note that this will work if masterReportConrtoller is the parent controller and InvoiceController is the child controller. If this is not the case, then use $rootScope.$broadcast and $rootScope.$on.
You can find more details here.
You can use $localStorage , $stateParams or $cookies or even ... I generally prefer $stateParams to send values and object to states and controller.
$state.go('state2', { someParam : 'broken magic' });
read file using $stateParams from controller . Details can be found here

Angular calling shared service data update in controller

I am trying to write some very primitive angular code with 2 controllers and 1 service.
So when I call shared service from controller 1 and update data, I want to use same in my controller 2 $scope so that controller 2 $scope value can reflect on my DOM.
App.controller('oneCtrl', function($scope, $uibModal, $log, sharedProperties) {
// Call a new DOM element to so that ModalInstanceCtrl will be called
// Once controller 2 finishes, I want to update a $scope variable here
// $scope.projectList = getProjectList();
});
App.controller('ModalInstanceCtrl', function ($scope, $uibModalInstance, sharedProperties) {
// This is a new modal which uses sharedProperties
// Update setProjectList() in service
});
App.service('sharedProperties', function() {
var projectList = new Array();
return {
getProjectList: function() {
return projectList;
},
setProjectList: function(value) {
projectList.push(value);
},
}
});
Once controller 2 calls setProjectList(). I want to auto update $scope value in controller 1 using getProjectList()
Please let me know how I can do that? Also do let me know if any further details needed on same.
A service in angular is a singleton so if you change data on the service it will be reflected whenever you call that service.
var app = angular.module('plunker', []);
app.controller('FirstCtrl', function($scope, userData) {
$scope.favoriteBook = userData.favoriteBook;
$scope.getFavoriteBook = function(){
$scope.favoriteBook = userData.favoriteBook;
}
});
app.controller('SecondCtrl', function($scope, userData) {
$scope.changeBook = function(){
userData.favoriteBook = 'The Hobbyt';
}
});
app.factory('userData', function(){
var favoriteBook = 'Harry Potter';
return{
favoriteBook : favoriteBook
}
})
Here you got a service that exposes an object, you can change the value of that object in the second controller and see it reflected in the first controller. Call changeBook(), and then getFavoriteBook()
This is the plunker:
the plunker

AngularJS how to get actual factory's data in controller?

I have the factory, when i get socket messages.
How i can get returned factory's actual data in my controller ?
Help please.
app.factory('socket',['$rootScope', function($rootScope) {
connection.open();
var connection = new autobahn.Connection({
url: 'wss://site.com:6555/',
realm: 'realm'
});
var collection = {
'topic1': [],
'topic2': []
};
function onevent(args) {
console.log("Event:", args[0]);
collection.topic1.push(args[0]);
}
connection.onopen = function(session) {
session.subscribe(userid, onevent);
}
return {
collection: collection
}
}]);
The factory cannot push data to a controller, but the controller can pull from the factory. To do so, inject the factory into the controller:
app.controller('yourController', ['$scope', 'socket', function($scope, socket) {
...
$scope.yourControllerCollection = socket.collection;
...
});
If you want the controller to auto-update when the socket factory receives an event and updates the collection, you can always inject the $rootScope into the factory and $emit an event that your controller can listen to. Something like:
app.factory('socket',['$rootScope', function($rootScope) {
...
function onevent(args) {
console.log("Event:", args[0]);
collection.topic1.push(args[0]);
$rootScope.$emit('SocketCollectionUpdated', collection); // Note that you can name your event whatever you want.
}
...
}]);
app.controller('yourController', ['$rootScope', '$scope', 'socket', function($rootScope, $scope, socket) {
...
$scope.yourControllerCollection = socket.collection;
$rootScope.$on('SocketCollectionUpdated', function (event, data) {
$scope.yourControllerCollection = data;
});
...
});
You want to inject the factory in the controller where you want to use the data. Here's a basic example of communicating data from factory to a controller.
app.factory('sharedData', function() {
return {
name: 'Daniel'
};
});
Then in your controller you can simple set this data object from the factory to the $scope.
app.controller('MainController', function($scope, sharedData) {
$scope.data = sharedData;
});
So in your case simply make a controller and inject the sockets factory, like this
app.controller('sockets', function($scope, sockets) {
$scope.collection = collection;
});

Angularjs data binding between controller and service

I'm not able to get the data binding between controller and service working.
I have a controller and a factory which makes an HTTP call. I would like to be able to call the factory method from other services and see the controller attributes get updated. I tried different options but none of them seem to be working. Any ideas would be greatly appreciated.
Please see the code here:
http://plnkr.co/edit/d3c16z?p=preview
Here is the javascript code.
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
$scope.name = 'World';
});
app.controller('EventDetailCtrl', ['$http', 'EventDetailSvc', '$scope',
function ($http, EventDetailSvc, $scope) {
this.event = EventDetailSvc.event;
EventDetailSvc.getEvent();
console.log(self.event);
$scope.$watch(angular.bind(this, function () {
console.log('under watch');
console.log(this.event);
return this.event;
}), function (newVal, oldVal) {
console.log('under watch2');
console.log(newVal);
this.event = newVal;
});
}])
.factory('EventDetailSvc', ['$http', function ($http) {
var event = {};
var factory = {};
factory.getEvent = function() {
$http.get('http://ip.jsontest.com')
.then(function (response) {
this.event = response.data;
console.log('http successful');
console.log(this.event);
return this.event;
}, function (errResponse) {
console.error("error while retrieving event");
})
};
factory.event = event;
return factory;
}]);
It seems to me that you have nested the event object inside of a factory object. You should be returning event directly instead wrapping it with factory. As it stands now you would need to call EventDetailSvc.factory.event to access your object.

Alternatives to angularjs $watch to trigger ui change from service

I'm wondering whether there is a different approach to using $watch in order to achieve the following.
Setup:
ControllerA depends on ServiceA.
ControllerB depends on ServiceB.
Current browser view is showing both controllers.
Scenario:
ControllerA is initiating a function on ServiceA, which in turn changes the value of propery X in ServiceB which should be reflected in the UI of ControllerB.
http://jsfiddle.net/zexscvax/2/
html:
<div>
<div ng-controller="ControllerA"></div>
<div ng-controller="ControllerB">Progress: {{progress}}</div>
</div>
js:
var myApp = angular.module('myApp', []);
myApp.factory('serviceA', ['$q', '$interval', 'serviceB', function ($q, $interval, serviceB) {
var service = {};
service.start = function () {
var progress = 0;
var deferred = $q.defer();
deferred.promise.then(null,null, notifyServiceB);
function notifyServiceB() {
serviceB.update(progress);
}
$interval(function() {
if (progress == 0.99) {
deferred.resolve();
} else {
progress += 0.01;
deferred.notify(progress);
}
}, 50, 100);
};
return service;
}]);
myApp.factory('serviceB', ['$rootScope', function ($rootScope) {
var service = {};
service.update = function (progress) {
console.log('update', progress);
service.progress = progress;
//$rootScope.$apply(); // <+ ERROR: $digest already in progress
};
return service;
}]);
myApp.controller('ControllerA', ['$scope', 'serviceA',
function ($scope, serviceA) {
serviceA.start();
}]);
myApp.controller('ControllerB', ['$scope', 'serviceB',
function ($scope, serviceB) {
$scope.progress = serviceB.progress;
/* this works but I'm not sure whether this is performing
$scope.$watch(function () { return serviceB.progress; },
function (value) {
$scope.progress = serviceB.progress;
}
);
*/
}]);
Without the $watch in ControllerB for the property X in ServiceB, the UI would not get updated. I've also tried injecting $rootScope in ServiceB in order to run an apply() but that wouldn't work.
I'm not entirely sure whether there's a better way to setup this scenario or whether $watch is fine. I'm a bit worried about performance issues as the value of property X changes almost every 50 ms (it's basically a visual timer counting down).
Thanks for your input.
If you don't use $watch, you can use $rootScope to broadcast, and on controller B, you can $on this event and handle the view update.

Resources