175175 * by calling the `localFn` as `localFn({amount: 22})`.
176176 *
177177 *
178+ * #### `bindToController`
179+ * When an isolate scope is used for a component (see above), and `controllerAs` is used, `bindToController` will
180+ * allow a component to have its properties bound to the controller, rather than to scope. When the controller
181+ * is instantiated, the initial values of the isolate scope bindings are already available.
178182 *
179183 * #### `controller`
180184 * Controller constructor function. The controller is instantiated before the
@@ -877,8 +881,8 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
877881 ? JQLitePrototype . clone . call ( $compileNodes ) // IMPORTANT!!!
878882 : $compileNodes ;
879883
880- forEach ( transcludeControllers , function ( instance , name ) {
881- $linkNode . data ( '$' + name + 'Controller' , instance ) ;
884+ forEach ( transcludeControllers , function ( controllerInstance , name ) {
885+ $linkNode . data ( '$' + name + 'Controller' , controllerInstance . instance ) ;
882886 } ) ;
883887
884888 $linkNode . data ( '$scope' , scope ) ;
@@ -1195,6 +1199,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
11951199 var terminalPriority = - Number . MAX_VALUE ,
11961200 newScopeDirective ,
11971201 controllerDirectives = previousCompileContext . controllerDirectives ,
1202+ controllers ,
11981203 newIsolateScopeDirective = previousCompileContext . newIsolateScopeDirective ,
11991204 templateDirective = previousCompileContext . templateDirective ,
12001205 nonTlbTranscludeDirective = previousCompileContext . nonTlbTranscludeDirective ,
@@ -1431,7 +1436,9 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
14311436 value = null ;
14321437
14331438 if ( elementControllers && retrievalMethod === 'data' ) {
1434- value = elementControllers [ require ] ;
1439+ if ( value = elementControllers [ require ] ) {
1440+ value = value . instance ;
1441+ }
14351442 }
14361443 value = value || $element [ retrievalMethod ] ( '$' + require + 'Controller' ) ;
14371444
@@ -1460,9 +1467,43 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
14601467 $element = attrs . $$element ;
14611468
14621469 if ( newIsolateScopeDirective ) {
1463- var LOCAL_REGEXP = / ^ \s * ( [ @ = & ] ) ( \? ? ) \s * ( \w * ) \s * $ / ;
1464-
14651470 isolateScope = scope . $new ( true ) ;
1471+ }
1472+
1473+ transcludeFn = boundTranscludeFn && controllersBoundTransclude ;
1474+ if ( controllerDirectives ) {
1475+ controllers = { } ;
1476+ forEach ( controllerDirectives , function ( directive ) {
1477+ var locals = {
1478+ $scope : directive === newIsolateScopeDirective || directive . $$isolateScope ? isolateScope : scope ,
1479+ $element : $element ,
1480+ $attrs : attrs ,
1481+ $transclude : transcludeFn
1482+ } , controllerInstance ;
1483+
1484+ controller = directive . controller ;
1485+ if ( controller == '@' ) {
1486+ controller = attrs [ directive . name ] ;
1487+ }
1488+
1489+ controllerInstance = $controller ( controller , locals , true , directive . controllerAs ) ;
1490+
1491+ // For directives with element transclusion the element is a comment,
1492+ // but jQuery .data doesn't support attaching data to comment nodes as it's hard to
1493+ // clean up (http://bugs.jquery.com/ticket/8335).
1494+ // Instead, we save the controllers for the element in a local hash and attach to .data
1495+ // later, once we have the actual element.
1496+ elementControllers [ directive . name ] = controllerInstance ;
1497+ if ( ! hasElementTranscludeDirective ) {
1498+ $element . data ( '$' + directive . name + 'Controller' , controllerInstance . instance ) ;
1499+ }
1500+
1501+ controllers [ directive . name ] = controllerInstance ;
1502+ } ) ;
1503+ }
1504+
1505+ if ( newIsolateScopeDirective ) {
1506+ var LOCAL_REGEXP = / ^ \s * ( [ @ = & ] ) ( \? ? ) \s * ( \w * ) \s * $ / ;
14661507
14671508 if ( templateDirective && ( templateDirective === newIsolateScopeDirective ||
14681509 templateDirective === newIsolateScopeDirective . $$originalDirective ) ) {
@@ -1475,27 +1516,35 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
14751516
14761517 safeAddClass ( $element , 'ng-isolate-scope' ) ;
14771518
1519+ var isolateScopeController = controllers && controllers [ newIsolateScopeDirective . name ] ;
1520+ var isolateBindingsContext = isolateScope ;
1521+ if ( isolateScopeController && isolateScopeController . identifier &&
1522+ newIsolateScopeDirective . bindToController === true ) {
1523+ isolateBindingsContext = isolateScopeController . instance ;
1524+ }
14781525 forEach ( newIsolateScopeDirective . scope , function ( definition , scopeName ) {
14791526 var match = definition . match ( LOCAL_REGEXP ) || [ ] ,
1480- attrName = match [ 3 ] || scopeName ,
1527+ attrName ,
14811528 optional = ( match [ 2 ] == '?' ) ,
14821529 mode = match [ 1 ] , // @, =, or &
14831530 lastValue ,
14841531 parentGet , parentSet , compare ;
14851532
1533+ attrName = match [ 3 ] || scopeName ;
1534+
14861535 isolateScope . $$isolateBindings [ scopeName ] = mode + attrName ;
14871536
14881537 switch ( mode ) {
14891538
14901539 case '@' :
14911540 attrs . $observe ( attrName , function ( value ) {
1492- isolateScope [ scopeName ] = value ;
1541+ isolateBindingsContext [ scopeName ] = value ;
14931542 } ) ;
14941543 attrs . $$observers [ attrName ] . $$scope = scope ;
14951544 if ( attrs [ attrName ] ) {
14961545 // If the attribute has been provided then we trigger an interpolation to ensure
14971546 // the value is there for use in the link fn
1498- isolateScope [ scopeName ] = $interpolate ( attrs [ attrName ] ) ( scope ) ;
1547+ isolateBindingsContext [ scopeName ] = $interpolate ( attrs [ attrName ] ) ( scope ) ;
14991548 }
15001549 break ;
15011550
@@ -1511,21 +1560,21 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
15111560 }
15121561 parentSet = parentGet . assign || function ( ) {
15131562 // reset the change, or we will throw this exception on every $digest
1514- lastValue = isolateScope [ scopeName ] = parentGet ( scope ) ;
1563+ lastValue = isolateBindingsContext [ scopeName ] = parentGet ( scope ) ;
15151564 throw $compileMinErr ( 'nonassign' ,
15161565 "Expression '{0}' used with directive '{1}' is non-assignable!" ,
15171566 attrs [ attrName ] , newIsolateScopeDirective . name ) ;
15181567 } ;
1519- lastValue = isolateScope [ scopeName ] = parentGet ( scope ) ;
1568+ lastValue = isolateBindingsContext [ scopeName ] = parentGet ( scope ) ;
15201569 var unwatch = scope . $watch ( $parse ( attrs [ attrName ] , function parentValueWatch ( parentValue ) {
1521- if ( ! compare ( parentValue , isolateScope [ scopeName ] ) ) {
1570+ if ( ! compare ( parentValue , isolateBindingsContext [ scopeName ] ) ) {
15221571 // we are out of sync and need to copy
15231572 if ( ! compare ( parentValue , lastValue ) ) {
15241573 // parent changed and it has precedence
1525- isolateScope [ scopeName ] = parentValue ;
1574+ isolateBindingsContext [ scopeName ] = parentValue ;
15261575 } else {
15271576 // if the parent can be assigned then do so
1528- parentSet ( scope , parentValue = isolateScope [ scopeName ] ) ;
1577+ parentSet ( scope , parentValue = isolateBindingsContext [ scopeName ] ) ;
15291578 }
15301579 }
15311580 return lastValue = parentValue ;
@@ -1535,7 +1584,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
15351584
15361585 case '&' :
15371586 parentGet = $parse ( attrs [ attrName ] ) ;
1538- isolateScope [ scopeName ] = function ( locals ) {
1587+ isolateBindingsContext [ scopeName ] = function ( locals ) {
15391588 return parentGet ( scope , locals ) ;
15401589 } ;
15411590 break ;
@@ -1548,36 +1597,12 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
15481597 }
15491598 } ) ;
15501599 }
1551- transcludeFn = boundTranscludeFn && controllersBoundTransclude ;
1552- if ( controllerDirectives ) {
1553- forEach ( controllerDirectives , function ( directive ) {
1554- var locals = {
1555- $scope : directive === newIsolateScopeDirective || directive . $$isolateScope ? isolateScope : scope ,
1556- $element : $element ,
1557- $attrs : attrs ,
1558- $transclude : transcludeFn
1559- } , controllerInstance ;
15601600
1561- controller = directive . controller ;
1562- if ( controller == '@' ) {
1563- controller = attrs [ directive . name ] ;
1564- }
1565-
1566- controllerInstance = $controller ( controller , locals ) ;
1567- // For directives with element transclusion the element is a comment,
1568- // but jQuery .data doesn't support attaching data to comment nodes as it's hard to
1569- // clean up (http://bugs.jquery.com/ticket/8335).
1570- // Instead, we save the controllers for the element in a local hash and attach to .data
1571- // later, once we have the actual element.
1572- elementControllers [ directive . name ] = controllerInstance ;
1573- if ( ! hasElementTranscludeDirective ) {
1574- $element . data ( '$' + directive . name + 'Controller' , controllerInstance ) ;
1575- }
1576-
1577- if ( directive . controllerAs ) {
1578- locals . $scope [ directive . controllerAs ] = controllerInstance ;
1579- }
1601+ if ( controllers ) {
1602+ forEach ( controllers , function ( controller , key ) {
1603+ controller ( ) ;
15801604 } ) ;
1605+ controllers = null ;
15811606 }
15821607
15831608 // PRELINKING
0 commit comments