Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

$onInit and friends #13763

Closed
wants to merge 20 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
50b0c1a
feat($compile): call `$ngOnInit` on directive controllers after contr…
petebacondarwin Jan 13, 2016
cf4e7be
test($compile): check that $onInit is called correctly for ES6 classes
petebacondarwin Jan 13, 2016
452dd39
docs($compile): document the new `$onInit` controller hook
petebacondarwin Jan 13, 2016
f72ecbd
feat($compile): allow `require` to be an object
petebacondarwin Jan 13, 2016
b2c0b05
feat($compile): allow required controllers to be bound to the directi…
petebacondarwin Jan 13, 2016
37793ce
feat($compile): allow `require` to be an object
petebacondarwin Jan 14, 2016
a12ddc6
test($compile): check explicit return controllers are not broken by b…
petebacondarwin Jan 14, 2016
59feecc
feat($compile): allow required controllers to be bound to the directi…
petebacondarwin Jan 14, 2016
8040bab
feat($compile): call `$ngOnInit` on directive controllers after contr…
petebacondarwin Jan 14, 2016
9e6db1a
feat($compile): allow required controllers to be bound to the directi…
petebacondarwin Jan 14, 2016
1d18df2
fix($compile): ensure controllers with return value constructors are …
petebacondarwin Jan 15, 2016
4b304a6
docs($compile): squash me
petebacondarwin Jan 15, 2016
cae40ca
fix($compile): only bind required controllers if `bindToController` i…
petebacondarwin Jan 15, 2016
276b9ee
docs($compile): fix typo
petebacondarwin Jan 15, 2016
6d66a75
fix($compile): only bind required controllers if `bindToController` i…
petebacondarwin Jan 15, 2016
32b7da3
test($compile): check that $onInit is called correctly for ES6 classes
petebacondarwin Jan 15, 2016
cb495a5
fix($compile): only bind required controllers if `bindToController` i…
petebacondarwin Jan 15, 2016
270e230
fix($compile): ensure controllers with return value constructors are …
petebacondarwin Jan 15, 2016
281d987
fix($compile): only bind required controllers if `bindToController` i…
petebacondarwin Jan 15, 2016
9f76a11
test($compile): check that $onInit is called correctly for ES6 classes
petebacondarwin Jan 15, 2016
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
136 changes: 129 additions & 7 deletions src/ng/compile.js
Original file line number Diff line number Diff line change
Expand Up @@ -218,8 +218,18 @@
* definition: `controller: 'myCtrl as myAlias'`.
*
* When an isolate scope is used for a directive (see above), `bindToController: true` will
* allow a component to have its properties bound to the controller, rather than to scope. When the controller
* is instantiated, the initial values of the isolate scope bindings will be available if the controller is not an ES6 class.
* allow a component to have its properties bound to the controller, rather than to scope.
*
* After the controller is instantiated, the initial values of the isolate scope bindings will be bound to the controller
* properties. You can access these bindings once they have been initialized by providing a controller method called
* `$onInit`, which is called after all the controllers on an element have been constructed and had their bindings
* initialized.
*
* <div class="alert alert-warning">
* **Deprecation warning:** although bindings for non-ES6 class controllers are currently
* bound to `this` before the controller constructor is called, this use is now deprecated. Please place initialization
* code that relies upon bindings inside a `$onInit` method on the controller, instead.
* </div>
*
* It is also possible to set `bindToController` to an object hash with the same format as the `scope` property.
* This will set up the scope bindings to the controller directly. Note that `scope` can still be used
Expand Down Expand Up @@ -255,12 +265,29 @@
* The `$transclude` function also has a method on it, `$transclude.isSlotFilled(slotName)`, which returns
* `true` if the specified slot contains content (i.e. one or more DOM nodes).
*
* The controller can provide the following methods that act as life-cycle hooks:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we mention that this is basically a new phase that happens before both pre and postLink?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Merge pull request #11 from 05bca054/rename-to-ats …
WIP: rename to *.ats

* * `$onInit` - Called on each controller after all the controllers on an element have been constructed and
* had their bindings initialized (and before the pre &amp; post linking functions for the directives on
* this element). This is a good place to put initialization code for your controller.
*
* #### `require`
* Require another directive and inject its controller as the fourth argument to the linking function. The
* `require` takes a string name (or array of strings) of the directive(s) to pass in. If an array is used, the
* injected argument will be an array in corresponding order. If no such directive can be
* found, or if the directive does not have a controller, then an error is raised (unless no link function
* is specified, in which case error checking is skipped). The name can be prefixed with:
* `require` property can be a string, an array or an object:
* * a **string** containing the name of the directive to pass to the linking function
* * an **array** containing the names of directives to pass to the linking function. The argument passed to the
* linking function will be an array of controllers in the same order as the names in the `require` property
* * an **object** whose property values are the names of the directives to pass to the linking function. The argument
* passed to the linking function will also be an object with matching keys, whose values will hold the corresponding
* controllers.
*
* If the `require` property is an object and `bindToController` is truthy, then the required controllers are
* bound to the controller using the keys of the `require` property. This binding occurs after all the controllers
* have been constructed but before `$onInit` is called.
* See the {@link $compileProvider#component} helper for an example of how this can be used.
*
* If no such required directive(s) can be found, or if the directive does not have a controller, then an error is
* raised (unless no link function is specified and the required controllers are not being bound to the directive
* controller, in which case error checking is skipped). The name can be prefixed with:
*
* * (no prefix) - Locate the required controller on the current element. Throw an error if not found.
* * `?` - Attempt to locate the required controller or pass `null` to the `link` fn if not found.
Expand Down Expand Up @@ -1005,6 +1032,80 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
*
* ```
*
* ### Intercomponent Communication
* Directives can require the controllers of other directives to enable communication
* between the directives. This can be achieved in a component by providing an
* object mapping for the `require` property. Here is the tab pane example built
* from components...
*
* <example module="docsTabsExample">
* <file name="script.js">
* angular.module('docsTabsExample', [])
* .component('myTabs', {
* transclude: true,
* controller: function() {
* var panes = this.panes = [];
*
* this.select = function(pane) {
* angular.forEach(panes, function(pane) {
* pane.selected = false;
* });
* pane.selected = true;
* };
*
* this.addPane = function(pane) {
* if (panes.length === 0) {
* this.select(pane);
* }
* panes.push(pane);
* };
* },
* templateUrl: 'my-tabs.html'
* })
* .component('myPane', {
* transclude: true,
* require: {tabsCtrl: '^myTabs'},
* bindings: {
* title: '@'
* },
* controller: function() {
* this.$onInit = function() {
* this.tabsCtrl.addPane(this);
* console.log(this);
* };
* },
* templateUrl: 'my-pane.html'
* });
* </file>
* <file name="index.html">
* <my-tabs>
* <my-pane title="Hello">
* <h4>Hello</h4>
* <p>Lorem ipsum dolor sit amet</p>
* </my-pane>
* <my-pane title="World">
* <h4>World</h4>
* <em>Mauris elementum elementum enim at suscipit.</em>
* <p><a href ng-click="i = i + 1">counter: {{i || 0}}</a></p>
* </my-pane>
* </my-tabs>
* </file>
* <file name="my-tabs.html">
* <div class="tabbable">
* <ul class="nav nav-tabs">
* <li ng-repeat="pane in $ctrl.panes" ng-class="{active:pane.selected}">
* <a href="" ng-click="$ctrl.select(pane)">{{pane.title}}</a>
* </li>
* </ul>
* <div class="tab-content" ng-transclude></div>
* </div>
* </file>
* <file name="my-pane.html">
* <div class="tab-pane" ng-show="$ctrl.selected" ng-transclude></div>
* </file>
* </example>
*
*
* <br />
* Components are also useful as route templates (e.g. when using
* {@link ngRoute ngRoute}):
Expand Down Expand Up @@ -1072,7 +1173,8 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
transclude: options.transclude,
scope: {},
bindToController: options.bindings || {},
restrict: 'E'
restrict: 'E',
require: options.require
};
}

Expand Down Expand Up @@ -2280,6 +2382,11 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
for (var i = 0, ii = require.length; i < ii; i++) {
value[i] = getControllers(directiveName, require[i], $element, elementControllers);
}
} else if (isObject(require)) {
value = {};
forEach(require, function(controller, property) {
value[property] = getControllers(directiveName, controller, $element, elementControllers);
});
}

return value || null;
Expand Down Expand Up @@ -2388,6 +2495,21 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
}
}

// Bind the required controllers to the controller, if `require` is an object and `bindToController` is truthy
forEach(controllerDirectives, function(controllerDirective, name) {
var require = controllerDirective.require;
if (controllerDirective.bindToController && !isArray(require) && isObject(require)) {
extend(elementControllers[name].instance, getControllers(name, require, $element, elementControllers));
}
});

// Trigger the `$onInit` method on all controllers that have one
forEach(elementControllers, function(controller) {
if (isFunction(controller.instance.$onInit)) {
controller.instance.$onInit();
}
});

// PRELINKING
for (i = 0, ii = preLinkFns.length; i < ii; i++) {
linkFn = preLinkFns[i];
Expand Down
Loading