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!
I wanted to display the added users dynamically in the dashboard.
My code is in the following way.
Controller: where the actual action triggers .
Adding the user function
$scope.addUser= function(){
modalService.addUser();
}
function init(){
// Someother functions
getUserRequests()
};
function getUserRequests() {
datacontext.getExtranetUserRequests()
.then(function (data){
vm.ExtranetUserRequest = data;
});
};
Service: modalService
addUser: function (column) {
var modalInstance = $modal.open({
templateUrl: 'app/NewExtranetSite/Popup/userModal.html',
controller: 'userModal',
});
updateUser: function(){
// updates the user
});
Controller :userModal
In the userModal.html after adding the info and on clicking save, add user function will be triggered.
function addUser(){
datacontext.saveNewExtranetuserRequest($scope.user);
};
I would like to initiate the getUserRequests() after the completion of add user in the user modal
So that the newly added user can be visble on the dashboard without refreshing the page
Let me answer u shortly.
You have a view where you are adding user details from input using one form ().
On ng-submit or ng-click action you can call one method in your that particular view's controller.
Now to display user details, you might know json. So create a blank $scope variable which will contain added user details.($scope.variable=[];)
Now on submit just hit **
$sope.variable.push({'key':value,'value':value});
**
once your object is populated with new data it will automatically displayed in the view.
5. We have just awesome ng-repeat angular's directive to show dynamical list containing objects.
6. **
ng-repeat="key in variable track by $index"
**
The $modal.open function returns a promise, so it's easy to wait for the modal to close and then execute another function. Let 'addUser' return this promise, then wait for it to finish before executing getUserRequests:
in modalService:
addUser: function (column) {
var modalInstance = $modal.open({
templateUrl: 'app/NewExtranetSite/Popup/userModal.html',
controller: 'userModal',
});
return modalInstance;
}
in controller:
$scope.addUser= function(){
modalService.addUser().then((resultReturnedFromModal) => {
getUserRequests();
});
}
Sorry for the bad post.
Let me explain briefly in this post
I would like to display the added data dynamically on the page.
I have a controller where the user addition action takes place.
(function () {
'use strict';
var controllerId = 'newUser';
angular.module('app').controller(controllerId,
['modalService', '$scope', 'dataContext', newUser]);
function newUser(modalService, $scope, dataContext) {
init();
function init() {
var extranetSiteRequestId = +$routeParams.id;
if (extranetSiteRequestId && extranetSiteRequestId > 0) {
getItem(extranetSiteRequestId);
getUserRequests();
}
}
$scope.newuserRequest = function () {
modalService.addUser();
}
function getUserRequests() {
datacontext.getExtranetUserRequests().then(function (data) {
vm.UserData = data;
});
};
}
}());
I am using a service modalService to handle the add user request.
(function (){
'use strict';
var serviceId = 'modalService'
angular.module('app').service(serviceId, ['$modal', modalService]);
function modalService($modal) {
return {
addUser: function () {
var modalInstance = $modal.open({
templateUrl: 'app/NewExtranetSite/Popup/userModal.html',
controller: 'userModal',
});
modalInstance.result.then(function (userDetails) {
if (userDetails) {
alert(userDetails) ;
};
})
},
};
}
})();
finally in the userModal controller am handling the new user added request
(function () {
'use strict';
var controllerId = 'userModal';
angular.module('app').controller(controllerId, ['$scope', '$modalInstance', 'datacontext', 'common', addUserModalFunction]);
function addUserModalFunction($scope, $modalInstance, datacontext, common) {
var vm = $scope;
vm.cancel = cancel;
vm.submit = addUser;
init();
function init() {
common.logger.log("controller loaded", null, controllerId);
common.activateController([], controllerId);
}
function cancel() {
$modalInstance.close();
}
$scope.open = function ($event, opened) {
$event.preventDefault();
$event.stopPropagation();
$scope[opened] = true;
};
function addUser() {
datacontext.saveNewExtranetuserRequest($scope.user).then(function(data){
$modalInstance.close($scope.user);
});
};
};
})();
Now the problem is I would like to add a success or then function in the newUser Controller after the modalService.adduser complete
EX: modalService.addUser().then(function(results){
});
Fikkatra Thanks for the reply but couldn't able to achieve
am very bad # angular
I read an answer about "dynamic factory" from How to create Dynamic factory in Angular js?
It works great but some additional conditions required for my project make me tired.
conditions are simple.
There is a button that trigger dynamic directive element. From now we call the original background's scope 'ParentScope' and directive's 'ChildScope'.
At the same time, 'ParentScope' dynamically make factory named "ex2-service".
This directive's controller needs to inject "ex2-service".
above simple example is http://jsfiddle.net/chaht01/QM52v/29/
jscode below
var app = angular.module("myApp", [])
.service("echo", function() {
return {
echo: function(msg) {
console.log(msg);
return msg;
}
};
}), makeService = function(module, identifier) {
module.factory(identifier+'-service', ['echo', function(echo) {
return {
run: function(msg) {
return echo.echo(identifier + ": " + msg);
}
};
}]);
};
makeService(app, 'ex1');
app.controller("myController", ['ex1-service',
'$injector',
'$scope',
function(service, $injector, $scope) {
$scope.service1 = service.run('injected.');
$scope.test = function(){
$scope.isReady = true;
makeService(app, 'ex2');
}
}]).controller("child",['ex2-service','$scope',function(service,$scope){
$scope.service2 = service.run('dynamically injected')
}])
when I click button, get error like
Error: [$injector:unpr] Unknown provide: ex2-serviceProvider <- ex2-service
how can I solve this problem??
I am getting an unknown provider error when attempting to launch an angular bootstrap modal window from my app by clicking on an image. I launch the same type of modal successfully elsewhere in the app.
Here is a screenshot of the error in the debugger
Is there something wrong with my controller code below? I looked at several other unknown provider error posts on stack, and to my knowledge I'm doing things properly.
app.controller('ModalInstanceCtrl', function($scope, $modalInstance, items,
removeVideoFromCart, removeLiteratureFromCart, productHasItems, cartItemHasVideos,
cartItemHasLiterature, getCartMailToBody, cartMailtoLink, logSentItems) {
$scope.items = items;
$scope.ok = function() {
$modalInstance.close($scope.test);
};
$scope.removeVideoFromCart = function (video, familyIndex, index) {
removeVideoFromCart(video, familyIndex, index);
$scope.cartMailtoLink = getCartMailToBody(); //update the mailto link body to remove video links
}
$scope.removeLiteratureFromCart = function (literature, familyIndex, index) {
removeLiteratureFromCart(literature, familyIndex, index);
$scope.cartMailtoLink = getCartMailToBody(); //update the mailto link body to remove lit links
}
$scope.cancel = function() {
$modalInstance.dismiss('cancel');
};
$scope.productHasItems = function(index) {
return productHasItems(index);
}
$scope.cartItemHasVideos = function(index) {
return cartItemHasVideos(index);
}
$scope.cartItemHasLiterature = function (index) {
return cartItemHasLiterature(index);
}
$scope.getCartMailToBody = function () {
getCartMailToBody();
}
$scope.cartMailtoLink = getCartMailToBody();
$scope.logSentItems = function () {
logSentItems();
}
});
Thank you very much for your time. Let me know if you need more information or if I am being unclear.
I'm going to assume that app points to a declaration of that module that is defined at the root of your app e.g. in app.js:
app = angular.module('app', []);
And that you're including each dependency within your index.html e.g. after any angular scripts and app.js
<script src="yourDependency.js"></script>
In terms of the controller code itself, you don't need to assign to $scope a property that contains a function that calls the removeVideoFromCart service within your 'ModalInstanceCtrl' controller, because then you will still need to call that wrapper function again (which it currently looks like you are not doing).
You can just call the method within the controller rather than wrap it in a function e.g.
$scope.removeVideoFromCart = removeVideoFromCart(video, familyIndex, index);
or just call the service e.g. if you don't need to bind the data to the UI like sending form data that on success just redirects elsewhere (although in your case it looks like you do want to bind the data to the UI):
removeVideoFromCart(video, familyIndex, index);
It's not clear from your code where the parameters for each service originate from. Are they within the items object? e.g.
var video, familyIndex, index
vm.items = items;
video = items.video;
familyIndex = items.familyIndex;
index = items.index;
In terms of style, I prefer not assigning the module instances to a variable and instead use the setter syntax (following [John Papa's] (https://github.com/johnpapa/angular-styleguide#modules) styleguide, but also included in Todd Motto's), like so:
angular
.module('app')
.controller('ModalInstanceCtrl', ModalInstanceCtrl);
ModalInstanceCtrl.$inject['your', 'dependencies', 'go', 'here']
function ModalInstanceCtrl(/*dependencies here as parameters e.g.*/, removeVideoFromCart) {
var vm = this; // use in place of $scope and clarifies the context of the this keyword
vm.items = items;
video = items.video;
familyIndex = items.familyIndex;
index = items.index;
$scope.removeVideoFromCart = removeVideoFromCart(video, familyIndex, index);
$scope.removeLiteratureFromCart = removeLiteratureFromCart(literature, familyIndex, index);
//etc
});
NB: I would prefer a facade into all of those methods e.g. clearCartAndCloseModal('other', 'services') to hide all of the implementation details from the controller. This also makes it easier to create one controller per view that is in turn easier to test beacuse you have pushed all logic into the services. But I'm not clear from your code whether there is any relationship between each of the services.
#Claies #ritesh I was typing a long edit with responses to the questions when I happened upon my solution. I had multiple functions that opened modal windows using ModalInstanceController. For example, here are two:
$scope.open = function(size) {
var modalInstance = $modal.open({
templateUrl: 'myModalContent.html',
controller: 'ModalInstanceCtrl',
size: size,
resolve: {
items: function() {
return $scope.selectedVideo3;
}
}
});
modalInstance.result.then(function(selectedItem) {
$scope.selected = selectedItem;
}, function() {
$log.info('Modal dismissed at: ' + new Date());
});
};
$scope.openCart = function(size) {
var modalInstance = $modal.open({
templateUrl: 'myAttachmentModalContent.html',
controller: 'ModalInstanceCtrl',
size: size,
resolve: {
items: function() {
return "";
},
removeVideoFromCart: function() {
return $scope.removeVideoFromCart;
},
removeLiteratureFromCart: function() {
return $scope.removeLiteratureFromCart;
},
productHasItems: function() {
return $scope.productHasItems;
},
cartItemHasVideos: function() {
return $scope.cartItemHasVideos;
},
cartItemHasLiterature: function() {
return $scope.cartItemHasLiterature;
},
getCartMailToBody: function() {
return $scope.getCartMailToBody
},
cartMailtoLink: function() {
return $scope.cartMailtoLink
},
logSentItems: function () {
return $scope.logSentItems;
}
}
});
modalInstance.result.then(function(selectedItem) {
$scope.selected = selectedItem;
}, function() {
$log.info('Modal dismissed at: ' + new Date());
});
};
I only use most of the dependencies for ModalInstanceController in the openCart function, so I didn't add all of the dependency function declarations to my other open methods (You can see in the resolve for the $scope.open method above I only declare items and no functions).
I needed to declare all of these functions like I did in $scope.openCart and it fixed my problem.
Thank you for reaching out.
My case was UpperCase LowerCase problem in the injected service name.
I would ask for some help and explanation on the given issue. It seems like I can not pass variable to scope from directive to use it in controller, variable is called caret_position. Please see below code.
Controller
var myApp = angular.module('myApp', []);
myApp.controller('Composer', function Composer($scope, $http) {
// getting snippets json data
$http.get('data/snippets/default.json').success(function(data) {
$scope.snippets = data;
$scope.snippets.count = data.length;
});
// adding snippet to composed text
$scope.composed_text = '';
$scope.snippet_insert = function() {
var snippet = this.item.content;
console.log($scope.caret_position); // stackoverflow.com note: this is not working
};
});
Directive:
myApp.directive('caretPosition', function() {
return {
link: function(scope, element, attrs) {
element.bind('keyup click', function(e){
var caret_position = element[0].selectionStart;
scope.caret_position = caret_position; // stackoverflow.com note: this is not working
scope.$apply(); // stackoverflow.com note: not working as well
console.log('my current position: ' + caret_position);
});
}
}
});
The recommended way to share data between directives and controllers is by using a Service, you can create one by using the factory method:
var app = angular.module('plunker', []);
app.factory('SharedService', function() {
return {
sharedObject: {
value: '',
value2: ''
}
};
});
And then you may inject your SharedService on both your directive and controller.
Here is a more detailed example about sharing data between controllers and directives:
http://plnkr.co/edit/Q1VdKJP2tpvqqJL1LF6m?p=preview
Hope that helps
Update:
I just edited your example to use that concept and it is working, take a look:
http://plnkr.co/edit/2pUODHCd9qTRcQTiziC2?p=preview
You would need to use a 2 way binding variable on your directive with isolate scope. That is the way a directive is able to update a scope property of an enclosing controller.you can do this by specifying scope:{catetproperty:'='} on your directive definition object
make the service in
var myApp = angular.module('mean.mapping2');
myApp.factory('SharedService', function() {
return {
caretInfo: {
position: 0
}
};
});
inject service in controller
angular.module('mean.mapping2').controller('mapProperties', ['$scope', 'Global', 'Mapping2', '$http', '$stateParams','SharedService',
console.log( $scope.caret = SharedService.caretInfo);//controller part for calling services
in directive use as
.directive('repeatEvents', function ($timeout,SharedService) {
.directive('setupConnections', function ($location,SharedService) {//i have two directive
use this in directive
var caret_position = items;
SharedService.position = caret_position;
console.log('Caret current position: ' + SharedService.position);