diff --git a/benchmarks/largetable-bp/main.html b/benchmarks/largetable-bp/main.html
index 471a4441dd9e..b4f90a4cc951 100644
--- a/benchmarks/largetable-bp/main.html
+++ b/benchmarks/largetable-bp/main.html
@@ -20,6 +20,7 @@
interpolation + fnInvocation:
ngBind + filter:
interpolation + filter:
+ ngModel:
@@ -84,6 +85,13 @@ interpolation with filter
{{column.i | noop}}:{{column.j | noop}}|
+
diff --git a/src/ng/compile.js b/src/ng/compile.js
index b1d4185f6d23..f8c7367fa3cc 100644
--- a/src/ng/compile.js
+++ b/src/ng/compile.js
@@ -1614,7 +1614,6 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
var terminalPriority = -Number.MAX_VALUE,
newScopeDirective,
controllerDirectives = previousCompileContext.controllerDirectives,
- controllers,
newIsolateScopeDirective = previousCompileContext.newIsolateScopeDirective,
templateDirective = previousCompileContext.templateDirective,
nonTlbTranscludeDirective = previousCompileContext.nonTlbTranscludeDirective,
@@ -1672,7 +1671,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
if (!directive.templateUrl && directive.controller) {
directiveValue = directive.controller;
- controllerDirectives = controllerDirectives || {};
+ controllerDirectives = controllerDirectives || createMap();
assertNoDuplicate("'" + directiveName + "' controller",
controllerDirectives[directiveName], directive, $compileNode);
controllerDirectives[directiveName] = directive;
@@ -1840,49 +1839,75 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
function getControllers(directiveName, require, $element, elementControllers) {
- var value, retrievalMethod = 'data', optional = false;
- var $searchElement = $element;
- var match;
- if (isString(require)) {
- match = require.match(REQUIRE_PREFIX_REGEXP);
- require = require.substring(match[0].length);
-
- if (match[3]) {
- if (match[1]) match[3] = null;
- else match[1] = match[3];
- }
- if (match[1] === '^') {
- retrievalMethod = 'inheritedData';
- } else if (match[1] === '^^') {
- retrievalMethod = 'inheritedData';
- $searchElement = $element.parent();
- }
- if (match[2] === '?') {
- optional = true;
- }
+ var i, value;
- value = null;
+ if (typeof require === 'string') {
+ var match = require.match(REQUIRE_PREFIX_REGEXP);
+ var name = require.substring(match[0].length);
+ var type = match[1] || match[3];
- if (elementControllers && retrievalMethod === 'data') {
- if (value = elementControllers[require]) {
- value = value.instance;
- }
+ //If only parents then start at the parent element
+ //Otherwise attempt getting the controller from elementControllers to avoid .data
+ if (type === '^^') {
+ $element = $element.parent();
+ } else {
+ value = elementControllers && elementControllers[name];
+ value = value && value.instance;
+ }
+
+ if (!value) {
+ var dataName = '$' + name + 'Controller';
+ value = type ? $element.inheritedData(dataName) : $element.data(dataName);
}
- value = value || $searchElement[retrievalMethod]('$' + require + 'Controller');
- if (!value && !optional) {
+ if (!value && match[2] !== '?') {
throw $compileMinErr('ctreq',
"Controller '{0}', required by directive '{1}', can't be found!",
- require, directiveName);
+ name, directiveName);
}
+
return value || null;
- } else if (isArray(require)) {
- value = [];
- forEach(require, function(require) {
- value.push(getControllers(directiveName, require, $element, elementControllers));
- });
}
- return value;
+
+ if (isArray(require)) {
+ value = new Array(i = require.length);
+ while (i--) {
+ value[i] = getControllers(directiveName, require[i], $element, elementControllers);
+ }
+ return value;
+ }
+ }
+
+ function setupControllers(scope, isolateScope, $element, attrs, transcludeFn, elementControllers) {
+ // For directives with element transclusion the element is a comment,
+ // but jQuery .data doesn't support attaching data to comment nodes as it's hard to
+ // clean up (http://bugs.jquery.com/ticket/8335).
+ // Instead, we save the controllers for the element in a local hash and attach to .data
+ // later, once we have the actual element.
+ var controllerData = !hasElementTranscludeDirective && $element.data();
+
+ for (var directiveName in controllerDirectives) {
+ var directive = controllerDirectives[directiveName];
+
+ var locals = {
+ $scope: directive === newIsolateScopeDirective || directive.$$isolateScope ? isolateScope : scope,
+ $element: $element,
+ $attrs: attrs,
+ $transclude: transcludeFn
+ };
+
+ var directiveController = directive.controller;
+ if (directiveController === '@') {
+ directiveController = attrs[directive.name];
+ }
+
+ var controllerInstance = $controller(directiveController, locals, true, directive.controllerAs);
+
+ elementControllers[directive.name] = controllerInstance;
+ if (controllerData) {
+ controllerData['$' + directive.name + 'Controller'] = controllerInstance.instance;
+ }
+ }
}
@@ -1911,36 +1936,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
}
if (controllerDirectives) {
- // TODO: merge `controllers` and `elementControllers` into single object.
- controllers = {};
- elementControllers = {};
- forEach(controllerDirectives, function(directive) {
- var locals = {
- $scope: directive === newIsolateScopeDirective || directive.$$isolateScope ? isolateScope : scope,
- $element: $element,
- $attrs: attrs,
- $transclude: transcludeFn
- }, controllerInstance;
-
- controller = directive.controller;
- if (controller == '@') {
- controller = attrs[directive.name];
- }
-
- controllerInstance = $controller(controller, locals, true, directive.controllerAs);
-
- // For directives with element transclusion the element is a comment,
- // but jQuery .data doesn't support attaching data to comment nodes as it's hard to
- // clean up (http://bugs.jquery.com/ticket/8335).
- // Instead, we save the controllers for the element in a local hash and attach to .data
- // later, once we have the actual element.
- elementControllers[directive.name] = controllerInstance;
- if (!hasElementTranscludeDirective) {
- $element.data('$' + directive.name + 'Controller', controllerInstance.instance);
- }
-
- controllers[directive.name] = controllerInstance;
- });
+ setupControllers(scope, isolateScope, $element, attrs, transcludeFn, elementControllers = createMap());
}
if (newIsolateScopeDirective) {
@@ -1954,14 +1950,14 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
isolateScope.$$isolateBindings,
newIsolateScopeDirective, isolateScope);
}
- if (controllers) {
+ if (elementControllers) {
// Initialize bindToController bindings for new/isolate scopes
var scopeDirective = newIsolateScopeDirective || newScopeDirective;
var bindings;
var controllerForBindings;
- if (scopeDirective && controllers[scopeDirective.name]) {
+ if (scopeDirective && elementControllers[scopeDirective.name]) {
bindings = scopeDirective.$$bindings.bindToController;
- controller = controllers[scopeDirective.name];
+ controller = elementControllers[scopeDirective.name];
if (controller && controller.identifier && bindings) {
controllerForBindings = controller;
@@ -1970,7 +1966,9 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
bindings, scopeDirective);
}
}
- forEach(controllers, function(controller) {
+ // Initialize the controllers before linking
+ for (i in elementControllers) {
+ controller = elementControllers[i];
var result = controller();
if (result !== controller.instance &&
controller === controllerForBindings) {
@@ -1980,8 +1978,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
initializeDirectiveBindings(scope, attrs, result,
bindings, scopeDirective);
}
- });
- controllers = null;
+ }
}
// PRELINKING