feat(ionic): remove all delegates

BREAKING CHANGE: $ionicScrollDelegate, $ionicSlideBoxDelegate, and
$ionicSideMenuDelegate have been removed.

  - $ionicScrollDelegate has been changed to $ionicScrollController.
    Documentation:
    [ionContent](
    http://ajoslin.github.io/docs/nightly/api/directive/ionContent),
    [ionScroll](
    http://ajoslin.github.io/docs/nightly/api/directive/ionScroll)

    Change your code from this:

    ```html
    <ion-content ng-controller="MyCtrl">
      <button ng-click="scrollBottom()">Scroll to bottom!</button>
    </ion-content>
    ```
    ```js
    function MyCtrl($scope, $ionicScrollDelegate) {
      $scope.scrollBottom = function() {
        $ionicScrollDelegate.scrollBottom();
      };
    }
    ```

    To this:

    ```html
    <!-- optional attr controller-bind, see docs -->
    <ion-content ng-controller="MyCtrl">
      <button ng-click="scrollBottom()">Scroll to bottom!</button>
    </ion-content>
    ```
    ```js
    function MyCtrl($scope) {
      $scope.scrollBottom = function() {
        $scope.$ionicScrollController.scrollBottom();
      };
    }
    ```

  - $ionicSideMenuDelegate has been changed to
    $ionicSideMenusController. Documentation:
    [ionSideMenus](http://ajoslin.github.io/docs/nightly/api/directive/ionSideMenus)

    Change your code from this:

    ```html
    <ion-side-menus>
      <ion-side-menu side="left">Side Menu Left</ion-side-menu>
      <ion-pane ion-side-menu-content ng-controller="MyCtrl">
        <button ng-click="toggleLeftMenu()">
          Toggle Left Menu!
        </button>
      </ion-pane>
    </ion-side-menus>
    ```
    ```js
    function MyCtrl($scope, $ionicSideMenuDelegate) {
      $scope.toggleLeftMenu = function() {
        $ionicSideMenuDelegate.toggleLeft();
      };
    }
    ```

    To this:

    ```html
    <!-- optional attr controller-bind, see documentation -->
    <ion-side-menus>
      <ion-side-menu side="left">Side Menu Left</ion-side-menu>
      <ion-pane ion-side-menu-content ng-controller="MyCtrl">
        <button ng-click="toggleLeftMenu()">
          Toggle Left Menu!
        </button>
      </ion-pane>
    </ion-side-menus>
    ```
    ```js
    function MyCtrl($scope) {
      $scope.toggleLeftMenu = function() {
        $scope.$ionicSideMenuController.toggleLeft();
      };
    }
    ```

  - $ionicSlideBoxDelegate has been removed and upgraded to
    $ionicSlideBoxController. It had only one method that
    was unneeded.  [Documentation](
    http://ajoslin.github.io/docs/nightly/api/directive/ionSlideBox)
This commit is contained in:
Andy Joslin
2014-03-19 11:36:35 -06:00
parent 630c6481ff
commit dbe4e3901d
23 changed files with 436 additions and 693 deletions

View File

@@ -6,15 +6,36 @@ angular.module('ionic.ui.scroll')
/**
* @private
*/
.controller('$ionicScroll', ['$scope', 'scrollViewOptions', '$timeout', '$ionicScrollDelegate', '$window', function($scope, scrollViewOptions, $timeout, $ionicScrollDelegate, $window) {
.factory('$$scrollValueCache', function() {
return {};
})
.controller('$ionicScroll', [
'$scope',
'scrollViewOptions',
'$timeout',
'$window',
'$$scrollValueCache',
'$location',
'$parse',
'$rootScope',
'$document',
function($scope, scrollViewOptions, $timeout, $window, $$scrollValueCache, $location, $parse, $rootScope, $document) {
var self = this;
var element = this.element = scrollViewOptions.el;
var $element = this.$element = angular.element(element);
var scrollView = this.scrollView = new ionic.views.Scroll(scrollViewOptions);
this.$scope = $scope;
$scope.$parent.$$ionicScrollController = this;
//Attach self to element as a controller so other directives can require this controller
//through `require: '$ionicScroll'
//Also attach to parent so that sibling elements can require this
($element.parent().length ? $element.parent() : $element)
.data('$$ionicScrollController', this);
$parse(scrollViewOptions.controllerBind || '$ionicScrollController')
.assign($scope.$parent, this);
if (!angular.isDefined(scrollViewOptions.bouncing)) {
ionic.Platform.ready(function() {
@@ -22,42 +43,107 @@ angular.module('ionic.ui.scroll')
});
}
var $element = this.$element = angular.element(element);
//Attach self to element as a controller so other directives can require this controller
//through `require: '$ionicScroll'
$element.data('$$ionicScrollController', this);
//Register delegate for event handling
$ionicScrollDelegate.register($scope, $element, scrollView);
var resize = angular.bind(scrollView, scrollView.resize);
$window.addEventListener('resize', resize);
$scope.$on('$viewContentLoaded', function(e, historyData) {
if (e.defaultPrevented) {
return;
}
//only the top-most scroll area under a view should remember that view's
//scroll position
e.preventDefault();
var values = historyData && historyData.rememberedScrollValues;
if (values) {
$timeout(function() {
scrollView.scrollTo(+values.left || null, +values.top || null);
}, 0, false);
}
$scope.$on('$destroy', function() {
historyData && (historyData.rememberedScrollValues = scrollView.getValues());
});
});
// set by rootScope listener if needed
var backListenDone = angular.noop;
$scope.$on('$destroy', function() {
$window.removeEventListener('resize', resize);
backListenDone();
if (self._rememberScrollId) {
$$scrollValueCache[self._rememberScrollId] = scrollView.getValues();
}
});
this.setRefresher = function(refresherScope, refresherElement) {
$element.on('scroll', function(e) {
var detail = (e.originalEvent || e).detail || {};
$scope.$onScroll && $scope.$onScroll({
event: e,
scrollTop: detail.scrollTop || 0,
scrollLeft: detail.scrollLeft || 0
});
});
$scope.$on('$viewContentLoaded', function(e, historyData) {
//only the top-most scroll area under a view should remember that view's
//scroll position
if (e.defaultPrevented) { return; }
e.preventDefault();
var viewId = historyData.viewId;
self.rememberScrollPosition(viewId);
self.scrollToRememberedPosition();
backListenDone = $rootScope.$on('$viewHistory.viewBack', function(e, fromViewId, toViewId) {
//When going back from this view, forget its saved scroll position
if (viewId === fromViewId) {
self.forgetScrollPosition();
}
});
});
$timeout(function() {
scrollView.run();
});
this._rememberScrollId = null;
this.rememberScrollPosition = function(id) {
if (!id) {
throw new Error("Must supply an id to remember the scroll by!");
}
this._rememberScrollId = id;
};
this.forgetScrollPosition = function() {
delete $$scrollValueCache[this._rememberScrollId];
this._rememberScrollId = null;
};
this.scrollToRememberedPosition = function(shouldAnimate) {
var values = $$scrollValueCache[this._rememberScrollId];
if (values) {
scrollView.scrollTo(+values.left, +values.top, shouldAnimate);
}
};
this.resize = function() {
return $timeout(resize);
};
this.scrollTop = function(shouldAnimate) {
this.resize().then(function() {
scrollView.scrollTo(0, 0, !!shouldAnimate);
});
};
this.scrollBottom = function(shouldAnimate) {
this.resize().then(function() {
var max = scrollView.getScrollMax();
scrollView.scrollTo(max.left, max.top, !!shouldAnimate);
});
};
this.scrollTo = function(left, top, shouldAnimate) {
this.resize().then(function() {
scrollView.scrollTo(left, top, !!shouldAnimate);
});
};
this.anchorScroll = function(shouldAnimate) {
this.resize().then(function() {
var hash = $location.hash();
var elm = hash && $document[0].getElementById(hash);
if (hash && elm) {
var scroll = ionic.DomUtil.getPositionInParent(elm, self.$element);
scrollView.scrollTo(scroll.left, scroll.top, !!shouldAnimate);
} else {
scrollView.scrollTo(0,0, !!shouldAnimate);
}
});
};
/**
* @private
*/
this._setRefresher = function(refresherScope, refresherElement) {
var refresher = this.refresher = refresherElement;
var refresherHeight = self.refresher.clientHeight || 0;
scrollView.activatePullToRefresh(refresherHeight, function() {
@@ -71,10 +157,6 @@ angular.module('ionic.ui.scroll')
refresherScope.$onRefresh();
});
};
$timeout(function() {
scrollView.run();
});
}]);
})();

View File

@@ -3,11 +3,36 @@
angular.module('ionic.ui.header', ['ngAnimate', 'ngSanitize'])
.directive('barHeader', ['$ionicScrollDelegate', function($ionicScrollDelegate) {
.directive('barHeader', ['$document', function($document) {
return {
restrict: 'C',
link: function($scope, $element, $attr) {
$ionicScrollDelegate($scope).tapScrollToTop($element);
ionic.requestAnimationFrame(function() {
var scrollCtrl = $element.controller('$ionicScroll');
if (!scrollCtrl) {
return;
}
ionic.on('tap', onTap, $element[0]);
$scope.$on('$destroy', function() {
ionic.off('tap', onTap, $element[0]);
});
function onTap(e) {
if (ionic.DomUtil.getParentOrSelfWithClass(e.target, 'button', 4)) {
return;
}
var touch = e.gesture && e.gesture.touches[0] || e.detail.touches[0];
var bounds = $element[0].getBoundingClientRect();
if(ionic.DomUtil.rectContains(
touch.pageX, touch.pageY,
bounds.left, bounds.top - 20,
bounds.left + bounds.width, bounds.top + bounds.height)
) {
scrollCtrl.scrollTop(true);
}
}
});
}
};
}])

View File

@@ -1,7 +1,7 @@
(function() {
'use strict';
angular.module('ionic.ui.content', ['ionic.ui.service', 'ionic.ui.scroll'])
angular.module('ionic.ui.content', ['ionic.ui.scroll'])
/**
* Panel is a simple 100% width and height, fixed panel. It's meant for content to be
@@ -28,6 +28,8 @@ angular.module('ionic.ui.content', ['ionic.ui.service', 'ionic.ui.scroll'])
* @ngdoc directive
* @name ionContent
* @module ionic
* @controller ionicScroll as $scope.$ionicScrollController
* @restrict E
*
* @description
* The ionContent directive provides an easy to use content area that can be configured
@@ -45,6 +47,9 @@ angular.module('ionic.ui.content', ['ionic.ui.service', 'ionic.ui.scroll'])
* Use the classes 'has-header', 'has-subheader', 'has-footer', and 'has-tabs'
* to modify the positioning of the ion-content relative to surrounding elements.
*
* @param {string=} controller-bind The scope variable to bind this element's scrollView's
* {@link ionic.controller:ionicScroll ionicScroll controller} to.
* Default: $scope.$ionicScrollController.
* @param {boolean=} padding Whether to add padding to the content.
* of the content. Defaults to true on iOS, false on Android.
* @param {boolean=} scroll Whether to allow scrolling of content. Defaults to true.
@@ -67,6 +72,7 @@ function($parse, $timeout, $controller, $ionicBind) {
transclude: true,
require: '^?ionNavView',
scope: true,
priority: 501,
template:
'<div class="scroll-content">' +
'<div class="scroll"></div>' +
@@ -97,9 +103,11 @@ function($parse, $timeout, $controller, $ionicBind) {
scrollEventInterval: '@'
});
$scope.$watch($attr.padding, function(newVal) {
scrollContent.toggleClass('padding', !!newVal);
});
if (angular.isDefined($attr.padding)) {
$scope.$watch($attr.padding, function(newVal) {
scrollContent.toggleClass('padding', !!newVal);
});
}
if ($scope.scroll === "false") {
//do nothing
@@ -111,6 +119,7 @@ function($parse, $timeout, $controller, $ionicBind) {
$scope: $scope,
scrollViewOptions: {
el: $element[0],
controllerBind: $attr.controllerBind,
bouncing: $scope.$eval($scope.hasBouncing),
startX: $scope.$eval($scope.startX) || 0,
startY: $scope.$eval($scope.startY) || 0,
@@ -128,7 +137,7 @@ function($parse, $timeout, $controller, $ionicBind) {
}
});
//Publish scrollView to parent so children can access it
scrollView = $scope.$parent.scrollView = scrollCtrl.scrollView;
scrollView = scrollCtrl.scrollView;
}
transclude($scope, function(clone) {
@@ -227,7 +236,7 @@ function($parse, $timeout, $controller, $ionicBind) {
$onPulling: '&onPulling'
});
scrollCtrl.setRefresher($scope, $element[0]);
scrollCtrl._setRefresher($scope, $element[0]);
$scope.$on('scroll.refreshComplete', function() {
$element[0].classList.remove('active');
scrollCtrl.scrollView.finishPullToRefresh();

View File

@@ -7,11 +7,15 @@ angular.module('ionic.ui.scroll', [])
* @ngdoc directive
* @name ionScroll
* @module ionic
* @controller ionicScroll as $scope.$ionicScrollController
* @restrict E
*
* @description
* Creates a scrollable container for all content inside.
*
* @param {string=} controller-bind The scope variable to bind this element's scrollView's
* {@link ionic.controller:ionicScroll ionicScroll controller} to.
* Default: $scope.$ionicScrollController.
* @param {string=} direction Which way to scroll. 'x' or 'y'. Default 'y'.
* @param {boolean=} paging Whether to scroll with paging.
* @param {expression=} on-refresh Called on pull-to-refresh, triggered by an {@link ionic.directive:ionRefresher}.
@@ -61,6 +65,7 @@ angular.module('ionic.ui.scroll', [])
var scrollViewOptions= {
el: $element[0],
controllerBind: $attr.controllerBind,
paging: isPaging,
scrollbarX: $scope.$eval($scope.scrollbarX) !== false,
scrollbarY: $scope.$eval($scope.scrollbarY) !== false,

View File

@@ -94,7 +94,7 @@ angular.module('ionic.ui.sideMenu', ['ionic.service.gesture', 'ionic.service.vie
* ```js
* function ContentController($scope) {
* $scope.toggleLeft = function() {
* $scope.sideMenuController.toggleLeft();
* $scope.$ionicSideMenusController.toggleLeft();
* };
* }
* ```

View File

@@ -270,6 +270,9 @@ function($rootScope, $animate, $ionicBind, $compile, $ionicViewService) {
tabsCtrl = ctrls[0],
tabCtrl = ctrls[1];
//Remove title attribute so browser-tooltip does not apear
$element[0].removeAttribute('title');
$ionicBind($scope, $attr, {
animate: '=',
onSelect: '&',

View File

@@ -23,11 +23,6 @@ angular.module('ionic.service', [
'ionic.decorator.location'
]);
// UI specific services and delegates
angular.module('ionic.ui.service', [
'ionic.ui.service.scrollDelegate'
]);
angular.module('ionic.ui', [
'ionic.ui.checkbox',
'ionic.ui.content',
@@ -47,7 +42,6 @@ angular.module('ionic.ui', [
angular.module('ionic', [
'ionic.service',
'ionic.ui.service',
'ionic.ui',
// Angular deps

View File

@@ -1,266 +0,0 @@
(function() {
'use strict';
angular.module('ionic.ui.service.scrollDelegate', [])
/**
* @ngdoc service
* @name $ionicScrollDelegate
* @module ionic
* @description
* Allows you to have some control over a scrollable area (created by an
* {@link ionic.directive:ionContent} or {@link ionic.directive:ionScroll}
* directive).
*
* Inject it into a controller, create a new instance based upon the current scope,
* and its methods will send messages to the nearest scrollView and its children.
*
* @usage
* ```js
* function MyController($scope, $ionicScrollDelegate) {
* var delegate = $ionicScrollDelegate($scope);
* $scope.scrollToTop = function() {
* delegate.scrollTop();
* };
* }
* ```
* ```html
* <ion-content ng-controller="MyController">
* <button class="button" ng-click="scrollToTop()">
* Scroll To Top
* </button>
* </ion-content>
* ```
*/
.factory('$ionicScrollDelegate', ['$rootScope', '$timeout', '$location', '$ionicViewService', function($rootScope, $timeout, $location, $ionicViewService) {
//Exposed for testing
var rememberedScrollValues = ionicScrollDelegate._rememberedScrollValues = {};
function getScrollCtrl($scope) {
var ctrl;
while ($scope) {
if ( (ctrl = $scope.$$ionicScrollController) ) {
return ctrl;
}
$scope = $scope.$parent;
}
return ctrl;
}
function ionicScrollDelegate($scope) {
var scrollCtrl = getScrollCtrl($scope);
var scrollScope = scrollCtrl && scrollCtrl.$scope || $rootScope;
return {
/**
* @ngdoc method
* @name $ionicScrollDelegate#scrollTop
* @description
* @param {boolean=} shouldAnimate Whether the scroll should animate.
*/
scrollTop: function(animate) {
scrollScope.$broadcast('scroll.scrollTop', animate);
},
/**
* @ngdoc method
* @name $ionicScrollDelegate#scrollBottom
* @description
* @param {boolean=} shouldAnimate Whether the scroll should animate.
*/
scrollBottom: function(animate) {
scrollScope.$broadcast('scroll.scrollBottom', animate);
},
/**
* @ngdoc method
* @name $ionicScrollDelegate#scroll
* @description
* @param {number} left The x-value to scroll to.
* @param {number} top The y-value to scroll to.
* @param {boolean=} shouldAnimate Whether the scroll should animate.
*/
scrollTo: function(left, top, animate) {
scrollScope.$broadcast('scroll.scrollTo', left, top, animate);
},
/**
* @ngdoc method
* @name $ionicScrollDelegate#anchorScroll
* @description
*
* Tell the scrollView to scroll to the element with an id
* matching window.location.hash.
*
* If no matching element is found, it will scroll to top.
*
* @param {boolean=} shouldAnimate Whether the scroll should animate.
*/
anchorScroll: function(animate) {
scrollScope.$broadcast('scroll.anchorScroll', animate);
},
/**
* @ngdoc method
* @name $ionicScrollDelegate#resize
* @description
*
* Tell the scrollView to recalculate the size of its container.
*/
resize: function() {
scrollScope.$broadcast('scroll.resize');
},
/**
* @private
*/
tapScrollToTop: function(element, animate) {
var _this = this;
if (!angular.isDefined(animate)) {
animate = true;
}
ionic.on('tap', function(e) {
var target = e.target;
//Don't scroll to top for a button click
if (ionic.DomUtil.getParentOrSelfWithClass(target, 'button')) {
return;
}
var el = element[0];
var bounds = el.getBoundingClientRect();
if(ionic.DomUtil.rectContains(e.gesture.touches[0].pageX, e.gesture.touches[0].pageY, bounds.left, bounds.top, bounds.left + bounds.width, bounds.top + 20)) {
_this.scrollTop(animate);
}
}, element[0]);
},
/**
* @ngdoc method
* @name $ionicScrollDelegate#rememberScrollPosition
* @description
*
* When this scroll area is destroyed, its last scroll position will be
* saved using the given id.
*
* @param {string} id The identifier for this saved scroll position.
*/
rememberScrollPosition: function(id) {
if (!id) {
throw new Error("Must supply a unique id!");
}
scrollScope.$broadcast('scroll.rememberPosition', id);
},
/**
* @ngdoc method
* @name $ionicScrollDelegate#scrollToRememberedPosition
* @description
*
* If a scroll position was remembered using the given id, loads the
* remembered scroll position and scrolls there.
*
* @param {string} id The identifier for this saved scroll position.
* @param {boolean=} shouldAnimate Whether to animate the scroll.
*/
scrollToRememberedPosition: function(id, animate) {
if (!id) {
throw new Error("Must supply a unique id!");
}
scrollScope.$broadcast('scroll.scrollToRememberedPosition', id, !!animate);
},
/**
* @private
* Attempt to get the current scroll view in scope (if any)
*
* Note: will not work in an isolated scope context.
*/
getScrollView: function() {
return scrollCtrl && scrollCtrl.scrollView;
}
};
}
/**
* @private
* Register a scope and scroll view for scroll event handling.
* $scope {Scope} the scope to register and listen for events
*/
ionicScrollDelegate.register = function($scope, $element, scrollView) {
var scrollEl = $element[0];
function scrollViewResize() {
// Run the resize after this digest
return $timeout(function() {
scrollView.resize();
});
}
$element.on('scroll', function(e) {
var detail = (e.originalEvent || e).detail || {};
$scope.$onScroll && $scope.$onScroll({
event: e,
scrollTop: detail.scrollTop || 0,
scrollLeft: detail.scrollLeft || 0
});
});
$scope.$on('scroll.resize', scrollViewResize);
$scope.$on('scroll.anchorScroll', function(e, animate) {
scrollViewResize().then(function() {
var hash = $location.hash();
var elm;
if (hash && (elm = document.getElementById(hash)) ) {
var scroll = ionic.DomUtil.getPositionInParent(elm, scrollEl);
scrollView.scrollTo(scroll.left, scroll.top, !!animate);
} else {
scrollView.scrollTo(0,0, !!animate);
}
});
});
$scope.$on('scroll.scrollTo', function(e, left, top, animate) {
scrollViewResize().then(function() {
scrollView.scrollTo(left, top, !!animate);
});
});
$scope.$on('scroll.scrollTop', function(e, animate) {
scrollViewResize().then(function() {
scrollView.scrollTo(0, 0, !!animate);
});
});
$scope.$on('scroll.scrollBottom', function(e, animate) {
scrollViewResize().then(function() {
var sv = scrollView;
if (sv) {
var max = sv.getScrollMax();
sv.scrollTo(max.left, max.top, !!animate);
}
});
});
var rememberScrollId;
$scope.$on('scroll.rememberPosition', function(e, id) {
rememberScrollId = id;
});
$scope.$on('$destroy', function() {
if (rememberScrollId) {
rememberedScrollValues[rememberScrollId] = scrollView.getValues();
}
});
$scope.$on('scroll.scrollToRememberedPosition', function(e, id, animate) {
var values = rememberedScrollValues[id];
if (values) {
scrollViewResize().then(function() {
scrollView.scrollTo(+values.left || null, +values.top || null, animate);
});
}
});
};
return ionicScrollDelegate;
}]);
})(ionic);

View File

@@ -159,8 +159,6 @@ angular.module('ionic.service.view', ['ui.router', 'ionic.service.platform'])
rsp.viewId = backView.viewId;
rsp.navAction = 'moveBack';
rsp.viewId = backView.viewId;
//when going back, erase scrollValues
currentView.rememberedScrollValues = {};
if(backView.historyId === currentView.historyId) {
// went back in the same history
rsp.navDirection = 'back';
@@ -234,9 +232,13 @@ angular.module('ionic.service.view', ['ui.router', 'ionic.service.platform'])
stateName: this.getCurrentStateName(),
stateParams: this.getCurrentStateParams(),
url: $location.url(),
rememberedScrollValues: null
});
if (rsp.navAction == 'moveBack') {
//moveBack(from, to);
$rootScope.$emit('$viewHistory.viewBack', currentView.viewId, rsp.viewId);
}
// add the new view to this history's stack
hist.stack.push(viewHistory.views[rsp.viewId]);
}