How to access another scope with controller? - angularjs

I know that you can create binding with a directive but is it possible with a controller? Maybe this is stupidly simple but I couldn't figure out.
For example I would like to click on a text in x scope's view and execute a function in another scope.
function firstCtrl() {
var vm = this;
vm.data = 'First scope stuff!';
}
function secondCtrl() {
var vm = this;
vm.data = 'Second scope stuff!';
}
vm.clicked = function() {
alert('firstScope is clicked!');
};
Here is what I have: http://plnkr.co/edit/CpPKsEGPZWzufB2K1FK3

There are multiple ways how to communicate between controllers.
One way is emitting an event on scope:
function FirstCtrl($scope)
{
$scope.$on('someEvent', function(event, args) {});
// another controller or even directive
}
function SecondCtrl($scope)
{
$scope.$emit('someEvent', args);
}
Another way sharing a service.
Hope it helps. Also check this https://stackoverflow.com/a/9407953/4782034

Related

How to store controller functions in a service and call them in AngularJS

I need to execute functions of some controllers when my application ends (e.g. when closing the navigator tab) so I've thought in a service to manage the list of those functions and call them when needed. These functions changes depending on the controllers I have opened.
Here's some code
Controller 1
angular.module('myApp').component('myComponent', {
controller: function ($scope) {
var mc = this;
mc.saveData = function(objectToSave){
...
};
}
});
Controller 2
angular.module('myApp').component('anotherComponent', {
controller: function ($scope) {
var ac = this;
ac.printData = function(objects, priority){
...
};
}
});
How to store those functions (saveData & printData) considering they have different parameters, so when I need it, I can call them (myComponent.saveData & anotherComponent.printData).
The above code is not general controller but the angular1.5+ component with its own controller scope. So the methods saveData and printData can only be accessed in respective component HTML template.
So to utilise the above method anywhere in application, they should be part of some service\factory and that needs to be injected wherever you may required.
You can create service like :
angular.module('FTWApp').service('someService', function() {
this.saveData = function (objectToSave) {
// saveData method code
};
this.printData = function (objects, priority) {
// printData method code
};
});
and inject it wherever you need, like in your component:
controller: function(someService) {
// define method parameter data
someService.saveData(objectToSave);
someService.printData (objects, priority);
}
I managed to make this, creating a service for managing the methods that will be fired.
angular.module('FTWApp').service('myService',function(){
var ac = this;
ac.addMethodForOnClose = addMethodForOnClose;
ac.arrMethods = [];
function addMethodForOnClose(idModule, method){
ac.arrMethods[idModule] = {
id: idModule,
method: method
}
};
function executeMethodsOnClose(){
for(object in ac.arrayMethods){
ac.arrMethods[object].method();
}
});
Then in the controllers, just add the method needed to that array:
myService.addMethodForOnClose(id, vm.methodToLaunchOnClose);
Afterwards, capture the $window.onunload and run myService.executeMethodsOnClose()

How to change parent controller's object instance using an AngularJS service?

I'm using nested controllers and UI-Router. My top level controller, called MainCtrl, is set in my app's index.html file. If the MainCtrl uses a service, to pass data around, how can I change an instance of an object in the MainCtrl from a child controller without using $scope?
This is basically what I have (typed from memory):
var mainCtrl = function (ProfileSvc) {
var vm = this;
vm.profile = ProfileSvc.profile;
};
var loginCtrl = function (ProfileSvc, AuthSvc) {
var vm = this;
vm.doLogin = function (form) {
if (form.$error) { return; }
AuthSvc.login(form.user, form.pass).
.then(function(response) {
ProfileSvc.profile = response.data.profile;
}, function(errResponse) {
// error
}
};
};
User #shershen posted a reply to another question that gave me the idea to use $scope.$on and an event, however I really do not want references to $scope in my code:
Propagating model changes to a Parent Controller in Angular
I think without using $scope you may want to use the Controller as ctrl in your views. So...
var mainCtrl = function (ProfileSvc) {
var vm = this;
vm.profile = ProfileSvc.profile;
vm.updateProfile = function(profileAttrs) {
vm.profile = ProfileSvc.update(profileAttrs);
}
};
Then in the view, something along the lines of:
<div ng-controller="mainCtrl as main">
<button ng-click="main.updateProfile({ name: 'Fishz' })">
</div>
Hope this helps!
I had to do something similar on a project and ended up using $cacheFactory. First just load it up as a service with something like:
myApp.factory('appCache', function($cacheFactory) {
return $cacheFactory('appCache');
});
Then make sure you inject appCache into your controllers and then in your controllers you can call the cache service's put and get methods to store and retrieve your object.
In my case the parent view and child view both can change the object I'm caching, but the user only can commit from the parent.

How to add a function to all scopes..

I have a function on rootScope that I defined.
I later use it in controllers on the controller's scope - as it inherits the function from the rootScope.
now, I would like to do the same in directives with isolatedScopes.
since they don't prototypically inherit from rootScope, I need to find a another way to place that function on each isolated scope.
is there a way to customize isolated scope on creation, or some other hook?
update
Clarification: i need the function on the scope - not rootScope.
the reason is that we use this.$on('$destroy'...). so we basically want to execute code each time my scope is destroyed.
Current implementation: currently i override $rootScope.$new and check, if isolated i simply add the function on the scope. however this feels hackish and i would like to find a better solution.
here is my current code
$rootScope.registerTask = function(){
....
this.$on("$destroy", function() {
...
});
};
$rootScope.origNew = $rootScope.$new;
$rootScope.$new = function(isolate, parent ){
var newScope = this.origNew(isolate, parent );
if ( isolate ){
newScope.unregisterTask = ...;
newScope.registerTask = $rootScope.registerTask;
}
return newScope;
};
$rootScope.unregisterTask = ...;
Ahh.. the answer has been in front of me all along.. because the question was wrong!
All I wanted to do is assign functions on each and every scope (including isolated).
Well, scopes have prototypical inheritance, then just use that!
var Scope = Object.getPrototypeOf($rootScope);
Scope.registerTask = function(){
....
this.$on("$destroy", function() {
...
});
};
Scope.unregisterTask = ...;
should do the charm.. anywhere on load..
just goes to show - don't write code under pressure, you will work harder and get crappy results.. :)
I think you can inject the $rootScope in to the directive as,
app.directive('testDirective', ['$rootScope', function($rootScope) {
return {
scope : {},
link : function() {
},
controller : ['$scope', function($scope) {
$rootScope.rootScopeFunc();
}]
}
}]);
here is a DEMO

AngularJS controller constructor and instantiation with arguments

Is it possible to instantiate a controller in AngularJS and pass arguments to its constructor like in OOP ? I can't figure out how to refactor 3 identical controller with just variables name and content which change...
Thanx.
If you have 3 separate sections on the page that have very similar controller code, it sounds like you should consider using a directive. Even if you don't need to control the DOM directly (which is the classic reason to use directive), and only need the standard Angular data-bindings, then this is a nice way to reuse controllers in different contexts by the attributes set on the directive.
You can see a working plunkr at
http://plnkr.co/edit/qclp6MOxGWP7Ughod4T8?p=preview
But the key point is directives can bind-to variables in their parent scope's controller. Say, in the parent scope you have 3 variables, so:
$scope.myVariable1 = 'Value 1';
$scope.myVariable2 = 'Value 2';
$scope.myVariable3 = 'Value 3';
Then you can setup 3 instances of the directive in the template:
<my-directive my-param="myVariable1"></my-directive>
<my-directive my-param="myVariable2"></my-directive>
<my-directive my-param="myVariable3"></my-directive>
Then each directive can use the variable in the 'my-param' attribute
scope: {
'myParam':'='
}
The '=' means that in the scope of the directive you have a variable, called 'myParam', that is equal (+ bound to) the variable specified by the 'my-param' attribute on the directive. So on the template of the directive, you can use:
<div>Value of parameter: {{myParam}}</div>
And in the controller of the directive, you have access to is as:
$scope.myParam
And should then be able to customise its behaviour based on that instance's myParam.
You can create services with a common interface and then pass the corresponding services to each controller through dependency injection. In my code this is the case of table controllers where the only thing that changes is the data source:
Imagine you have some code that looks like this
angular.module('myapp').controller('TableACtrl', ['$scope', function($scope) {
$scope.data = // get the data from some source
$scope.someFunction = function () { ... };
$scope.someOtherFunction = function () { ... };
}]);
angular.module('myapp').controller('TableBCtrl', ['$scope', function($scope) {
$scope.data = // get the data from some other source
$scope.someFunction = function () { ... };
$scope.someOtherFunction = function () { ... };
}]);
It looks something like:
var tableCtrl = function($scope, dataSource) {
$scope.data = dataSource.getData();
$scope.someFunction = function () { ... };
$scope.someOtherFunction = function () { ... };
};
angular.module('myapp')
.controller('TableACtrl', ['$scope', 'dataSourceA', tableCtrl])
.controller('TableBCtrl', ['$scope', 'dataSourceB', tableCtrl])
angular.module('myapp')
.service('dataSourceA', function() {
return {
getData: function() { ... }
};
});
angular.module('myapp')
.service('dataSourceB', function() {
return {
getData: function() { ... }
};
});
I would still put things within a module, so you don't pollute the global window. But that's a different issue.

Decorator for Scope

Is it possible, and if so how, to decorate $scope so all scopes have some extra function / property?
I'm trying to do this:
$provide.decorator('$scope', function($scope)
{
$scope.cakes = true;
return $scope;
});
But it explodes with:
Unknown provider: $scopeProvider from App.
I know I can add properties and functions to the $rootScope and it will prototypically inherit, but I want isolated scopes in directives to also have access to these added things.
I had the same problem.
Just extend $rootScope prototype.
Then isolated scopes will also have this method.
This is my attempt to use lodash debounce function as native scope method:
angular.module('Test', [])
.config(function($provide) {
$provide.decorator('$rootScope', function ($delegate) {
$delegate.__proto__.$$busy = 0;
$delegate.__proto__.$watchDebounce = function (watchExpression, listener, objectEquality){
var _scope = this;
var debouncedListener = _.debounce(function (newValue, oldValue, scope){
listener(newValue, oldValue, scope);
_scope.$$busy = 0;
scope.$digest();
}, 1000);
var wrappedListener = function (newValue, oldValue, scope){
_scope.$$busy = 1;
debouncedListener(newValue, oldValue, scope);
}
return this.$watch(watchExpression, wrappedListener, objectEquality);
}
return $delegate;
})
})
Working example here http://jsfiddle.net/3ncct/
It doesn't seem possible, but I'd say that's the whole point of an isolate scope.
What you can do is access decorated stuff via scope.$root, however for many use cases it will defeat the purpose because the added functions will still only access the $rootScope instead of your isolated one.

Resources