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

Commit feeb197

Browse files
Narretzpetebacondarwin
authored andcommitted
refactor(loader): move component definition code to the $compileProvider
The `Module.component()` helper now delegates to `$compileProvider.component()`. This has the following benefits: - when using only the loader, we are not accessing out of scope variables / functions - components can be registered via $compileProvider - docs are a bit easier to find - it is easier to keep the Batarang version of the loader up to date if there is minimal code in that file. Closes #13692
1 parent 98c2db7 commit feeb197

File tree

4 files changed

+322
-277
lines changed

4 files changed

+322
-277
lines changed

src/loader.js

+3-146
Original file line numberDiff line numberDiff line change
@@ -288,155 +288,12 @@ function setupModuleLoader(window) {
288288
* @module ng
289289
* @param {string} name Name of the component in camel-case (i.e. myComp which will match as my-comp)
290290
* @param {Object} options Component definition object (a simplified
291-
* {@link ng.$compile#directive-definition-object directive definition object}),
292-
* has the following properties (all optional):
293-
*
294-
* - `controller` – `{(string|function()=}` – Controller constructor function that should be
295-
* associated with newly created scope or the name of a {@link ng.$compile#-controller-
296-
* registered controller} if passed as a string. Empty function by default.
297-
* - `controllerAs` – `{string=}` – An identifier name for a reference to the controller.
298-
* If present, the controller will be published to scope under the `controllerAs` name.
299-
* If not present, this will default to be the same as the component name.
300-
* - `template` – `{string=|function()=}` – html template as a string or a function that
301-
* returns an html template as a string which should be used as the contents of this component.
302-
* Empty string by default.
303-
*
304-
* If `template` is a function, then it is {@link guide/di injectable}, and receives
305-
* the following locals:
306-
*
307-
* - `$element` - Current element
308-
* - `$attrs` - Current attributes object for the element
309-
*
310-
* - `templateUrl` – `{string=|function()=}` – path or function that returns a path to an html
311-
* template that should be used as the contents of this component.
312-
*
313-
* If `templateUrl` is a function, then it is {@link guide/di injectable}, and receives
314-
* the following locals:
315-
*
316-
* - `$element` - Current element
317-
* - `$attrs` - Current attributes object for the element
318-
* - `bindings` – `{object=}` – Define DOM attribute binding to component properties.
319-
* Component properties are always bound to the component controller and not to the scope.
320-
* - `transclude` – `{boolean=}` – Whether {@link $compile#transclusion transclusion} is enabled.
321-
* Disabled by default.
322-
* - `isolate` – `{boolean=}` – Whether the new scope is isolated. Isolated by default.
323-
* - `restrict` - `{string=}` - String of subset of {@link ng.$compile#-restrict- EACM} which
324-
* restricts the component to specific directive declaration style. If omitted, this defaults to 'E'.
325-
* - `$canActivate` – `{function()=}` – TBD.
326-
* - `$routeConfig` – `{object=}` – TBD.
291+
* {@link ng.$compile#directive-definition-object directive definition object})
327292
*
328293
* @description
329-
* Register a component definition with the compiler. This is short for registering a specific
330-
* subset of directives which represents actual UI components in your application. Component
331-
* definitions are very simple and do not require the complexity behind defining directives.
332-
* Component definitions usually consist only of the template and the controller backing it.
333-
* In order to make the definition easier, components enforce best practices like controllerAs
334-
* and default behaviors like scope isolation, restrict to elements.
335-
*
336-
* <br />
337-
* Here are a few examples of how you would usually define components:
338-
*
339-
* ```js
340-
* var myMod = angular.module(...);
341-
* myMod.component('myComp', {
342-
* template: '<div>My name is {{myComp.name}}</div>',
343-
* controller: function() {
344-
* this.name = 'shahar';
345-
* }
346-
* });
347-
*
348-
* myMod.component('myComp', {
349-
* template: '<div>My name is {{myComp.name}}</div>',
350-
* bindings: {name: '@'}
351-
* });
352-
*
353-
* myMod.component('myComp', {
354-
* templateUrl: 'views/my-comp.html',
355-
* controller: 'MyCtrl as ctrl',
356-
* bindings: {name: '@'}
357-
* });
358-
*
359-
* ```
360-
*
361-
* <br />
362-
* Components are also useful as route templates (e.g. when using
363-
* {@link ngRoute ngRoute}):
364-
*
365-
* ```js
366-
* var myMod = angular.module('myMod', ['ngRoute']);
367-
*
368-
* myMod.component('home', {
369-
* template: '<h1>Home</h1><p>Hello, {{ home.user.name }} !</p>',
370-
* controller: function() {
371-
* this.user = {name: 'world'};
372-
* }
373-
* });
374-
*
375-
* myMod.config(function($routeProvider) {
376-
* $routeProvider.when('/', {
377-
* template: '<home></home>'
378-
* });
379-
* });
380-
* ```
381-
*
382-
* <br />
383-
* When using {@link ngRoute.$routeProvider $routeProvider}, you can often avoid some
384-
* boilerplate, by assigning the resolved dependencies directly on the route scope:
385-
*
386-
* ```js
387-
* var myMod = angular.module('myMod', ['ngRoute']);
388-
*
389-
* myMod.component('home', {
390-
* template: '<h1>Home</h1><p>Hello, {{ home.user.name }} !</p>',
391-
* bindings: {user: '='}
392-
* });
393-
*
394-
* myMod.config(function($routeProvider) {
395-
* $routeProvider.when('/', {
396-
* template: '<home user="$resolve.user"></home>',
397-
* resolve: {user: function($http) { return $http.get('...'); }}
398-
* });
399-
* });
400-
* ```
401-
*
402-
* <br />
403-
* See also {@link ng.$compileProvider#directive $compileProvider.directive()}.
294+
* See {@link ng.$compileProvider#component $compileProvider.component()}.
404295
*/
405-
component: function(name, options) {
406-
function factory($injector) {
407-
function makeInjectable(fn) {
408-
if (isFunction(fn) || Array.isArray(fn)) {
409-
return function(tElement, tAttrs) {
410-
return $injector.invoke(fn, this, {$element: tElement, $attrs: tAttrs});
411-
};
412-
} else {
413-
return fn;
414-
}
415-
}
416-
417-
var template = (!options.template && !options.templateUrl ? '' : options.template);
418-
return {
419-
controller: options.controller || function() {},
420-
controllerAs: identifierForController(options.controller) || options.controllerAs || name,
421-
template: makeInjectable(template),
422-
templateUrl: makeInjectable(options.templateUrl),
423-
transclude: options.transclude === undefined ? false : options.transclude,
424-
scope: options.isolate === false ? true : {},
425-
bindToController: options.bindings || {},
426-
restrict: options.restrict || 'E'
427-
};
428-
}
429-
430-
if (options.$canActivate) {
431-
factory.$canActivate = options.$canActivate;
432-
}
433-
if (options.$routeConfig) {
434-
factory.$routeConfig = options.$routeConfig;
435-
}
436-
factory.$inject = ['$injector'];
437-
438-
return moduleInstance.directive(name, factory);
439-
},
296+
component: invokeLaterAndSetModuleName('$compileProvider', 'component'),
440297

441298
/**
442299
* @ngdoc method

src/ng/compile.js

+162-2
Original file line numberDiff line numberDiff line change
@@ -880,8 +880,8 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
880880
* @param {string|Object} name Name of the directive in camel-case (i.e. <code>ngBind</code> which
881881
* will match as <code>ng-bind</code>), or an object map of directives where the keys are the
882882
* names and the values are the factories.
883-
* @param {Function|Array} directiveFactory An injectable directive factory function. See
884-
* {@link guide/directive} for more info.
883+
* @param {Function|Array} directiveFactory An injectable directive factory function. See the
884+
* {@link guide/directive directive guide} and the {@link $compile compile API} for more info.
885885
* @returns {ng.$compileProvider} Self for chaining.
886886
*/
887887
this.directive = function registerDirective(name, directiveFactory) {
@@ -928,6 +928,166 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
928928
return this;
929929
};
930930

931+
/**
932+
* @ngdoc method
933+
* @name $compileProvider#component
934+
* @module ng
935+
* @param {string} name Name of the component in camelCase (i.e. `myComp` which will match `<my-comp>`)
936+
* @param {Object} options Component definition object (a simplified
937+
* {@link ng.$compile#directive-definition-object directive definition object}),
938+
* with the following properties (all optional):
939+
*
940+
* - `controller` – `{(string|function()=}` – controller constructor function that should be
941+
* associated with newly created scope or the name of a {@link ng.$compile#-controller-
942+
* registered controller} if passed as a string. An empty `noop` function by default.
943+
* - `controllerAs` – `{string=}` – identifier name for to reference the controller in the component's scope.
944+
* If present, the controller will be published to scope under the `controllerAs` name.
945+
* If not present, this will default to be the same as the component name.
946+
* - `template` – `{string=|function()=}` – html template as a string or a function that
947+
* returns an html template as a string which should be used as the contents of this component.
948+
* Empty string by default.
949+
*
950+
* If `template` is a function, then it is {@link auto.$injector#invoke injected} with
951+
* the following locals:
952+
*
953+
* - `$element` - Current element
954+
* - `$attrs` - Current attributes object for the element
955+
*
956+
* - `templateUrl` – `{string=|function()=}` – path or function that returns a path to an html
957+
* template that should be used as the contents of this component.
958+
*
959+
* If `templateUrl` is a function, then it is {@link auto.$injector#invoke injected} with
960+
* the following locals:
961+
*
962+
* - `$element` - Current element
963+
* - `$attrs` - Current attributes object for the element
964+
*
965+
* - `bindings` – `{object=}` – defines bindings between DOM attributes and component properties.
966+
* Component properties are always bound to the component controller and not to the scope.
967+
* See {@link ng.$compile#-bindtocontroller- `bindToController`}.
968+
* - `transclude` – `{boolean=}` – whether {@link $compile#transclusion content transclusion} is enabled.
969+
* Disabled by default.
970+
* - `isolate` – `{boolean=}` – whether the new scope is isolated. Isolated by default.
971+
* - `restrict` - `{string=}` - a string containing one or more characters from {@link ng.$compile#-restrict- EACM},
972+
* which restricts the component to specific directive declaration style. If omitted, this defaults to 'E'.
973+
* - `$canActivate` – `{function()=}` – TBD.
974+
* - `$routeConfig` – `{object=}` – TBD.
975+
*
976+
* @returns {ng.$compileProvider} the compile provider itself, for chaining of function calls.
977+
* @description
978+
* Register a **Component definition** with the compiler. This is a shorthand for registering a special
979+
* type of directive, which represents a self-contained UI component in your application.
980+
*
981+
* Component definitions are very simple and do not require much of the complexity behind defining general
982+
* directives. Component definitions usually consist only of a template and a controller backing it.
983+
*
984+
* In order to make the definition easier, components enforce best practices like use of `controllerAs`,
985+
* `bindToController` and default behaviors like **isolate scope** and restriction to elements.
986+
*
987+
* Here are a few examples of how you would usually define components:
988+
*
989+
* ```js
990+
* var myMod = angular.module(...);
991+
* myMod.component('myComp', {
992+
* template: '<div>My name is {{myComp.name}}</div>',
993+
* controller: function() {
994+
* this.name = 'shahar';
995+
* }
996+
* });
997+
*
998+
* myMod.component('myComp', {
999+
* template: '<div>My name is {{myComp.name}}</div>',
1000+
* bindings: {name: '@'}
1001+
* });
1002+
*
1003+
* myMod.component('myComp', {
1004+
* templateUrl: 'views/my-comp.html',
1005+
* controller: 'MyCtrl as ctrl',
1006+
* bindings: {name: '@'}
1007+
* });
1008+
*
1009+
* ```
1010+
*
1011+
* <br />
1012+
* Components are also useful as route templates (e.g. when using
1013+
* {@link ngRoute ngRoute}):
1014+
*
1015+
* ```js
1016+
* var myMod = angular.module('myMod', ['ngRoute']);
1017+
*
1018+
* myMod.component('home', {
1019+
* template: '<h1>Home</h1><p>Hello, {{ home.user.name }} !</p>',
1020+
* controller: function() {
1021+
* this.user = {name: 'world'};
1022+
* }
1023+
* });
1024+
*
1025+
* myMod.config(function($routeProvider) {
1026+
* $routeProvider.when('/', {
1027+
* template: '<home></home>'
1028+
* });
1029+
* });
1030+
* ```
1031+
*
1032+
* <br />
1033+
* When using {@link ngRoute.$routeProvider $routeProvider}, you can often avoid some
1034+
* boilerplate, by assigning the resolved dependencies directly on the route scope:
1035+
*
1036+
* ```js
1037+
* var myMod = angular.module('myMod', ['ngRoute']);
1038+
*
1039+
* myMod.component('home', {
1040+
* template: '<h1>Home</h1><p>Hello, {{ home.user.name }} !</p>',
1041+
* bindings: {user: '='}
1042+
* });
1043+
*
1044+
* myMod.config(function($routeProvider) {
1045+
* $routeProvider.when('/', {
1046+
* template: '<home user="$resolve.user"></home>',
1047+
* resolve: {user: function($http) { return $http.get('...'); }}
1048+
* });
1049+
* });
1050+
* ```
1051+
*
1052+
* <br />
1053+
* See also {@link ng.$compileProvider#directive $compileProvider.directive()}.
1054+
*/
1055+
this.component = function registerComponent(name, options) {
1056+
function factory($injector) {
1057+
function makeInjectable(fn) {
1058+
if (isFunction(fn) || isArray(fn)) {
1059+
return function(tElement, tAttrs) {
1060+
return $injector.invoke(fn, this, {$element: tElement, $attrs: tAttrs});
1061+
};
1062+
} else {
1063+
return fn;
1064+
}
1065+
}
1066+
1067+
var template = (!options.template && !options.templateUrl ? '' : options.template);
1068+
return {
1069+
controller: options.controller || function() {},
1070+
controllerAs: identifierForController(options.controller) || options.controllerAs || name,
1071+
template: makeInjectable(template),
1072+
templateUrl: makeInjectable(options.templateUrl),
1073+
transclude: options.transclude,
1074+
scope: options.isolate === false ? true : {},
1075+
bindToController: options.bindings || {},
1076+
restrict: options.restrict || 'E'
1077+
};
1078+
}
1079+
1080+
if (options.$canActivate) {
1081+
factory.$canActivate = options.$canActivate;
1082+
}
1083+
if (options.$routeConfig) {
1084+
factory.$routeConfig = options.$routeConfig;
1085+
}
1086+
factory.$inject = ['$injector'];
1087+
1088+
return this.directive(name, factory);
1089+
};
1090+
9311091

9321092
/**
9331093
* @ngdoc method

0 commit comments

Comments
 (0)