feat(ionContent): use child scope instead of isolate scope

Adds new '$ionicBind' service, which takes an object containing
binding definitions (similar to angular directive isolate scope
definition).  Allows binding of any directive attribute & expressions
from a scope, letting us do normal attribute -> scope binding
without having to create isolate scopes.

Closes #555. Closes #669
This commit is contained in:
Andy Joslin
2014-02-24 08:08:17 -05:00
parent 9e74608c36
commit 49e0dac999
7 changed files with 268 additions and 35 deletions

View File

@@ -18,33 +18,23 @@ angular.module('ionic.ui.content', ['ionic.ui.service', 'ionic.ui.scroll'])
// The content directive is a core scrollable content area
// that is part of many View hierarchies
.directive('ionContent', ['$parse', '$timeout', '$ionicScrollDelegate', '$controller', function($parse, $timeout, $ionicScrollDelegate, $controller) {
.directive('ionContent', [
'$parse',
'$timeout',
'$ionicScrollDelegate',
'$controller',
'$ionicBind',
function($parse, $timeout, $ionicScrollDelegate, $controller, $ionicBind) {
return {
restrict: 'E',
replace: true,
template: '<div class="scroll-content"><div class="scroll" ng-transclude></div></div>',
transclude: true,
require: '^?ionNavView',
scope: {
onRefresh: '&',
onRefreshOpening: '&',
onScroll: '&',
onScrollComplete: '&',
refreshComplete: '=',
onInfiniteScroll: '=',
infiniteScrollDistance: '@',
hasBouncing: '@',
scroll: '@',
padding: '@',
hasScrollX: '@',
hasScrollY: '@',
scrollbarX: '@',
scrollbarY: '@',
startX: '@',
startY: '@',
scrollEventInterval: '@'
},
scope: true,
template:
'<div class="scroll-content">' +
'<div class="scroll"></div>' +
'</div>',
compile: function(element, attr, transclude) {
if(attr.hasHeader == "true") { element.addClass('has-header'); }
if(attr.hasSubheader == "true") { element.addClass('has-subheader'); }
@@ -60,7 +50,31 @@ angular.module('ionic.ui.content', ['ionic.ui.service', 'ionic.ui.scroll'])
function prelink($scope, $element, $attr, navViewCtrl) {
var clone, sc, scrollView, scrollCtrl,
c = angular.element($element.children()[0]);
scrollContent = angular.element($element[0].querySelector('.scroll'));
transclude($scope, function(clone) {
scrollContent.append(clone);
});
$ionicBind($scope, $attr, {
onRefresh: '&',
onRefreshOpening: '&',
onScroll: '&',
onScrollComplete: '&',
refreshComplete: '=',
onInfiniteScroll: '&',
infiniteScrollDistance: '@',
hasBouncing: '@',
scroll: '@',
padding: '@',
hasScrollX: '@',
hasScrollY: '@',
scrollbarX: '@',
scrollbarY: '@',
startX: '@',
startY: '@',
scrollEventInterval: '@'
});
if($scope.scroll === "false") {
// No scrolling
@@ -92,6 +106,7 @@ angular.module('ionic.ui.content', ['ionic.ui.service', 'ionic.ui.scroll'])
}
}
});
//Publish scrollView to parent so children can access it
scrollView = $scope.$parent.scrollView = scrollCtrl.scrollView;

View File

@@ -3,6 +3,7 @@
* modules.
*/
angular.module('ionic.service', [
'ionic.service.bind',
'ionic.service.platform',
'ionic.service.actionSheet',
'ionic.service.gesture',

53
js/ext/angular/src/service/ionicBind.js vendored Normal file
View File

@@ -0,0 +1,53 @@
angular.module('ionic.service.bind', [])
.factory('$ionicBind', ['$parse', '$interpolate', function($parse, $interpolate) {
var LOCAL_REGEXP = /^\s*([@=&])(\??)\s*(\w*)\s*$/;
return function(scope, attrs, bindDefinition) {
angular.forEach(bindDefinition || {}, function (definition, scopeName) {
//Adapted from angular.js $compile
var match = definition.match(LOCAL_REGEXP) || [],
attrName = match[3] || scopeName,
mode = match[1], // @, =, or &
parentGet,
unwatch;
switch(mode) {
case '@':
if (!attrs[attrName]) {
return;
}
attrs.$observe(attrName, function(value) {
scope[scopeName] = value;
});
// we trigger an interpolation to ensure
// the value is there for use immediately
if (attrs[attrName]) {
scope[scopeName] = $interpolate(attrs[attrName])(scope);
}
break;
case '=':
if (!attrs[attrName]) {
return;
}
unwatch = scope.$watch(attrs[attrName], function(value) {
scope[scopeName] = value;
});
//Destroy parent scope watcher when this scope is destroyed
scope.$on('$destroy', unwatch);
break;
case '&':
/* jshint -W044 */
if (attrs[attrName] && attrs[attrName].match(RegExp(scopeName + '\(.*?\)'))) {
throw new Error('& expression binding "' + scopeName + '" looks like it will recursively call "' +
attrs[attrName] + '" and cause a stack overflow! Please choose a different scopeName.');
}
parentGet = $parse(attrs[attrName]);
scope[scopeName] = function(locals) {
return parentGet(scope, locals);
};
break;
}
});
};
}]);