AngularJS: Using the same controller multiple times on a view - angularjs

I am delving into Angularjs and got to the point where I am taking my application beyond the simple todo list stages.
A scenario I have, which must be common, is to have one controller that needs to be shared across multiple areas on my view.
In my case I have a profileController which I want to use to get the profile of the user from the server on each load.
When calling the loadUserProfile from within the profileController I saw that it was called 'n'-times (for the number of times I referenced the controller on the view), which is cool, so I changed the profileController to simply expose variables such as userProfile and isAuthenticated which simply references variables that I set in the $rootScope. These variables are set in a controller that is loaded once per page declared on my <body data-ng-controller="baseController as vm">, however this does not seem to work, and I am wondering what I am missing.
So it get's the data from the profileService, but when updating the $rootScope variables, it does not reflect it on my view.
Below is my baseController:
(function () {
'use strict';
var controllerId = 'baseController';
angular.module('app.controllers').controller(controllerId, ['$location', 'common', 'authClientSvc', 'profileService', '$rootScope', baseController]);
function baseController($location, common, authClientSvc, profileService, $rootScope) {
var getLogFn = common.logger.getLogFn;
var log = getLogFn(controllerId);
var vm = this;
$rootScope.isAuthenticated = false;
$rootScope.userProfile = {
email: '',
name: '',
surname: ''
};
activate();
function activate() {
var promises = [getUserProfile()];
common.activateController([promises], controllerId)
.then(function () { log('Activated Secure View'); });
}
function getUserProfile() {
profileService.getProfile('a#a.com').then(function (data) {
setTimeout(function () {
$rootScope.$apply(function () {
$rootScope.userProfile = data;
$rootScope.isAuthenticated = authClientSvc.isAuthenticated()
})
}, 1000);
});
}
}
})();
And my profileController where I expose the $rootScope variables below:
(function () {
'use strict';
var controllerId = 'profileController';
angular.module('app')
.controller(controllerId, ['common', 'authClientSvc', 'profileService', '$rootScope', profileController]);
function profileController(common, authClientSvc, profileService, $rootScope) {
var getLogFn = common.logger.getLogFn;
var log = getLogFn(controllerId);
var vm = this;
vm.logoutUrl = '/';
vm.isAuthenticated = $rootScope.isAuthenticated;
vm.profile = $rootScope.userProfile;
activate();
function activate() {
var promises = [];
common.activateController(promises, controllerId)
.then(function () { log('Activated Profile controller'); });
}
vm.logOut = function () {
authClientSvc.logout();
}
}
})();
I am not sure what is wrong, because in Fiddler I can clearly see the data coming back, and when I debug it and put log messages in the baseController after if gets the profile, it gets back the right data, but for some reason it is not working on my elements. Below is a sample of how I use it on the view:
<div class="login-info" data-ng-controller="profileController as vm">
<span ng-switch="vm.isAuthenticated">
<a ng-switch-when="true" href="javascript:void(0);" id="show-shortcut" data-action="toggleShortcut">
<img src="/Content/img/avatars/male.png" alt="me" class="online" />
<span data-localize="{{vm.profile.name}}.{{vm.profile.surname}}">
{{vm.profile.name}}.{{vm.profile.surname}}
</span>
<i class="fa fa-angle-down"></i>
</a>
<span ng-switch-when="false">
<img src="/Content/img/avatars/male.png" alt="guest" class="online" />
<span data-localize="Guest.user">
Guest User
</span>
</span>
</span>
</div>
Any help would be greatly appreciated, or even suggestions on how to achieve this in a better way.
Thanks.

According to Angular documentation:
When a Controller is attached to the DOM via the ng-controller directive, Angular will instantiate a new Controller object, using the specified Controller's constructor function. A new child scope will be available as an injectable parameter to the Controller's constructor function as $scope.
Meaning: Controllers are instantiated when the ng-controller directive is attached to the DOM. If you have two ng-controllers, then you will have two separate instances of ng-controller.
And
Do not use controllers to:
Share code or state across controllers — Use angular services instead.

Related

How to share controller data between two HTML page in Angular JS [duplicate]

I have a basic controller that displays my products,
App.controller('ProductCtrl',function($scope,$productFactory){
$productFactory.get().success(function(data){
$scope.products = data;
});
});
In my view I'm displaying this products in a list
<ul>
<li ng-repeat="product as products">
{{product.name}}
</li>
</ul
What I'm trying to do is when someone click on the product name, i have another view named cart where this product is added.
<ul class="cart">
<li>
//click one added here
</li>
<li>
//click two added here
</li>
</ul>
So my doubt here is, how do pass this clicked products from first controller to second? i assumed that cart should be a controller too.
I handle click event using directive. Also i feel i should be using service to achieve above functionality just can't figure how? because cart will be predefined number of products added could be 5/10 depending on which page user is. So i would like to keep this generic.
Update:
I created a service to broadcast and in the second controller i receive it. Now the query is how do i update dom? Since my list to drop product is pretty hardcoded.
From the description, seems as though you should be using a service. Check out http://egghead.io/lessons/angularjs-sharing-data-between-controllers and AngularJS Service Passing Data Between Controllers to see some examples.
You could define your product service (as a factory) as such:
app.factory('productService', function() {
var productList = [];
var addProduct = function(newObj) {
productList.push(newObj);
};
var getProducts = function(){
return productList;
};
return {
addProduct: addProduct,
getProducts: getProducts
};
});
Dependency inject the service into both controllers.
In your ProductController, define some action that adds the selected object to the array:
app.controller('ProductController', function($scope, productService) {
$scope.callToAddToProductList = function(currObj){
productService.addProduct(currObj);
};
});
In your CartController, get the products from the service:
app.controller('CartController', function($scope, productService) {
$scope.products = productService.getProducts();
});
how do pass this clicked products from first controller to second?
On click you can call method that invokes broadcast:
$rootScope.$broadcast('SOME_TAG', 'your value');
and the second controller will listen on this tag like:
$scope.$on('SOME_TAG', function(response) {
// ....
})
Since we can't inject $scope into services, there is nothing like a singleton $scope.
But we can inject $rootScope. So if you store value into the Service, you can run $rootScope.$broadcast('SOME_TAG', 'your value'); in the Service body. (See #Charx description about services)
app.service('productService', function($rootScope) {/*....*/}
Please check good article about $broadcast, $emit
Solution without creating Service, using $rootScope:
To share properties across app Controllers you can use Angular $rootScope. This is another option to share data, putting it so that people know about it.
The preferred way to share some functionality across Controllers is Services, to read or change a global property you can use $rootscope.
var app = angular.module('mymodule',[]);
app.controller('Ctrl1', ['$scope','$rootScope',
function($scope, $rootScope) {
$rootScope.showBanner = true;
}]);
app.controller('Ctrl2', ['$scope','$rootScope',
function($scope, $rootScope) {
$rootScope.showBanner = false;
}]);
Using $rootScope in a template (Access properties with $root):
<div ng-controller="Ctrl1">
<div class="banner" ng-show="$root.showBanner"> </div>
</div>
You can do this by two methods.
By using $rootscope, but I don't reccommend this. The $rootScope is the top-most scope. An app can have only one $rootScope which will be
shared among all the components of an app. Hence it acts like a
global variable.
Using services. You can do this by sharing a service between two controllers. Code for service may look like this:
app.service('shareDataService', function() {
var myList = [];
var addList = function(newObj) {
myList.push(newObj);
}
var getList = function(){
return myList;
}
return {
addList: addList,
getList: getList
};
});
You can see my fiddle here.
An even simpler way to share the data between controllers is using nested data structures. Instead of, for example
$scope.customer = {};
we can use
$scope.data = { customer: {} };
The data property will be inherited from parent scope so we can overwrite its fields, keeping the access from other controllers.
angular.module('testAppControllers', [])
.controller('ctrlOne', function ($scope) {
$scope.$broadcast('test');
})
.controller('ctrlTwo', function ($scope) {
$scope.$on('test', function() {
});
});
I saw the answers here, and it is answering the question of sharing data between controllers, but what should I do if I want one controller to notify the other about the fact that the data has been changed (without using broadcast)? EASY! Just using the famous visitor pattern:
myApp.service('myService', function() {
var visitors = [];
var registerVisitor = function (visitor) {
visitors.push(visitor);
}
var notifyAll = function() {
for (var index = 0; index < visitors.length; ++index)
visitors[index].visit();
}
var myData = ["some", "list", "of", "data"];
var setData = function (newData) {
myData = newData;
notifyAll();
}
var getData = function () {
return myData;
}
return {
registerVisitor: registerVisitor,
setData: setData,
getData: getData
};
}
myApp.controller('firstController', ['$scope', 'myService',
function firstController($scope, myService) {
var setData = function (data) {
myService.setData(data);
}
}
]);
myApp.controller('secondController', ['$scope', 'myService',
function secondController($scope, myService) {
myService.registerVisitor(this);
this.visit = function () {
$scope.data = myService.getData();
}
$scope.data = myService.getData();
}
]);
In this simple manner, one controller can update another controller that some data has been updated.
we can store data in session and can use it anywhere in out program.
$window.sessionStorage.setItem("Mydata",data);
Other place
$scope.data = $window.sessionStorage.getItem("Mydata");
1
using $localStorage
app.controller('ProductController', function($scope, $localStorage) {
$scope.setSelectedProduct = function(selectedObj){
$localStorage.selectedObj= selectedObj;
};
});
app.controller('CartController', function($scope,$localStorage) {
$scope.selectedProducts = $localStorage.selectedObj;
$localStorage.$reset();//to remove
});
2
On click you can call method that invokes broadcast:
$rootScope.$broadcast('SOME_TAG', 'your value');
and the second controller will listen on this tag like:
$scope.$on('SOME_TAG', function(response) {
// ....
})
3
using $rootScope:
4
window.sessionStorage.setItem("Mydata",data);
$scope.data = $window.sessionStorage.getItem("Mydata");
5
One way using angular service:
var app = angular.module("home", []);
app.controller('one', function($scope, ser1){
$scope.inputText = ser1;
});
app.controller('two',function($scope, ser1){
$scope.inputTextTwo = ser1;
});
app.factory('ser1', function(){
return {o: ''};
});
I've created a factory that controls shared scope between route path's pattern, so you can maintain the shared data just when users are navigating in the same route parent path.
.controller('CadastroController', ['$scope', 'RouteSharedScope',
function($scope, routeSharedScope) {
var customerScope = routeSharedScope.scopeFor('/Customer');
//var indexScope = routeSharedScope.scopeFor('/');
}
])
So, if the user goes to another route path, for example '/Support', the shared data for path '/Customer' will be automatically destroyed. But, if instead of this the user goes to 'child' paths, like '/Customer/1' or '/Customer/list' the the scope won't be destroyed.
You can see an sample here: http://plnkr.co/edit/OL8of9
I don't know if it will help anyone, but based on Charx (thanks!) answer I have created simple cache service. Feel free to use, remix and share:
angular.service('cache', function() {
var _cache, _store, _get, _set, _clear;
_cache = {};
_store = function(data) {
angular.merge(_cache, data);
};
_set = function(data) {
_cache = angular.extend({}, data);
};
_get = function(key) {
if(key == null) {
return _cache;
} else {
return _cache[key];
}
};
_clear = function() {
_cache = {};
};
return {
get: _get,
set: _set,
store: _store,
clear: _clear
};
});
Make a factory in your module and add a reference of the factory in controller and use its variables in the controller and now get the value of data in another controller by adding reference where ever you want
One way using angular service:
var app = angular.module("home", []);
app.controller('one', function($scope, ser1){
$scope.inputText = ser1;
});
app.controller('two',function($scope, ser1){
$scope.inputTextTwo = ser1;
});
app.factory('ser1', function(){
return {o: ''};
});
<div ng-app='home'>
<div ng-controller='one'>
Type in text:
<input type='text' ng-model="inputText.o"/>
</div>
<br />
<div ng-controller='two'>
Type in text:
<input type='text' ng-model="inputTextTwo.o"/>
</div>
</div>
https://jsfiddle.net/1w64222q/
FYI
The $scope Object has the $emit, $broadcast, $on
AND
The $rootScope Object has the identical $emit, $broadcast, $on
read more about publish/subscribe design pattern in angular here
To improve the solution proposed by #Maxim using $broadcast, send data don't change
$rootScope.$broadcast('SOME_TAG', 'my variable');
but to listening data
$scope.$on('SOME_TAG', function(event, args) {
console.log("My variable is", args);// args is value of your variable
})
There are three ways to do it,
a) using a service
b) Exploiting depending parent/child relation between controller scopes.
c) In Angular 2.0 "As" keyword will be pass the data from one controller to another.
For more information with example, Please check the below link:
http://www.tutorial-points.com/2016/03/angular-js.html
var custApp = angular.module("custApp", [])
.controller('FirstController', FirstController)
.controller('SecondController',SecondController)
.service('sharedData', SharedData);
FirstController.$inject = ['sharedData'];
function FirstController(sharedData) {
this.data = sharedData.data;
}
SecondController.$inject['sharedData'];
function SecondController(sharedData) {
this.data = sharedData.data;
}
function SharedData() {
this.data = {
value: 'default Value'
}
}
First Controller
<div ng-controller="FirstController as vm">
<input type=text ng-model="vm.data.value" />
</div>
Second Controller
<div ng-controller="SecondController as vm">
Second Controller<br>
{{vm.data.value}}
</div>
I think the best way is to use $localStorage. (Works all the time)
app.controller('ProductController', function($scope, $localStorage) {
$scope.setSelectedProduct = function(selectedObj){
$localStorage.selectedObj= selectedObj;
};
});
Your cardController will be
app.controller('CartController', function($scope,$localStorage) {
$scope.selectedProducts = $localStorage.selectedObj;
$localStorage.$reset();//to remove
});
You can also add
if($localStorage.selectedObj){
$scope.selectedProducts = $localStorage.selectedObj;
}else{
//redirect to select product using $location.url('/select-product')
}

Unknown provider: dataProvider [duplicate]

I have two Angular controllers:
function Ctrl1($scope) {
$scope.prop1 = "First";
}
function Ctrl2($scope) {
$scope.prop2 = "Second";
$scope.both = Ctrl1.prop1 + $scope.prop2; //This is what I would like to do ideally
}
I can't use Ctrl1 inside Ctrl2 because it is undefined. However if I try to pass it in like so…
function Ctrl2($scope, Ctrl1) {
$scope.prop2 = "Second";
$scope.both = Ctrl1.prop1 + $scope.prop2; //This is what I would like to do ideally
}
I get an error. Does anyone know how to do this?
Doing
Ctrl2.prototype = new Ctrl1();
Also fails.
NOTE: These controllers are not nested inside each other.
One way to share variables across multiple controllers is to create a service and inject it in any controller where you want to use it.
Simple service example:
angular.module('myApp', [])
.service('sharedProperties', function () {
var property = 'First';
return {
getProperty: function () {
return property;
},
setProperty: function(value) {
property = value;
}
};
});
Using the service in a controller:
function Ctrl2($scope, sharedProperties) {
$scope.prop2 = "Second";
$scope.both = sharedProperties.getProperty() + $scope.prop2;
}
This is described very nicely in this blog (Lesson 2 and on in particular).
I've found that if you want to bind to these properties across multiple controllers it works better if you bind to an object's property instead of a primitive type (boolean, string, number) to retain the bound reference.
Example: var property = { Property1: 'First' }; instead of var property = 'First';.
UPDATE: To (hopefully) make things more clear here is a fiddle that shows an example of:
Binding to static copies of the shared value (in myController1)
Binding to a primitive (string)
Binding to an object's property (saved to a scope variable)
Binding to shared values that update the UI as the values are updated (in myController2)
Binding to a function that returns a primitive (string)
Binding to the object's property
Two way binding to an object's property
I like to illustrate simple things by simple examples :)
Here is a very simple Service example:
angular.module('toDo',[])
.service('dataService', function() {
// private variable
var _dataObj = {};
// public API
this.dataObj = _dataObj;
})
.controller('One', function($scope, dataService) {
$scope.data = dataService.dataObj;
})
.controller('Two', function($scope, dataService) {
$scope.data = dataService.dataObj;
});
And here the jsbin
And here is a very simple Factory example:
angular.module('toDo',[])
.factory('dataService', function() {
// private variable
var _dataObj = {};
// public API
return {
dataObj: _dataObj
};
})
.controller('One', function($scope, dataService) {
$scope.data = dataService.dataObj;
})
.controller('Two', function($scope, dataService) {
$scope.data = dataService.dataObj;
});
And here the jsbin
If that is too simple, here is a more sophisticated example
Also see the answer here for related best practices comments
--- I know this answer is not for this question, but I want people who reads this question and want to handle Services such as Factories to avoid trouble doing this ----
For this you will need to use a Service or a Factory.
The services are the BEST PRACTICE to share data between not nested controllers.
A very very good annotation on this topic about data sharing is how to declare objects. I was unlucky because I fell in a AngularJS trap before I read about it, and I was very frustrated. So let me help you avoid this trouble.
I read from the "ng-book: The complete book on AngularJS" that AngularJS ng-models that are created in controllers as bare-data are WRONG!
A $scope element should be created like this:
angular.module('myApp', [])
.controller('SomeCtrl', function($scope) {
// best practice, always use a model
$scope.someModel = {
someValue: 'hello computer'
});
And not like this:
angular.module('myApp', [])
.controller('SomeCtrl', function($scope) {
// anti-pattern, bare value
$scope.someBareValue = 'hello computer';
};
});
This is because it is recomended(BEST PRACTICE) for the DOM(html document) to contain the calls as
<div ng-model="someModel.someValue"></div> //NOTICE THE DOT.
This is very helpful for nested controllers if you want your child controller to be able to change an object from the parent controller....
But in your case you don't want nested scopes, but there is a similar aspect to get objects from services to the controllers.
Lets say you have your service 'Factory' and in the return space there is an objectA that contains objectB that contains objectC.
If from your controller you want to GET the objectC into your scope, is a mistake to say:
$scope.neededObjectInController = Factory.objectA.objectB.objectC;
That wont work...
Instead use only one dot.
$scope.neededObjectInController = Factory.ObjectA;
Then, in the DOM you can call objectC from objectA. This is a best practice related to factories, and most important, it will help to avoid unexpected and non-catchable errors.
Solution without creating Service, using $rootScope:
To share properties across app Controllers you can use Angular $rootScope. This is another option to share data, putting it so that people know about it.
The preferred way to share some functionality across Controllers is Services, to read or change a global property you can use $rootscope.
var app = angular.module('mymodule',[]);
app.controller('Ctrl1', ['$scope','$rootScope',
function($scope, $rootScope) {
$rootScope.showBanner = true;
}]);
app.controller('Ctrl2', ['$scope','$rootScope',
function($scope, $rootScope) {
$rootScope.showBanner = false;
}]);
Using $rootScope in a template (Access properties with $root):
<div ng-controller="Ctrl1">
<div class="banner" ng-show="$root.showBanner"> </div>
</div>
The sample above worked like a charm. I just did a modification just in case I need to manage multiple values. I hope this helps!
app.service('sharedProperties', function () {
var hashtable = {};
return {
setValue: function (key, value) {
hashtable[key] = value;
},
getValue: function (key) {
return hashtable[key];
}
}
});
I tend to use values, happy for anyone to discuss why this is a bad idea..
var myApp = angular.module('myApp', []);
myApp.value('sharedProperties', {}); //set to empty object -
Then inject the value as per a service.
Set in ctrl1:
myApp.controller('ctrl1', function DemoController(sharedProperties) {
sharedProperties.carModel = "Galaxy";
sharedProperties.carMake = "Ford";
});
and access from ctrl2:
myApp.controller('ctrl2', function DemoController(sharedProperties) {
this.car = sharedProperties.carModel + sharedProperties.carMake;
});
The following example shows how to pass variables between siblings controllers and take an action when the value changes.
Use case example: you have a filter in a sidebar that changes the content of another view.
angular.module('myApp', [])
.factory('MyService', function() {
// private
var value = 0;
// public
return {
getValue: function() {
return value;
},
setValue: function(val) {
value = val;
}
};
})
.controller('Ctrl1', function($scope, $rootScope, MyService) {
$scope.update = function() {
MyService.setValue($scope.value);
$rootScope.$broadcast('increment-value-event');
};
})
.controller('Ctrl2', function($scope, MyService) {
$scope.value = MyService.getValue();
$scope.$on('increment-value-event', function() {
$scope.value = MyService.getValue();
});
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp">
<h3>Controller 1 Scope</h3>
<div ng-controller="Ctrl1">
<input type="text" ng-model="value"/>
<button ng-click="update()">Update</button>
</div>
<hr>
<h3>Controller 2 Scope</h3>
<div ng-controller="Ctrl2">
Value: {{ value }}
</div>
</div>
I'd like to contribute to this question by pointing out that the recommended way to share data between controllers, and even directives, is by using services (factories) as it has been already pointed out, but also I'd like to provide a working practical example of how to that should be done.
Here is the working plunker: http://plnkr.co/edit/Q1VdKJP2tpvqqJL1LF6m?p=info
First, create your service, that will have your shared data:
app.factory('SharedService', function() {
return {
sharedObject: {
value: '',
value2: ''
}
};
});
Then, simply inject it on your controllers and grab the shared data on your scope:
app.controller('FirstCtrl', function($scope, SharedService) {
$scope.model = SharedService.sharedObject;
});
app.controller('SecondCtrl', function($scope, SharedService) {
$scope.model = SharedService.sharedObject;
});
app.controller('MainCtrl', function($scope, SharedService) {
$scope.model = SharedService.sharedObject;
});
You can also do that for your directives, it works the same way:
app.directive('myDirective',['SharedService', function(SharedService){
return{
restrict: 'E',
link: function(scope){
scope.model = SharedService.sharedObject;
},
template: '<div><input type="text" ng-model="model.value"/></div>'
}
}]);
Hope this practical and clean answer can be helpful to someone.
You could do that with services or factories. They are essentially the same apart for some core differences. I found this explanation on thinkster.io to be the easiest to follow. Simple, to the point and effective.
Couldn't you also make the property part of the scopes parent?
$scope.$parent.property = somevalue;
I'm not saying it's right but it works.
Ah, have a bit of this new stuff as another alternative. It's localstorage, and works where angular works. You're welcome. (But really, thank the guy)
https://github.com/gsklee/ngStorage
Define your defaults:
$scope.$storage = $localStorage.$default({
prop1: 'First',
prop2: 'Second'
});
Access the values:
$scope.prop1 = $localStorage.prop1;
$scope.prop2 = $localStorage.prop2;
Store the values
$localStorage.prop1 = $scope.prop1;
$localStorage.prop2 = $scope.prop2;
Remember to inject ngStorage in your app and $localStorage in your controller.
There are two ways to do this
1) Use get/set service
2)
$scope.$emit('key', {data: value}); //to set the value
$rootScope.$on('key', function (event, data) {}); // to get the value
Second Approach :
angular.module('myApp', [])
.controller('Ctrl1', ['$scope',
function($scope) {
$scope.prop1 = "First";
$scope.clickFunction = function() {
$scope.$broadcast('update_Ctrl2_controller', $scope.prop1);
};
}
])
.controller('Ctrl2', ['$scope',
function($scope) {
$scope.prop2 = "Second";
$scope.$on("update_Ctrl2_controller", function(event, prop) {
$scope.prop = prop;
$scope.both = prop + $scope.prop2;
});
}
])
Html :
<div ng-controller="Ctrl2">
<p>{{both}}</p>
</div>
<button ng-click="clickFunction()">Click</button>
For more details see plunker :
http://plnkr.co/edit/cKVsPcfs1A1Wwlud2jtO?p=preview
I looked thru the answers above, I recommend pejman's Dec 29 '16 at 13:31 suggestion but he/she has not left a full answer. Here it is, I will put this as --> (you need a service and a listener $watch on one of the scopes from controllers for changes in the service area)
var app =
angular.module('myApp', ['ngRoute', 'ngSanitize']);
app.service('bridgeService', function () {
var msg = "";
return msg;
});
app.controller('CTRL_1'
, function ($scope, $http, bridgeService)
{
$http.get(_restApi, config)
.success(
function (serverdata, status, config) {
$scope.scope1Box = bridgeService.msg = serverdata;
});
});
app.controller('CTRL_2'
, function ($scope, $http, bridgeService)
{
$scope.$watch( function () {
return (bridgeService.msg);
}, function (newVal, oldVal) {
$scope.scope2Box = newVal;
}, true
);
});
If you don't want to make service then you can do like this.
var scope = angular.element("#another ctrl scope element id.").scope();
scope.plean_assign = some_value;
Besides $rootScope and services, there is a clean and easy alternative solution to extend angular to add the shared data:
in the controllers:
angular.sharedProperties = angular.sharedProperties
|| angular.extend(the-properties-objects);
This properties belong to 'angular' object, separated from the scopes, and can be shared in scopes and services.
1 benefit of it that you don't have to inject the object: they are accessible anywhere immediately after your defination!

How can angularjs component detect that data is loaded in parent controller?

I have a MainController aliased vm, that has a property vm.data that is loaded from some DataService.
The View of the Controller has a custom component rs-dropdown, that is supposed to load dropdown values once the vm.data is loaded. vm.data is a property of Main Controller.
Is there any way how the component itself can watch for changes in parent MainController and, once the data is loaded, perform its load action?
Thanks
UPDATE: Thanks Clavies, adding ng-if="condition" to the component actually makes this component to be created and initialized once the condition is true. Looks like angular constantly monitors ng-if and turns the component ON when the condition is met.
Here I added ng-if="vm.woReAssign" attribute to my component, and now the component with be initialized and created only when this condition is true.
<rs-dropdown ng-if="vm.woReAssign"
rs-server-controller="WorkOrder"
rs-server-action="GetServiceProviderDropDownList"
rs-filter="{woNum:'{{vm.woReAssign.WorkOrderNumber}}'}"
rs-class="form-control">
</rs-dropdown>
P.S. Even though the condition becomes true via async calls with possible delay, it seems that Angular checks this condition all the time, and then revives the component.
I tried to create the scenario you mentioned. You can listen the controller update via lifecycle lifecycle hook $onchange in component desing.
See the example where dropdown component gets filled when data is populated into parent controller on button click.Hope this helps!
var app = angular.module("myApp", []);
app.controller("mainController", function(bookService, $scope) {
var vm = this;
vm.showDropdown = false;
vm.loadBooks = function() {
bookService.getAllBooks().then(function(books) {
vm.books = books;
vm.showDropdown = true;
}, function(err) {
vm.showDropdown = false;
//error
});
}
});
function dropdownControllerFn(bookService) {
var model = this;
model.$onInit = function() {
//do intialization
}
model.$onChanges = function(changes) {
model.books = changes;
};
}
app.component("rsDropdown", {
templateUrl: 'dropdownComponent.html',
bindings: {
books: '<'
},
controllerAs: 'model',
controller: dropdownControllerFn
});
app.factory('bookService', ['$http', '$timeout', '$q', function($http, $timeout, $q) {
var urlBase = 'favouriteBooks.json';
var service = {};
service.getAllBooks = function() {
return $http.get(urlBase).then(function(response) {
return response.data;
});
};
return service;
}]);;
}]);
dropdownComponent.html
<div>
<select ng-options="s.bookName for s in model.books" ng-model="selitem">
<option value="">--Select--</option>
</select>
</div>
index.html
<body data-ng-app="myApp" data-ng-controller="mainController as vm">
<rs-dropdown books=vm.books ng-if="vm.showDropdown"></rs-dropdown>
<button ng-click="vm.loadBooks()">Get Books</button>
</body>

How to fetch scope value from one controller to another controller in angular JS

How to fetch the scope value from one controller to another controller
html
<button class="btn btn-info" ng-click="setLanguage('en')">English</button>
<button class="btn btn-info" ng-click="setLanguage('de')">Danish</button>
Javascript
.controller('demoCtrl', function($scope) {
$scope.setLanguage = function(language) {
$scope.language = language;
}
});
You can use a service to get the value set by one controller into another controller.
.service('someService', function () {
this.language = null;
})
Controller
.controller('demoCtrl', function($scope, $rootScope, someService) {
$scope.setLanguage = function(language) {
$scope.language = language;
someService.language = language;
$rootScope.$broadcast('languageChanged');
}
});
In the other controller
.controller('someCtrl', function($scope, $rootScope, someService) {
$rootScope.$on('languageChanged', function () {
$scope.language = someService.language;
}
});
You can share data between controllers with the help of either a service or you can use $emit() and $broadcast() functions.
There is a listener function in AngularJS $on() which will always be listening to the broadcast and emit events.
The syntax for the above function :
$scope.$emit(‘name1’,{}); //here you can send your data
$scope.$broadcast(‘name2’,{});
$scope.$on(‘name2’,function(event,args){
});
When using a service you can simply inject the service into your two controllers and share the data.
.service('someService', function () {
var self=this;
self.language = '';
});
.controller('firstCtrl', function($scope, someService) {
$scope.setLanguage = function(language) {
$scope.language = language;
someService.language = language;
}
});
.controller('secondCtrl', function($scope, someService) {
$scope.language = someService.language;
});
If we want to call example method of:
"test1 DIV controller"
from:
"test2 DIV controller"
then we can write above method.
angular.Element(document.getelementByID('testDIV')).scope().example method name();
in test2DIVcontroller.
For Example :
<DIV ID="test1DIV" ng-controller="test1DIV controller"></DIV>
<DIV ID="test2DIV" ng-controller="test2DIV controller"></DIV>

Passing Data Between Controllers - Frustrated by lack of clarity in other answers

Like many before I want to pass data between controllers. Specifically I want the user to be able to keep track of creating another user. I looked at the following SO explanations and came away not getting the results I was looking for or with answers that I don't know how to implement:
How can I pass some data from one controller to another peer controller
angularjs - passing data between controllers
AngularJS: How can I pass variables between controllers?
I also don't think broadcast is the solution to the problem. I think this is something that should be done with a service or factory (either will do as long as it works since I don't really understand the difference between them right now)
My code:
SERVICE
angular.module('startupApp')
.service('vendorId', function () {
// AngularJS will instantiate a singleton by calling "new" on this function
var VendorId = [];
var Vendor = [];
return {
setVendorId: function(vendorId){
VendorId.push(vendorId);
},
getVendorId: function(){
return VendorId;
},
setVendor: function(vendor){
Vendor.push(vendor);
},
getVendor: function(){
return Vendor;
}
};
});
CONTROLLER 1
angular.module('startupApp')
.controller('controller1', function ($scope, Auth, $location, vendorId, $http) {
$scope.user = {};
vendorId.setVendor($scope.user);
vendorId.setVendorId($scope.user.id);
})
CONTROLLER 2
angular.module('startupApp')
.controller('controller2', function ($scope, Auth, $location, vendorId, $http) {
console.log(vendorId.getVendor());//[]
console.log(vendorId.getVendorId());//[]
})
These both end up as empty arrays. To be clear, there is page1.html that has a form that populates the $scope.user object in controller1. I want to be able to get this object on page2.html from the page2.html controller (controller2)
Your code should work, unless controller1 is not invoked before controller2.
run this code snippet and see by yourself.
angular.module("test", [])
.service("vendorId", function () {
var VendorId = [];
var Vendor = [];
return {
setVendorId: function(vendorId){
VendorId.push(vendorId);
},
getVendorId: function(){
return VendorId;
},
setVendor: function(vendor){
Vendor.push(vendor);
},
getVendor: function(){
return Vendor;
}
};
})
.controller("Controller1", function ($rootScope, vendorId) {
this.user = {id : 1};
this.save = function () {
vendorId.setVendor(this.user);
vendorId.setVendorId(this.user.id);
// simulates page change
$rootScope.saved = true;
}
})
.controller("Controller2", function (vendorId) {
this.user = vendorId.getVendor();
this.id = vendorId.getVendorId();
})
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div data-ng-app="test">
<div data-ng-if="!saved" data-ng-controller="Controller1 as scope1">
<input type="text" data-ng-model="scope1.user.name" />
<button data-ng-click="scope1.save()">Save</button>
</div>
<div data-ng-if="saved" data-ng-controller="Controller2 as scope2">
The user : {{scope2.user}} <br />
The user id : {{scope2.id}}
</div>
</div>

Resources