Angularjs: How to build a controller reuseable? - angularjs

I have an application with some controllers that have the same functions as: InitMenu, GetData, Search, Paging, ..
How can I build a generic controller with the main functions: InitMenu, GetData, Search, Paging, .. without having to write in each specific controller?

share all the common logic inside a service and inject it inside your controllers.
.service('CommonData, function(){
this.getData = function(){
}
this.InitMenu= function(){
}
this.search= function(){
}
})
otherwise you can make a parent controller and all the child controllers will prototype inherit from the parent scope.
.controller('ParentCtrl', function($scope){
$scope.myObj = {};
$scope.parentMethod = function(){
return myObj;
}
})
.controller('ChildCtrl', function($scope){
var stuff = $scope.parentMethod();
//you can access the parent method in the child template as well
})
<div ng-controller="ParentCtrl">
<div ng-controller="ChildCtrl">
{{parentMethod()}}
//if you use controllAs syntax change it to {{parentCtrlName.parentMethod()}}
</div>
</div>
if you use controllerAs syntax you can access the parent $scope by specifyng the controller name as a scope field in the child controller. e.g $scope.parentCtrlName.parentMethod() and parentCtrlname.parentMethod() in the child template.

Related

AngularJS inherit a single scope from another controller

Is there a way to inherit a single scope from one controller to another controller via $controller?
Here is my current code:
var peopleApp = angular.module('peopleApp', []);
peopleApp.controller('indexController', function($scope){
$scope.sampleVar = "Hello World";
$scope.sampleVar2 = "Hello World Again";
});
peopleApp.controller('addController', function($scope, $controller){
$controller('indexController', {$scope.sampleVar: $scope.sampleVar});
alert($scope.sampleVar);
});
I know this will throw an error because of "$controller('indexController', {$scope.sampleVar: $scope.sampleVar});" that should be "$controller('indexController', {$scope: $scope});" but the concept or desired return is there.
<body ng-controller="parentController">
<div ng-controller="childController1">
</div>
<div ng-controller="childController2">
</div>
</body>
You can inherit properties form parent controller to childController1 and childController2
Angularjs internally maintaines scope hierarchy via ng-controller directive where you defined in dom. For More detials in https://docs.angularjs.org/guide/controller
Another way is Inject $rootScope inside controller and access properties from
anywhere in your app controllers
var app = angular.module('sample',[])
app.controller('controller1',function($rootScope,$scope){
$rootScope.parent ="I am visible in any controller";
})
app.controller('controller2',function($rootScope,$scope){
//here you can access rootScope property using rootScope or Scope
// Because rootScope is parent for all scope object
// Angular scope protochain is like javascript protochain it looks from child to parent scope
console.log( $rootScope.parent);
console.log($scope.parent);
})

Calling of one controller from another

While loading I am calling one controller, however I am using ng-include to import another HTML page in the main HTML page.
I want to call ng-controller of included page.(How to call a child controller from a parent controller using ng-init)
You need not call it seperately. Just add 'ng-controller' directive on the 'ng-include' element and assign its controller as shown below:
<div ng-include="template.url" ng-controller="YourController"></div>
The best way to do this is to have the parent Controller broadcast an event that the child scope will register:
.controller("ControllerParent", [$scope, function($scope) {
$scope.alertChild = function() {
$scope.$broadcast("custom-event", data);
};
});
.controller("ControllerChild", [$scope, function($scope) {
$scope.$on("custom-event", function(event, data) {
//do something with event
};
});

Communication between child and parent directive

I'm trying to figure out how to make a child directive communicate with it's parent directive
I basically have this html markup
<myPanel>
<myData dataId={{dataId}}></myData>
</myPanel>
In the myData directive, if there is no data available, I want to hide the myPanel.
In the myData directive controller I've tried
$scope.$emit('HideParent');
And in the myPanel controller I've tried
$scope.$on('HideParent', function () { $scope.hide = true; });
And also
$scope.$watch('HideParent', function () { if (value) { $scope.hide = true; }});
In either situation, the myPanel directive isn't receiving the $emit
You may create controller in myPanel directive.
Then require this controller in myData directive. And when child directive has no data, call controller method to hide parent.
For example in you parent (myPanel) directive:
controller: function($scope, $element){
$scope.show = true;
this.hidePanel = function(){
$scope.show = false;
}
}
In myData directive require this controller:
require:'^myPanel'
And then, call controller function when you need
if (!scope.data){
myPanelCtrl.hidePanel();
}
Look this Plunker example
The answers above where the parent directive's controller is required is a great one if there truly is a dependency. I had a similar problem to solve, but wanted to use the child directive within multiple parent directives or even without a parent directive, so here's what I did.
Your HTML.
<myPanel>
<myData dataId={{dataId}}></myData>
</myPanel>
In your directive simply watch for changes to the attribute.
controller: function ($scope, $element, $attrs) {
$attrs.$observe('dataId', function(dataId) { $scope.hide = true; } );
}
It's explicit and simple without forcing a dependency relationship where one may not exist. Hope this helps some people.

Calling child controller's method

I have two controllers like this
<div ng-controller="ParentController">
<div ng-controller="ChildController">
... my grid table ..
</div>
</div>
js code
function ParentController($scope) {
...
$scope.refreshBtnClicked = function() {
//here I want to call refreshGrid method of ChildController
}
}
function ChildController($scope) {
...
$scope.refreshGrid = function() {
... its using some inner variables of Child controller...
}
}
How can I call it?
Try this way, you will have to implement your service function but that's basically grid stuff.
<div ng-controller="ParentController">
<div ng-controller="ChildController">
... my grid table ..
</div>
</div>
function ParentController($scope, gridService) {
...
$scope.refreshBtnClicked = gridService.refresh();
}
function ChildController($scope, gridService) {
...
do whatever you like with your gridService methods or properties.
}
your factory or service
angular.module('myApp')
.service('Gridservice', function Gridservice() {
//data and methods here
});
you can also use broadcast or emit to trigger events, between controllers. like if you refresh, also your childcontroller data gets updated.
With directives, you can "register" the child controller or scope in the parent. Something like this:
app.directive('parent', function(){
controller: function(){
this.children = [];
this.addChild = function(childScope){
this.children.push(childScope);
}
this.callChildFunc = function(childID){
children[childID].someFunc();
}
};
})
app.directive('child', function(){
require: '^parent',
link: (scope, elem, attrs, parentCtrl) {
parentCtrl.addChild(scope)
scope.someFunc = function(){};
}
})
The child calls a function that adds a reference to its scope in the parent so it can call its functions. Another option would be to use $scope instead of this in the parent's controller and pass the addChild function to the child's scope using an isolate scope.
Well, you would have to do these things the AngularJS way. If there is a method or idea you want to share across controllers, you should implement it as a factory or service. And then you can use emit or broadcast to simultaneously share this service between controllers.
Can one controller call another?

AngularJS - Access to child scope

If I have the following controllers:
function parent($scope, service) {
$scope.a = 'foo';
$scope.save = function() {
service.save({
a: $scope.a,
b: $scope.b
});
}
}
function child($scope) {
$scope.b = 'bar';
}
What's the proper way to let parent read b out of child? If it's necessary to define b in parent, wouldn't that make it semantically incorrect assuming that b is a property that describes something related to child and not parent?
Update: Thinking further about it, if more than one child had b it would create a conflict for parent on which b to retrieve. My question remains, what's the proper way to access b from parent?
Scopes in AngularJS use prototypal inheritance, when looking up a property in a child scope the interpreter will look up the prototype chain starting from the child and continue to the parents until it finds the property, not the other way around.
Check Vojta's comments on the issue https://groups.google.com/d/msg/angular/LDNz_TQQiNE/ygYrSvdI0A0J
In a nutshell: You cannot access child scopes from a parent scope.
Your solutions:
Define properties in parents and access them from children (read the link above)
Use a service to share state
Pass data through events. $emit sends events upwards to parents until the root scope and $broadcast dispatches events downwards. This might help you to keep things semantically correct.
While jm-'s answer is the best way to handle this case, for future reference it is possible to access child scopes using a scope's $$childHead, $$childTail, $$nextSibling and $$prevSibling members. These aren't documented so they might change without notice, but they're there if you really need to traverse scopes.
// get $$childHead first and then iterate that scope's $$nextSiblings
for(var cs = scope.$$childHead; cs; cs = cs.$$nextSibling) {
// cs is child scope
}
Fiddle
You can try this:
$scope.child = {} //declare it in parent controller (scope)
then in child controller (scope) add:
var parentScope = $scope.$parent;
parentScope.child = $scope;
Now the parent has access to the child's scope.
One possible workaround is inject the child controller in the parent controller using a init function.
Possible implementation:
<div ng-controller="ParentController as parentCtrl">
...
<div ng-controller="ChildController as childCtrl"
ng-init="ChildCtrl.init()">
...
</div>
</div>
Where in ChildController you have :
app.controller('ChildController',
['$scope', '$rootScope', function ($scope, $rootScope) {
this.init = function() {
$scope.parentCtrl.childCtrl = $scope.childCtrl;
$scope.childCtrl.test = 'aaaa';
};
}])
So now in the ParentController you can use :
app.controller('ParentController',
['$scope', '$rootScope', 'service', function ($scope, $rootScope, service) {
this.save = function() {
service.save({
a: $scope.parentCtrl.ChildCtrl.test
});
};
}])
Important:
To work properly you have to use the directive ng-controller and rename each controller using as like i did in the html eg.
Tips:
Use the chrome plugin ng-inspector during the process. It's going to help you to understand the tree.
Using $emit and $broadcast, (as mentioned by walv in the comments above)
To fire an event upwards (from child to parent)
$scope.$emit('myTestEvent', 'Data to send');
To fire an event downwards (from parent to child)
$scope.$broadcast('myTestEvent', {
someProp: 'Sending you some data'
});
and finally to listen
$scope.$on('myTestEvent', function (event, data) {
console.log(data);
});
For more details :- https://toddmotto.com/all-about-angulars-emit-broadcast-on-publish-subscribing/
Enjoy :)
Yes, we can assign variables from child controller to the variables in parent controller. This is one possible way:
Overview: The main aim of the code, below, is to assign child controller's $scope.variable to parent controller's $scope.assign
Explanation: There are two controllers. In the html, notice that the parent controller encloses the child controller. That means the parent controller will be executed before child controller. So, first setValue() will be defined and then the control will go to the child controller. $scope.variable will be assigned as "child". Then this child scope will be passed as an argument to the function of parent controller, where $scope.assign will get the value as "child"
<!DOCTYPE html>
<html>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.min.js"></script>
<script type="text/javascript">
var app = angular.module('myApp',[]);
app.controller('child',function($scope){
$scope.variable = "child";
$scope.$parent.setValue($scope);
});
app.controller('parent',function($scope){
$scope.setValue = function(childscope) {
$scope.assign = childscope.variable;
}
});
</script>
<body ng-app="myApp">
<div ng-controller="parent">
<p>this is parent: {{assign}}</p>
<div ng-controller="child">
<p>this is {{variable}}</p>
</div>
</div>
</body>
</html>

Resources