Broadcast different controllers in AngularJS - angularjs

I am developing a permission system for my app using a factory that looks like this:
angular.module('ecosystemServices', [])
.factory('Guard', function($http, $rootScope) {
var permissions = [];
return {
ready: function() {
if (permissions.length == 0) {
$http.get('/api/users/own-permissions')
.success(function(data){
permissions = data.user_permissions;
$rootScope.$broadcast('permissionsReady', 1);
});
} else {
$rootScope.$broadcast('permissionsReady', 1);
}
return true;
}
}
})
I have to wait until the permissions are loaded to start making queries, so I'm performing a broadcast to the controller, that looks something like this:
appControllers.controller('AgencyPanelCtrl', ['$rootScope', '$scope', '$location', '$routeParams', '$http','Guard',
function ($rootScope, $scope, $location, $routeParams, $http, Guard) {
$scope.loading = true;
Guard.ready();
$scope.has_permission = function(permission) {
return Guard.can(permission);
}
$rootScope.$on('permissionsReady', function(event, ready) {
$scope.initialize();
});
$scope.initialize = function() {
console.log("Initialized");
}
}]);
It's working, but if I change the controller and do the same (change the view and change the controller), the broadcast arrives to the old controller.
Does anyone knows why? Or how to fix it?

You are defining your event listener on the $rootScope. As the name would suggest, there is only one $rootScope, and it's at the root of the document.
As such, when your view changes and the old controller is no longer relevant, the $rootScope still has the listener, which still holds a reference to the old controller's $scope through a closure, and hence everything still goes to the old controller.
Instead, you should define the listener on the $scope of the controller:
$scope.$on('permissionsReady', function(event, ready) {
$scope.initialize();
});
This should fix your problem.

Related

how to passing data from one controller to another controller using angular js 1

hi all i using angular js i need to transfer the value from one page controller to another page controller and get that value into an a scope anybody help how to do this
code Page1.html
var app = angular.module("app", ["xeditable", "angularUtils.directives.dirPagination", "ngNotify", "ngCookies","ngRoute"]);
app.controller('Controller1', ['$scope', '$http', '$window', '$filter','$notify','$cookieStore',
function ($scope, $http, $window, $filter, $notify, $cookieStore)
{
$scope.Message="Hi welcome"
}]);
now i want to show scope message into page2 controller
var app = angular.module("app", ["xeditable", "angularUtils.directives.dirPagination", "ngNotify", "ngCookies","ngRoute"]);
app.controller('Controller2', ['$scope', '$http', '$window', '$filter','$notify','$cookieStore',
function ($scope, $http, $window, $filter, $notify, $cookieStore)
{
///here i want get that scope value
}]);
You can use $rootScope instead of $scope:
// do not forget to inject $rootScope as dependency
$rootScope.Message="Hi welcome";
But the best practice is using a service and share data and use it in any controller you want.
You should define a service and write getter/setter functions on this.
angular.module('app').service('msgService', function () {
var message;
this.setMsg = function (msg) {
message = msg;
};
this.getMsg = function () {
return message;
};
});
Now you should use the setMeg function in Controller1 and getMsg function in Controller2 after injecting the dependency like this.
app.controller('Controller1', ['$scope', '$http', '$window', '$filter','$notify','$cookieStore', 'msgService',
function ($scope, $http, $window, $filter, $notify, $cookieStore, msgService)
{
$scope.Message="Hi welcome"
msgService.setMsg($scope.Message);
}]);
app.controller('Controller2', ['$scope', '$http', '$window', '$filter','$notify','$cookieStore', 'msgService',
function ($scope, $http, $window, $filter, $notify, $cookieStore, msgService)
{
///here i want get that scope value
console.log('message from contoller 1 is : ', msgService.getMsg());
}]);
You should use services for it .
Services
app.factory('myService', function() {
var message= [];
return {
set: set,
get: get
}
function set(mes) {
message.push(mes)
}
function get() {
return message;
}
});
And in ctrl
ctrl1
$scope.message1= 'Hi';
myService.set($scope.message1);
ctrl2
var message = myService.get()
Sharing data from one controller to another using service
We can create a service to set and get the data between the controllers and then inject that service in the controller function where we want to use it.
Service :
app.service('setGetData', function() {
var data = '';
getData: function() { return data; },
setData: function(requestData) { data = requestData; }
});
Controllers :
app.controller('Controller1', ['setGetData',function(setGetData) {
// To set the data from the one controller
$scope.Message="Hi welcome";
setGetData.setData($scope.Message);
}]);
app.controller('Controller2', ['setGetData',function(setGetData) {
// To get the data from the another controller
var res = setGetData.getData();
console.log(res); // Hi welcome
}]);
Here, we can see that Controller1 is used for setting the data and Controller2 is used for getting the data. So, we can share the data from one controller to another controller like this.

Angular Factory Object binded to Scope not appearing in Modal

I'm trying to pass information about a listing that appears on the show page to the modal on that page.
I successfully created the factory service which returns me an object.
angular.module('articles').factory('ProductService', [ '$resource', 'Articles','$stateParams', function($resource, Articles, $stateParams) {
var listingInfo =
Articles.get({
articleId: $stateParams.articleId
});
return listingInfo;
}
]);
(logged by using angular.element(document.body).injector().get('ProductService'))
If I place this in my main ArticlesController I'm able to see the scope via browser console with angular.element($0).scope() and able to access the object by injecting into my controller and giving it a scope of $scope.product = ProductService;, allowing me to access the data in the expected way (product.furtherinfo).
However when trying the same technique for my modal controllers, I'm unable to find the scope when I log through the browser or access the data through binding or brackets.
I've tried passing the value through the resolve, injecting the dependency in all my controllers having to do with my modal, but nothing works.
// Modals
angular.module('articles').controller('ModalDemoCtrl',['$scope', '$modal', '$log', 'ProductService' , function ($scope, $modal, $log, ProductService) {
$scope.items = ['item1', 'item2', 'item3'];
$scope.product = ProductService;
$scope.animationsEnabled = true;
$scope.open = function (size) {
var modalInstance = $modal.open({
animation: $scope.animationsEnabled,
templateUrl: 'myModalContent.html',
controller: 'ModalInstanceCtrl',
size: size,
resolve: {
items: function () {
return $scope.items;
},
product: function () {
return $scope.product;
}
}
});
modalInstance.result.then(function (selectedItem) {
$scope.selected = selectedItem;
}, function () {
$log.info('Modal dismissed at: ' + new Date());
});
};
$scope.toggleAnimation = function () {
$scope.animationsEnabled = !$scope.animationsEnabled;
};
}]);
The idea is to pass the returned factory object to my modal so I can link it to an input(maybe hidden) that I could designate as a model to send through to an email.
angular.module('articles').controller('ModalInstanceCtrl',['$scope', '$modalInstance', 'items', '$http', 'product','ProductService','$stateParams', function ($scope, $modalInstance, items, $http, product,ProductService,$stateParams) {
$scope.items = items;
$scope.product = product;
$scope.sendMail = function(){
var data = ({
input : this.contactAgentInput,
inputBody : this.contactAgentInputBody,
})
$http.post('/contact-agent', data).
then(function(response) {
// this callback will be called asynchronously
$modalInstance.close($scope.selected.item);
console.log("all is well")
// when the response is available
}, function(response) {
// called asynchronously if an error occurs
// or server returns response with an error status.
})
}
$scope.cancel = function () {
$modalInstance.dismiss('cancel');
};
}]);
Use a debugger or console.log. If you add console.log(ProductService) in your modal controller, it should show you the service is being injected. – Anid Monsur
Thanks for the suggestion #AnidMonsur i noticed my Rentals controller firing off while In the Sales show page (I split the modules into Sales and Rentals). Thinking that I might be launching the modal from the wrong Module. Investigating now. – Meir Snyder
#AnidMonsur that did it! I was using the same names for some of the modal controllers ( stupid obviously) it must have launched the wrong modal instance and that's why I wasn't able to access the object. After giving them distinct names, it now works. Thanks so much, would have spent another day overlooking the error! – Meir Snyder
Let me guess. You're trying to display the items in the ModalDemoCtrl through the ModalInstanceCtrl, but you can't.
It seems you think DI is going to work as long as you put the name of the dependency but it's not until you register the dependency itself. So 'items' won't ever be a dependency at all and all you get is the value in the inner $scope on the controller (which probrablly is undefined).
In your case I'd say you register a third party factory (which is closer to a singleton) which can be injected just alike ProductService, and eventually would be called ItemsFactory.
Hope it helped.

A better way to use filters/objects in Angular controllers?

I'm setting a rootScope variable to maintain the state of the program. This works, but I don't think it's quite 'right'. Is there a better way to select an object from an array?
Here's my current code.
angular.module('myApp.controllers', [])
.controller('packingCtrl', ['$scope', '$http', '$filter', '$rootScope', function($scope, $http, $filter, $rootScope) {
$http.get('data/trayData.json').success(function(data) {
$scope.trays = data;
});
var currentOrder = $rootScope.currentlyPacking;;
$http.get('data/orderData.json').success(function(data) {
$scope.orders = data;
$scope.current = $filter('filter')($scope.orders, {orderId: currentOrder});
});
}])
Thanks in advance for any insight / best practices.
You can create a service to hold your state. Each service instance is a singleton, so when the service is injected into various controllers, all will see the same state.
var currentlyPackingSvc = function($http) {
var currentlyPacking = {
}
return {
currentlyPacking: currentlyPacking,
getTrays: function() { /* make $http call and update currentlyPacking */ },
getOrders: function() { /* make $http call and update currentlyPacking */ }
}
}
angular.service('currentlyPackingSvc', ['$http', currentlyPackingSvc]);
angular.controller('packingCtrl', ['$scope', '$http', '$filter', '$rootScope', 'currentlyPackingSvc'
function($scope, $http, $filter, $rootScope, currentlyPackingSvc) {
...
var currentOrder = currentlyPackingSvc.currentlyPacking;
...
}]);
Assuming you leave your 'currentlyPacking' property as an object, the changes should automatically be pushed to your scope.
This way, you have all your state isolated to one service that can be utilized anywhere.

How do I change a variable in MainController from PartialController? (angularjs)

I have a partial view that is using angular. How do I change a variable in the MainController from PartialController? I am not sure how to create the interdependence...
angularApp.controller('MainController', ['$scope', '$http', '$compile', function MainController($scope, $http, $compile) {
$scope.myVariable = "0";
//Had the following before refactoring due to repetitive code.
//Code now in PartialController
//$scope.searchData = function ($event) {
// //code
// $scope.myVariable = "1";
//}
}]);
angularApp.controller('PartialController', ['$scope', '$http', '$compile', function PartialController($scope, $http, $compile) {
$scope.searchData = function ($event) {
//code
$scope.myVariable = "1";
}
}]);
For sake of completeness, there are at least 3 ways:
With a service as #tymeJV suggested (BEST answer)
app.factory('dataStore', function () {
var dataStore = {};
return dataStore;
});
app.controller('ParentCtrl', function($scope, dataStore) {
$scope.dataStore = dataStore;
$scope.dataStore.foo = 'bar';
});
app.controller('ChildCtrl', function($scope, dataStore) {
dataStore.foo = 'not bar anymore';
});
With an object reference on the parent scope (A bit hackish)
app.controller('ParentCtrl', function($scope) {
$scope.data = {
foo: 'bar'
};
});
app.controller('ChildCtrl', function($scope) {
$scope.data.foo = 'not bar anymore';
});
With $parent (equally hackish)
app.controller('ParentCtrl', function($scope) {
$scope.foo = 'bar';
});
app.controller('ChildCtrl', function($scope) {
$scope.$parent.foo = 'not bar anymore';
});
Why are #2 and #3 hackish?
Because they create a dependency in your ChildCtrl of having it always be a child of the ParentCtrl... otherwise it will break.
So why include #2 and #3 at all?
For a few reasons:
Directives can have controllers, and required parent directives. Because of this, there are cases where you can "safely" use $parent or scope inheritance because you'll always know that ChildCtrl has ParentCtrl as a parent.
Sometimes you just need to hack something together.
As I said, for the sake of completeness.
This is a prime use for a service that can be injected to controllers when you need it and pull data from it:
app.factory("myService", function() {
var myVariable = null;
return {
get: function() {
return myVariable;
},
set: function(value) {
myVariable = value;
}
}
});
//Inject
angularApp.controller('MainController', ['$scope', '$http', 'myService', '$compile', function MainController($scope, $http, $compile, myService) {
myService.set(3);
});
tymeJV's answer is correct and is probably best practice in this case. I believe the reason it wasn't working in your example is because in Javascript, primitives (strings, numbers, booleans) are passed by value, whereas objects are passed by reference.
i.e. if you had $scope.obj.myVariable=1 in your main controller and edit $scope.obj.myVariable in your child controller, you should see the new value in both. (this is kinda #2 in blesh's answer). This is a common source of "bugs" in Angular so it's good to be aware of it.

angularjs location scope

I have some issue with AngularJS scope and location. Here is an example :
function CreateAccountCtrl($scope, $http, $location) {
...
$http.post(url,$scope.form).
success(function(data){
$location.path('/'); // I need to transfert data to the location
}
}
My problem is : I want to transfer the data to the / controller, I thought to use the rootScope but I don't think it is the best way to do this.
Any idea ?
You can use $rootScope to send data between controllers. $routeParams doesn't allow send complex data structures.
Let's see it.
Assign returned data of success callback to variable in $rootScope.
Navigate to the AccountController.
function CreateAccountCtrl($scope, $http, $location,$rootScope ) {
...
$http.post(url,$scope.form).
success(function(data){
$rootScope.accountInfo = data;
$location.path('/account');
}
}
Configure the route in the $routeProvider:
$routeProvider.when('/account', {
template: 'account.html',
controller: AccountCntl
});
In the AccountController can access the data on $rootScope.
function AccountCtrl($scope, $rootScope) {
$scope.accountInfo = $rootScope.accountInfo;
}
Use the $routeParams service.
function CreateAccountCtrl($scope, $http, $location) {
...
$http.post(url,$scope.form).
success(function(data){
$location.path('/account/' + data.accountId);
}
}
Configure the route in the $routeProvider:
$routeProvider.when('/account/:accountId', {
template: 'account.html',
controller: AccountCntl
});
Your AccountController can now access the data:
function AccountCtrl($scope, $routeParams) {
$scope.accountId = $routeParams.accountId;
}

Resources