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

Commit 69c7187

Browse files
committed
refactor: move component helper to the $compileProvider
The Module.component() 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
1 parent d28ae21 commit 69c7187

File tree

4 files changed

+319
-278
lines changed

4 files changed

+319
-278
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

+158-2
Original file line numberDiff line numberDiff line change
@@ -867,8 +867,8 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
867867
* @param {string|Object} name Name of the directive in camel-case (i.e. <code>ngBind</code> which
868868
* will match as <code>ng-bind</code>), or an object map of directives where the keys are the
869869
* names and the values are the factories.
870-
* @param {Function|Array} directiveFactory An injectable directive factory function. See
871-
* {@link guide/directive} for more info.
870+
* @param {Function|Array} directiveFactory An injectable directive factory function. See the
871+
* {@link guide/directive directive guide} and the {@link $compile compile API} for more info.
872872
* @returns {ng.$compileProvider} Self for chaining.
873873
*/
874874
this.directive = function registerDirective(name, directiveFactory) {
@@ -915,6 +915,162 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
915915
return this;
916916
};
917917

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

9191075
/**
9201076
* @ngdoc method

0 commit comments

Comments
 (0)