AngularJS: when creating a new directive, why there is a controller? - angularjs

To my understanding, controller is responsible for preparing the model, and pass the model to the directive which is responsible for updating DOM.
So when creating a new directive, why there is a controller inside?
Does this mean that I can do something like connecting server inside a directive?
app.directive('hover', function () {
return {
restrict: 'E',
controller: function ($scope) {
// what is the controller for?
...
}
}
}
});

From the docs on $compile service:
controller
[…] The controller is instantiated before
the pre-linking phase and it is shared with other directives (see
require attribute). This allows the directives to communicate with
each other and augment each other's behavior. The controller is
injectable (and supports bracket notation) with the following locals:
$scope - Current scope associated with the element
$element - Current element
$attrs - Current attributes object for the element
$transclude - A transclude linking function pre-bound to the correct transclusion scope. The scope can be overridden by an optional first argument.
function([scope], cloneLinkingFn).

Related

How can I share a scope with a directive in another module without using "scope: false"?

I have 2 modules, jsTag and mainApp. mainApp injects jsTag to use it's functionality
var jsTag = angular.module('jsTag')
angular.module('mainApp', ['jsTag']);
The jsTag module has a directive I'll call jsTagDirective
jsTag.directive('jsTagDirective', function(){
restrict: 'E',
scope: true,
controller: 'jsTagMainCtrl',
templateUrl: 'jsTag/source/Templates/js-tag.html'
The template of the above directive has an input tag inside of it, with an ng-model reference to jsText. I want to be able to capture the value of this model in the controller of my mainApp module. Changing scope to false does work - I can access $scope.jsText in the mainApp - but I understand this is bad practice. I can't figure out how to get inherited scope or isolated scope to pass the value upwards, though.
That's a perfect example why you should use isolated scope along with two-way binding. Docs here.
JS
jsTag.directive('jsTagDirective', function () {
return {
restrict: 'E',
scope: {
jsText: '='
},
controller: 'jsTagMainCtrl',
templateUrl: 'jsTag/source/Templates/js-tag.html'
};
});
HTML
<js-tag-directive js-text="jsText"></js-tag-directive>
This will make the directive have its jsText property in sync with a parent scope, even creating the said property.

Is it possible to get access to a controller without declaring it?

I have a bit of a weird situation which requires me to pass my controllers to my directives via a directive scope variable. This works great as long as there are only one controller in use per route, which is declared in my $routeProvider.
But now I have to have 2 controllers in use in the same template, which causes problems because I can't declare my controllers using ng-controller because that will throw a routeProvider error since I'm trying to access data from my route resolve. (You can only access route resolve data if you declare the controller in the same route as the resolve, which then makes using ng-controller in the template and controller in the directive useless to me).
So this is what I want to do:
// Declare one controller in the routing
.when('/someroute', {
controller: 'MyCtrl'
}
// But pass a different controller to my directive that hasn't been declared
// in either the route, template or directive
<my-directive ctrl="MyOtherCtrl"></my-directive>
But my question is, is it possible to access a controller and its functions without declaring the controller as ng-controller, controller in directive or in route? My far-fetched idea is that there's a service or something that you can inject which holds all of the controllers, but so far I've come up with none.
You could inject th controller by name into your directives controller using $controller.
var someOtherController = $controller('SomeOtherController ',{$scope: $scope});
It's methods would now be available on $scope. Be careful with this though, things can get hairy quickly.
There is a service to get an instance of any controller, use the $controller
in your directive, inject the $controller service then use it in your link function:
myApp.directive("myDirective", function($controller){
return {
scope: {
ctrl: "#"
},
link: function(scope, element, attrs){
var myNeededCtrl = $controller(scope.ctrl, {$scope: scope, otherDepenciesThatTheControllerNeed: ...});
myNeededCtrl.doSomething();
}
};
});

is there a way to have a directive put an object in the injector for its controller?

{
controller: 'myController',
templateUrl: '/templates/my-template.html',
restrict: "E",
};
Is there any way that I can add an object in the injector so that it's available inside myController? I know angular bootstrap does this with it's "resolve" attribute, when it news up a controller, so I would like to know if there's a way to do this here. And yes, I realize I can make this object available to the controller by setting it on the scope property, but I would like to know if I can do this through the injector rather than scope.
There is a way to manually create a controller and supply dependencies with $controller, however in case of passing the object from the link function to the controller in the directive (as seems to be in your case) there is an easier way and one does not need to pollute the scope to achieve that.
All that is needed is to obtain the reference to the already instantiated controller. This is simply done with require of own controller:
.directive("foo", function(){
return {
require: "foo",
controller: function($scope){
//...
},
link: function(scope, element, attrs, fooCtrl){
fooCtrl.someObj = {a: "aaa"};
}
}
});
Unlike the "resolved" value, this object is not available when the controller function runs. This is rarely needed though. If you need, however, to perform some initialization only when the object is available, then you can always expose a this.init = function(){...} function on the controller, and invoke it as needed.
You can create a service and inject it in your controller. Let's say the service is called "myObject", you can then define your controller as:
controller: function($scope, myObj) {
// myObj is accessible in the controller
}

Inheriting or sharing directive controller

I have two directives: A and B. They're very similar. I want directive B to inherit the controller in directive A.
In other words, the same function used for controller: in A's directive definition object needs to also be the controller: function used in B's directive definition object.
Aside from a copy/paste of the controller: function, how I use the same function in both A and B's definition?
Controllers are just regular JS functions, so, you can use prototyping:
function BaseController(){
this.commonFunct = function(){
...
}
}
function CtrlA(){
}
CtrlA.prototype = BaseController
function CtrlB(){
}
CtrlB.prototype = BaseController
this works with controllerAs syntax, when your controller is exposed to scope under some name, say ctrl. Then $scope.ctrl.commonFunct (more generic, works from any place of controller) or this.commonFunct (can be used in controller's instance methods, where this is controller itself) can be used to refer the function.
That works if you declare both controllers in one module as named functions. If they are declared in different modules, you can use mixin-like way with $controller:
// Base module
(function() {
'use strict';
angular.module('Base', []);
function BaseController($scope, <injectables>, that) {
that.commonFunct = function() {
};
}
angular.module('Base').controller('BaseController',
['$scope', '...', BaseController]);
})();
// Module that inherits functionality
(function() {
'use strict';
angular.module('Derived', ['Base']);
function DerivedController($scope, $controller, ...) {
$controller('BaseController', {
'$scope' : $scope,
...
'that' : this
});
// this.commonFunct is available
}
angular.module('Derived').controller('DerivedController',
['$scope', '$controller', '...', DerivedController]);
})();
MHO: I suggest to use named functions for declaring controllers / services and directives as it is more natural, JS way of doing things. Also, I like controllerAs syntax much as it helps to distinguish data, stored directly in scope (like $scope.data) with controller's methods (they all are stored in one scope's named object, like $scope.ctrl).
If I understand correct, you don't really want to inherit controllers. You want to use one controller in 2 different directives.
If that's the case, just declare the controller function, and pass it to both directive definition objects as a function or a string.

Access parent controller in angular directive

having same nested directives:
<div mydir="a">
<div mydir="b">
</div>
</div>
if mydir requires ?^mydir it always get itself's controller.
test_plunk
is it possible to access parent's controller?
According the angular documentation on jqLite, you can call controller() to retrieve the controller for any given element:
controller(name) - retrieves the controller of the current element or its parent.
By default retrieves controller associated with the ngController directive.
If name is provided as camelCase directive name, then the controller for this
directive will be retrieved (e.g. 'ngModel').
Within your link function, you can retrieve the parent controller by calling controller() on the parent element and passing in the name of the directive:
var parentCtrl = iElement.parent().controller('mydir');
$scope.$parent.a;
shall give you a
$scope is the scope injected by angular
$parent is a keyword reference to the parent scope from any scope in angular.
Yes this is JavaScript, and as was asked in the question, AngularJS powered Javascript

Resources