From 992c790f0786fa45c1cc3710f29bf49c7c322ba7 Mon Sep 17 00:00:00 2001 From: Vojta Jina Date: Tue, 29 Nov 2011 21:51:59 -0800 Subject: refactor(scope): separate controller from scope Controller is standalone object, created using "new" operator, not messed up with scope anymore. Instead, related scope is injected as $scope. See design proposal: https://docs.google.com/document/pub?id=1SsgVj17ec6tnZEX3ugsvg0rVVR11wTso5Md-RdEmC0k Closes #321 Closes #425 Breaks controller methods are not exported to scope automatically Breaks Scope#$new() does not take controller as argument anymore--- src/directives.js | 69 ++++++++++++++++----------------- src/scenario/Runner.js | 11 +++++- src/service/compiler.js | 2 +- src/service/filter/filters.js | 16 ++++---- src/service/filter/limitTo.js | 6 +-- src/service/filter/orderBy.js | 6 +-- src/service/formFactory.js | 41 +++++++++++--------- src/service/http.js | 29 +++++++------- src/service/route.js | 10 +++-- src/service/scope.js | 19 ++------- src/widget/form.js | 4 +- src/widget/input.js | 89 ++++++++++++++++++++----------------------- src/widget/select.js | 38 ++++++++---------- src/widgets.js | 20 +++++----- 14 files changed, 178 insertions(+), 182 deletions(-) (limited to 'src') diff --git a/src/directives.js b/src/directives.js index 1d5b36f2..53d03573 100644 --- a/src/directives.js +++ b/src/directives.js @@ -97,28 +97,30 @@ angularDirective("ng:init", function(expression){
Name: @@ -156,16 +158,15 @@ angularDirective("ng:init", function(expression){ */ -angularDirective("ng:controller", function(expression){ - this.scope(function(scope){ - var Controller = - getter(scope, expression, true) || - getter(window, expression, true); +angularDirective("ng:controller", function(expression) { + this.scope(true); + return ['$injector', '$window', function($injector, $window) { + var scope = this, + Controller = getter(scope, expression, true) || getter($window, expression, true); + assertArgFn(Controller, expression); - inferInjectionArgs(Controller); - return Controller; - }); - return noop; + $injector.instantiate(Controller, {$scope: scope}); + }]; }); /** @@ -189,8 +190,8 @@ angularDirective("ng:controller", function(expression){
@@ -277,9 +278,9 @@ angularDirective("ng:bind", function(expression, element){
@@ -363,8 +364,8 @@ angularDirective("ng:bind-template", function(expression, element){
@@ -470,10 +471,10 @@ angularDirective("ng:click", function(expression, element){
@@ -69,8 +69,8 @@ function currencyFilter($locale) {
@@ -448,8 +448,8 @@ var uppercaseFilter = valueFn(uppercase);
diff --git a/src/service/filter/orderBy.js b/src/service/filter/orderBy.js index 2e5a0286..c67d2769 100644 --- a/src/service/filter/orderBy.js +++ b/src/service/filter/orderBy.js @@ -32,14 +32,14 @@
diff --git a/src/service/formFactory.js b/src/service/formFactory.js index 15a4733f..565b22a4 100644 --- a/src/service/formFactory.js +++ b/src/service/formFactory.js @@ -25,15 +25,13 @@ diff --git a/src/service/route.js b/src/service/route.js index 77d94e9c..04bcfdb6 100644 --- a/src/service/route.js +++ b/src/service/route.js @@ -63,8 +63,8 @@ */ function $RouteProvider(){ - this.$get = ['$rootScope', '$location', '$routeParams', - function( $rootScope, $location, $routeParams) { + this.$get = ['$rootScope', '$location', '$routeParams', '$injector', + function( $rootScope, $location, $routeParams, $injector) { /** * @ngdoc event * @name angular.module.ng.$route#$beforeRouteChange @@ -278,8 +278,10 @@ function $RouteProvider(){ } } else { copy(next.params, $routeParams); - (Controller = next.controller) && inferInjectionArgs(Controller); - next.scope = parentScope.$new(Controller); + next.scope = parentScope.$new(); + if (next.controller) { + $injector.instantiate(next.controller, {$scope: next.scope}); + } } } $rootScope.$broadcast('$afterRouteChange', next, last); diff --git a/src/service/scope.js b/src/service/scope.js index fe72c953..089e4a41 100644 --- a/src/service/scope.js +++ b/src/service/scope.js @@ -126,8 +126,9 @@ function $RootScopeProvider(){ * @function * * @description - * Creates a new child {@link angular.module.ng.$rootScope.Scope scope}. The new scope can optionally behave as a - * controller. The parent scope will propagate the {@link angular.module.ng.$rootScope.Scope#$digest $digest()} and + * Creates a new child {@link angular.module.ng.$rootScope.Scope scope}. + * + * The parent scope will propagate the {@link angular.module.ng.$rootScope.Scope#$digest $digest()} and * {@link angular.module.ng.$rootScope.Scope#$digest $digest()} events. The scope can be removed from the scope * hierarchy using {@link angular.module.ng.$rootScope.Scope#$destroy $destroy()}. * @@ -135,13 +136,10 @@ function $RootScopeProvider(){ * the scope and its child scopes to be permanently detached from the parent and thus stop * participating in model change detection and listener notification by invoking. * - * @param {function()=} Class Constructor function which the scope should be applied to the scope. - * @param {...*} curryArguments Any additional arguments which are curried into the constructor. - * See {@link guide/dev_guide.di dependency injection}. * @returns {Object} The newly created child scope. * */ - $new: function(Class, curryArguments) { + $new: function() { var Child = function() {}; // should be anonymous; This is so that when the minifier munges // the name it does not become random set of chars. These will then show up as class // name in the debugger. @@ -161,15 +159,6 @@ function $RootScopeProvider(){ } else { this.$$childHead = this.$$childTail = child; } - // short circuit if we have no class - if (Class) { - // can't use forEach, we need speed! - var ClassPrototype = Class.prototype; - for(var key in ClassPrototype) { - child[key] = bind(child, ClassPrototype[key]); - } - $injector.invoke(Class, child, curryArguments); - } return child; }, diff --git a/src/widget/form.js b/src/widget/form.js index 49e3a545..f3134db4 100644 --- a/src/widget/form.js +++ b/src/widget/form.js @@ -52,8 +52,8 @@
diff --git a/src/widget/input.js b/src/widget/input.js index a744e567..5db52704 100644 --- a/src/widget/input.js +++ b/src/widget/input.js @@ -31,9 +31,9 @@ var INTEGER_REGEXP = /^\s*(\-|\+)?\d+\s*$/;
@@ -96,8 +96,8 @@ var INTEGER_REGEXP = /^\s*(\-|\+)?\d+\s*$/;
@@ -136,9 +136,8 @@ var INTEGER_REGEXP = /^\s*(\-|\+)?\d+\s*$/; */ -angularInputType('email', function() { - var widget = this; - this.$on('$validate', function(event){ +angularInputType('email', function(element, widget) { + widget.$on('$validate', function(event) { var value = widget.$viewValue; widget.$emit(!value || value.match(EMAIL_REGEXP) ? "$valid" : "$invalid", "EMAIL"); }); @@ -170,8 +169,8 @@ angularInputType('email', function() {
@@ -210,9 +209,8 @@ angularInputType('email', function() { */ -angularInputType('url', function() { - var widget = this; - this.$on('$validate', function(event){ +angularInputType('url', function(element, widget) { + widget.$on('$validate', function(event) { var value = widget.$viewValue; widget.$emit(!value || value.match(URL_REGEXP) ? "$valid" : "$invalid", "URL"); }); @@ -239,8 +237,8 @@ angularInputType('url', function() {
@@ -270,7 +268,7 @@ angularInputType('url', function() { */ -angularInputType('list', function() { +angularInputType('list', function(element, widget) { function parse(viewValue) { var list = []; forEach(viewValue.split(/\s*,\s*/), function(value){ @@ -278,14 +276,14 @@ angularInputType('list', function() { }); return list; } - this.$parseView = function() { - isString(this.$viewValue) && (this.$modelValue = parse(this.$viewValue)); + widget.$parseView = function() { + isString(widget.$viewValue) && (widget.$modelValue = parse(widget.$viewValue)); }; - this.$parseModel = function() { - var modelValue = this.$modelValue; + widget.$parseModel = function() { + var modelValue = widget.$modelValue; if (isArray(modelValue) - && (!isString(this.$viewValue) || !equals(parse(this.$viewValue), modelValue))) { - this.$viewValue = modelValue.join(', '); + && (!isString(widget.$viewValue) || !equals(parse(widget.$viewValue), modelValue))) { + widget.$viewValue = modelValue.join(', '); } }; }); @@ -318,8 +316,8 @@ angularInputType('list', function() {
@@ -388,8 +386,8 @@ angularInputType('number', numericRegexpInputType(NUMBER_REGEXP, 'NUMBER'));
@@ -449,9 +447,9 @@ angularInputType('integer', numericRegexpInputType(INTEGER_REGEXP, 'INTEGER'));
@@ -477,9 +475,8 @@ angularInputType('integer', numericRegexpInputType(INTEGER_REGEXP, 'INTEGER')); */ -angularInputType('checkbox', function(inputElement) { - var widget = this, - trueValue = inputElement.attr('ng:true-value'), +angularInputType('checkbox', function(inputElement, widget) { + var trueValue = inputElement.attr('ng:true-value'), falseValue = inputElement.attr('ng:false-value'); if (!isString(trueValue)) trueValue = true; @@ -496,7 +493,7 @@ angularInputType('checkbox', function(inputElement) { }; widget.$parseModel = function() { - widget.$viewValue = this.$modelValue === trueValue; + widget.$viewValue = widget.$modelValue === trueValue; }; widget.$parseView = function() { @@ -522,8 +519,8 @@ angularInputType('checkbox', function(inputElement) {
@@ -545,9 +542,7 @@ angularInputType('checkbox', function(inputElement) { */ -angularInputType('radio', function(inputElement) { - var widget = this; - +angularInputType('radio', function(inputElement, widget) { //correct the name inputElement.attr('name', widget.$id + '@' + inputElement.attr('name')); inputElement.bind('click', function() { @@ -569,9 +564,8 @@ angularInputType('radio', function(inputElement) { function numericRegexpInputType(regexp, error) { - return ['$element', function(inputElement) { - var widget = this, - min = 1 * (inputElement.attr('min') || Number.MIN_VALUE), + return function(inputElement, widget) { + var min = 1 * (inputElement.attr('min') || Number.MIN_VALUE), max = 1 * (inputElement.attr('max') || Number.MAX_VALUE); widget.$on('$validate', function(event){ @@ -598,7 +592,7 @@ function numericRegexpInputType(regexp, error) { ? '' + widget.$modelValue : ''; }; - }]; + }; } @@ -640,8 +634,8 @@ var HTML5_INPUTS_TYPES = makeMap(
@@ -713,7 +707,8 @@ angularWidget('input', function(inputElement){ this.descend(true); var modelExp = inputElement.attr('ng:model'); return modelExp && - ['$defer', '$formFactory', '$element', function($defer, $formFactory, inputElement){ + ['$defer', '$formFactory', '$element', + function($defer, $formFactory, inputElement) { var form = $formFactory.forElement(inputElement), // We have to use .getAttribute, since jQuery tries to be smart and use the // type property. Trouble is some browser change unknown to text. @@ -762,7 +757,7 @@ angularWidget('input', function(inputElement){ } //TODO(misko): setting $inject is a hack - !TypeController.$inject && (TypeController.$inject = ['$element']); + !TypeController.$inject && (TypeController.$inject = ['$element', '$scope']); widget = form.$createWidget({ scope: modelScope, model: modelExp, @@ -866,7 +861,7 @@ angularWidget('textarea', angularWidget('input')); function watchElementProperty(modelScope, widget, name, element) { var bindAttr = fromJson(element.attr('ng:bind-attr') || '{}'), - match = /\s*{{(.*)}}\s*/.exec(bindAttr[name]), + match = /\s*\{\{(.*)\}\}\s*/.exec(bindAttr[name]), isBoolean = BOOLEAN_ATTR[name]; widget['$' + name] = isBoolean ? ( // some browsers return true some '' when required is set without value. diff --git a/src/widget/select.js b/src/widget/select.js index d4be91d9..b0f5eac5 100644 --- a/src/widget/select.js +++ b/src/widget/select.js @@ -65,15 +65,15 @@
@@ -140,11 +140,11 @@ angularWidget('select', function(element){ optionsExp = selectElement.attr('ng:options'), modelExp = selectElement.attr('ng:model'), widget = form.$createWidget({ - scope: this, + scope: modelScope, model: modelExp, onChange: selectElement.attr('ng:change'), alias: selectElement.attr('name'), - controller: optionsExp ? Options : (multiple ? Multiple : Single)}); + controller: ['$scope', optionsExp ? Options : (multiple ? Multiple : Single)]}); selectElement.bind('$destroy', function() { widget.$destroy(); }); @@ -174,11 +174,9 @@ angularWidget('select', function(element){ //////////////////////////// - function Multiple() { - var widget = this; - - this.$render = function() { - var items = new HashMap(this.$viewValue); + function Multiple(widget) { + widget.$render = function() { + var items = new HashMap(widget.$viewValue); forEach(selectElement.children(), function(option){ option.selected = isDefined(items.get(option.value)); }); @@ -198,9 +196,7 @@ angularWidget('select', function(element){ } - function Single() { - var widget = this; - + function Single(widget) { widget.$render = function() { selectElement.val(widget.$viewValue); }; @@ -214,9 +210,8 @@ angularWidget('select', function(element){ widget.$viewValue = selectElement.val(); } - function Options() { - var widget = this, - match; + function Options(widget) { + var match; if (! (match = optionsExp.match(NG_OPTIONS_REGEXP))) { throw Error( @@ -224,8 +219,7 @@ angularWidget('select', function(element){ " but got '" + optionsExp + "'."); } - var widgetScope = this, - displayFn = $parse(match[2] || match[1]), + var displayFn = $parse(match[2] || match[1]), valueName = match[4] || match[6], keyName = match[5], groupByFn = $parse(match[3] || ''), @@ -253,7 +247,7 @@ angularWidget('select', function(element){ selectElement.html(''); // clear contents selectElement.bind('change', function() { - widgetScope.$apply(function() { + widget.$apply(function() { var optionGroup, collection = valuesFn(modelScope) || [], key = selectElement.val(), @@ -288,13 +282,13 @@ angularWidget('select', function(element){ } } if (isDefined(value) && modelScope.$viewVal !== value) { - widgetScope.$emit('$viewChange', value); + widget.$emit('$viewChange', value); } }); }); - widgetScope.$watch(render); - widgetScope.$render = render; + widget.$watch(render); + widget.$render = render; function render() { var optionGroups = {'':[]}, // Temporary location for the option groups before we render them diff --git a/src/widgets.js b/src/widgets.js index 09a800de..6b3e93ee 100644 --- a/src/widgets.js +++ b/src/widgets.js @@ -54,11 +54,11 @@
@@ -171,9 +171,9 @@ angularWidget('ng:include', function(element){
@@ -701,10 +701,10 @@ angularWidget('ng:view', function(element) {
-- cgit v1.2.3