From 5567b08c4bbf8671480bd9021d01504a92ee2e9d Mon Sep 17 00:00:00 2001 From: Andy Joslin Date: Mon, 24 Mar 2014 20:35:55 -0600 Subject: [PATCH] delegates: add tabbar, update docs --- .../src/controller/ionicScrollController.js | 2 + js/ext/angular/src/directive/ionicBar.js | 25 +----- js/ext/angular/src/directive/ionicNavBar.js | 8 +- js/ext/angular/src/directive/ionicSideMenu.js | 29 +++++-- js/ext/angular/src/directive/ionicSlideBox.js | 2 + js/ext/angular/src/directive/ionicTabBar.js | 80 ++++++++++++------- .../angular/test/directive/ionicBar.unit.js | 18 ----- .../test/directive/ionicSideMenu.unit.js | 17 +++- .../test/directive/ionicTabBar.unit.js | 28 ++++--- js/views/headerBarView.js | 18 ----- 10 files changed, 117 insertions(+), 110 deletions(-) diff --git a/js/ext/angular/src/controller/ionicScrollController.js b/js/ext/angular/src/controller/ionicScrollController.js index 480a74bed1..07d3937b03 100644 --- a/js/ext/angular/src/controller/ionicScrollController.js +++ b/js/ext/angular/src/controller/ionicScrollController.js @@ -165,6 +165,8 @@ angular.module('ionic.ui.scroll') * @param {string} handle * @returns `delegateInstance` A delegate instance that controls only the * scrollViews with `delegate-handle` matching the given handle. + * + * Example: `$ionicScrollDelegate.getByHandle('my-handle').scrollTop();` */ ])) diff --git a/js/ext/angular/src/directive/ionicBar.js b/js/ext/angular/src/directive/ionicBar.js index 6e2c7bc202..73d15dd838 100644 --- a/js/ext/angular/src/directive/ionicBar.js +++ b/js/ext/angular/src/directive/ionicBar.js @@ -11,18 +11,11 @@ angular.module('ionic.ui.header', ['ngAnimate', 'ngSanitize']) * @name ionHeaderBar * @module ionic * @restrict E - * @controller ionicBar as $scope.$ionicHeaderBarController * * @description * Adds a fixed header bar above some content. * - * Is able to have left or right buttons, and additionally its title can be - * aligned through the {@link ionic.controller:ionicBar ionicBar controller}. - * - * @param {string=} controller-bind The scope variable to bind this header bar's - * {@link ionic.controller:ionicBar ionicBar controller} to. - * Default: $scope.$ionicHeaderBarController. - * @param {string=} align-title Where to align the title at the start. + * @param {string=} align-title Where to align the title. * Avaialble: 'left', 'right', or 'center'. Defaults to 'center'. * * @usage @@ -48,18 +41,11 @@ angular.module('ionic.ui.header', ['ngAnimate', 'ngSanitize']) * @name ionFooterBar * @module ionic * @restrict E - * @controller ionicBar as $scope.$ionicFooterBarController * * @description * Adds a fixed footer bar below some content. * - * Is able to have left or right buttons, and additionally its title can be - * aligned through the {@link ionic.controller:ionicBar ionicBar controller}. - * - * @param {string=} controller-bind The scope variable to bind this footer bar's - * {@link ionic.controller:ionicBar ionicBar controller} to. - * Default: $scope.$ionicFooterBarController. - * @param {string=} align-title Where to align the title at the start. + * @param {string=} align-title Where to align the title. * Avaialble: 'left', 'right', or 'center'. Defaults to 'center'. * * @usage @@ -111,11 +97,12 @@ function tapScrollToTopDirective() { function barDirective(isHeader) { - return ['$parse', function($parse) { + return [function($parse) { return { restrict: 'E', compile: function($element, $attr) { $element.addClass(isHeader ? 'bar bar-header' : 'bar bar-footer'); + return { pre: prelink }; function prelink($scope, $element, $attr) { var hb = new ionic.views.HeaderBar({ @@ -123,10 +110,6 @@ function barDirective(isHeader) { alignTitle: $attr.alignTitle || 'center' }); - $parse($attr.controllerBind || - (isHeader ? '$ionicHeaderBarController' : '$ionicFooterBarController') - ).assign($scope, hb); - var el = $element[0]; //just incase header is on rootscope var parentScope = $scope.$parent || $scope; diff --git a/js/ext/angular/src/directive/ionicNavBar.js b/js/ext/angular/src/directive/ionicNavBar.js index d3b128a2aa..5ae03f25c3 100644 --- a/js/ext/angular/src/directive/ionicNavBar.js +++ b/js/ext/angular/src/directive/ionicNavBar.js @@ -19,15 +19,17 @@ angular.module('ionic.ui.navBar', ['ionic.service.view', 'ngSanitize']) /** * @ngdoc method * @name $ionicNavBarDelegate#align - * @description Calls {@link ionic.controller:ionicBar#align ionicBar#align} for this navBar. - * @param {string=} direction The direction to the align the title text towards. + * @description Aligns the title with the buttons in a given direction. + * @param {string=} direction The direction to the align the title text towards. + * Available: 'left', 'right', 'center'. Default: 'center'. */ 'align', /** * @ngdoc method * @name $ionicNavBarDelegate#showBackButton * @description - * Set whether the {@link ionic.directive:ionNavBackButton} should be shown (if it exists). + * Set whether the {@link ionic.directive:ionNavBackButton} should be shown + * (if it exists). * @param {boolean} show Whether to show the back button. */ 'showBackButton', diff --git a/js/ext/angular/src/directive/ionicSideMenu.js b/js/ext/angular/src/directive/ionicSideMenu.js index 83a3c753d6..456c03eab0 100644 --- a/js/ext/angular/src/directive/ionicSideMenu.js +++ b/js/ext/angular/src/directive/ionicSideMenu.js @@ -85,7 +85,15 @@ angular.module('ionic.ui.sideMenu', ['ionic.service.gesture', 'ionic.service.vie * @name $ionicSideMenuDelegate#isOpenRight * @returns {boolean} Whether the right menu is currently opened. */ - 'isOpenRight' + 'isOpenRight', + /** + * @ngdoc method + * @name $ionicSideMenuDelegate#canDragContent + * @param {boolean=} canDrag Set whether the content can or cannot be dragged to open + * side menus. + * @returns {boolean} Whether the content can be dragged to open side menus. + */ + 'canDragContent', /** * @ngdoc method * @name $ionicSideMenuDelegate#getByHandle @@ -93,6 +101,8 @@ angular.module('ionic.ui.sideMenu', ['ionic.service.gesture', 'ionic.service.vie * @returns `delegateInstance` A delegate instance that controls only the * {@link ionic.directive:ionSideMenus} directives with `delegate-handle` matching * the given handle. + * + * Example: `$ionicSideMenuDelegate.getByHandle('my-handle').toggleLeft();` */ ])) @@ -134,9 +144,9 @@ angular.module('ionic.ui.sideMenu', ['ionic.service.gesture', 'ionic.service.vie * * ``` * ```js - * function ContentController($scope) { + * function ContentController($scope, $ionicSideMenuDelegate) { * $scope.toggleLeft = function() { - * $scope.$$ionicSideMenuDelegateController.toggleLeft(); + * $ionicSideMenuDelegate.toggleLeft(); * }; * } * ``` @@ -158,6 +168,13 @@ angular.module('ionic.ui.sideMenu', ['ionic.service.gesture', 'ionic.service.vie right: { width: 275 } }); + this.canDragContent = function(canDrag) { + if (arguments.length) { + $scope.dragContent = !!canDrag; + } + return $scope.dragContent; + }; + $scope.sideMenuContentTranslateX = 0; var deregisterInstance = $ionicSideMenuDelegate._registerInstance( @@ -188,7 +205,7 @@ angular.module('ionic.ui.sideMenu', ['ionic.service.gesture', 'ionic.service.vie * @usage * ```html *
+ * drag-content="canDrag"> *
* ``` * For a complete side menu example, see the @@ -210,10 +227,10 @@ angular.module('ionic.ui.sideMenu', ['ionic.service.gesture', 'ionic.service.vie if (angular.isDefined(attr.dragContent)) { $scope.$watch(attr.dragContent, function(value) { - $scope.dragContent = value; + sideMenuCtrl.canDragContent(value); }); } else { - $scope.dragContent = true; + sideMenuCtrl.canDragContent(true); } var defaultPrevented = false; diff --git a/js/ext/angular/src/directive/ionicSlideBox.js b/js/ext/angular/src/directive/ionicSlideBox.js index 3aaa1374cc..ced645eb80 100644 --- a/js/ext/angular/src/directive/ionicSlideBox.js +++ b/js/ext/angular/src/directive/ionicSlideBox.js @@ -68,6 +68,8 @@ angular.module('ionic.ui.slideBox', []) * @returns `delegateInstance` A delegate instance that controls only the * {@link ionic.directive:ionSlideBox} directives with `delegate-handle` matching * the given handle. + * + * Example: `$ionicSlideBoxDelegate.getByHandle('my-handle').stop();` */ ])) diff --git a/js/ext/angular/src/directive/ionicTabBar.js b/js/ext/angular/src/directive/ionicTabBar.js index ae4b93136f..a08c0f30e6 100644 --- a/js/ext/angular/src/directive/ionicTabBar.js +++ b/js/ext/angular/src/directive/ionicTabBar.js @@ -7,31 +7,57 @@ angular.module('ionic.ui.tabs', ['ionic.service.view']) }]) /** - * @ngdoc controller - * @name ionicTabs + * @ngdoc service + * @name $ionicTabsDelegate * @module ionic * * @description - * Controller for the {@link ionic.directive:ionTabs} directive. + * Delegate for controlling the {@link ionic.directive:ionTabs} directive. + * + * Methods called directly on the $ionicTabsDelegate service will control all ionTabs + * directives. Use the {@link ionic.service:$ionicTabsDelegate#getByHandle getByHandle} + * method to control specific ionTabs instances. */ +.service('$ionicTabsDelegate', delegateService([ + /** + * @ngdoc method + * @name $ionicTabsDelegate#select + * @description Select the tab matching the given index. + * + * @param {number} index Index of the tab to select. + * @param {boolean=} shouldChangeHistory Whether this selection should load this tab's + * view history (if it exists) and use it, or just load the default page. + * Default false. + * Hint: you probably want this to be true if you have an + * {@link ionic.directive:ionNavView} inside your tab. + */ + 'select', + /** + * @ngdoc method + * @name $ionicTabsDelegate#selectedTabIndex + * @returns `number` The index of the selected tab, or -1. + */ + 'selectedIndex' + /** + * @ngdoc method + * @name $ionicTabsDelegate#getByHandle + * @param {string} handle + * @returns `delegateInstance` A delegate instance that controls only the + * {@link ionic.directive:ionTabs} directives with `delegate-handle` matching + * the given handle. + * + * Example: `$ionicTabsDelegate.getByHandle('my-handle').select(0);` + */ +])) + .controller('ionicTabs', ['$scope', '$ionicViewService', '$element', function($scope, $ionicViewService, $element) { var _selectedTab = null; var self = this; self.tabs = []; - /** - * @ngdoc method - * @name ionicTabs#selectedTabIndex - * @returns `number` The index of the selected tab, or -1. - */ self.selectedTabIndex = function() { return self.tabs.indexOf(_selectedTab); }; - /** - * @ngdoc method - * @name ionicTabs#selectedTab - * @returns `ionTab` The selected tab or null if none selected. - */ self.selectedTab = function() { return _selectedTab; }; @@ -72,17 +98,6 @@ angular.module('ionic.ui.tabs', ['ionic.service.view']) } }; - /** - * @ngdoc method - * @name ionicTabs#select - * @description Select the given tab or tab index. - * - * @param {ionTab|number} tabOrIndex A tab object or index of a tab to select - * @param {boolean=} shouldChangeHistory Whether this selection should load this tab's view history - * (if it exists) and use it, or just loading the default page. Default false. - * Hint: you probably want this to be true if you have an - * {@link ionic.directive:ionNavView} inside your tab. - */ self.select = function(tab, shouldEmitEvent) { var tabIndex; if (angular.isNumber(tab)) { @@ -132,7 +147,6 @@ angular.module('ionic.ui.tabs', ['ionic.service.view']) * @name ionTabs * @module ionic * @restrict E - * @controller ionicTabs as $scope.$ionicTabsController * @codepen KbrzJ * * @description @@ -161,15 +175,15 @@ angular.module('ionic.ui.tabs', ['ionic.service.view']) * * * + * * * ``` * - * @param {string=} controller-bind The scope variable to bind these tabs' - * {@link ionic.controller:ionicTabs ionicTabs controller} to. - * Default: $scope.$ionicTabsController. + * @param {string=} delegate-handle The handle used to identify these tabs + * with {@link ionic.service:$ionicTabsDelegate}. */ -.directive('ionTabs', ['$ionicViewService', '$parse', function($ionicViewService, $parse) { +.directive('ionTabs', ['$ionicViewService', '$ionicTabsDelegate', function($ionicViewService, $ionicTabsDelegate) { return { restrict: 'E', scope: true, @@ -183,7 +197,11 @@ angular.module('ionic.ui.tabs', ['ionic.service.view']) return { pre: prelink }; function prelink($scope, $element, $attr, tabsCtrl) { - $parse(attr.model || '$ionicTabsController').assign($scope, tabsCtrl); + var deregisterInstance = $ionicTabsDelegate._registerInstance( + tabsCtrl, $attr.delegateHandle + ); + + $scope.$on('$destroy', deregisterInstance); tabsCtrl.$scope = $scope; tabsCtrl.$element = $element; @@ -242,7 +260,7 @@ function($scope, $ionicViewService, $rootScope, $element) { * @param {expression=} badge-style The style of badge to put on this tab (eg tabs-positive). * @param {expression=} on-select Called when this tab is selected. * @param {expression=} on-deselect Called when this tab is deselected. - * @param {expression=} ng-click By default, the tab will be selected on click. If ngClick is set, it will not. You can explicitly switch tabs using {@link ionic.controller:ionicTabs#select ionicTabBar controller's select method}. + * @param {expression=} ng-click By default, the tab will be selected on click. If ngClick is set, it will not. You can explicitly switch tabs using {@link ionic.service:$ionicTabsDelegate#select $ionicTabsDelegate.select()}. */ .directive('ionTab', ['$rootScope', '$animate', '$ionicBind', '$compile', '$ionicViewService', function($rootScope, $animate, $ionicBind, $compile, $ionicViewService) { diff --git a/js/ext/angular/test/directive/ionicBar.unit.js b/js/ext/angular/test/directive/ionicBar.unit.js index 4fd9396a4c..cec6e39b32 100644 --- a/js/ext/angular/test/directive/ionicBar.unit.js +++ b/js/ext/angular/test/directive/ionicBar.unit.js @@ -124,24 +124,6 @@ describe('bar directives', function() { var el = setup(); expect(el.hasClass(data.className)).toBe(true); }); - - it('should assign views.HeaderBar to default controllerBind', function() { - var el = setup(); - expect(el.scope()[data.controllerBind] instanceof ionic.views.HeaderBar).toBe(true); - }); - it('should assign views.HeaderBar to attr controllerBind', function() { - var el = setup('controller-bind="monkeys"'); - expect(el.scope().monkeys instanceof ionic.views.HeaderBar).toBe(true); - }); - - it('should pass center to views.HeaderBar option by default', function() { - var el = setup(); - expect(el.scope()[data.controllerBind].opts.alignTitle).toBe('center'); - }); - it('should pass attr.alignTitle to views.HeaderBar', function() { - var el = setup('align-title="left"'); - expect(el.scope()[data.controllerBind].opts.alignTitle).toBe('left'); - }); }); }); }); diff --git a/js/ext/angular/test/directive/ionicSideMenu.unit.js b/js/ext/angular/test/directive/ionicSideMenu.unit.js index 1f2ba7cacb..fb0e7eb567 100644 --- a/js/ext/angular/test/directive/ionicSideMenu.unit.js +++ b/js/ext/angular/test/directive/ionicSideMenu.unit.js @@ -5,7 +5,7 @@ describe('Ionic Angular Side Menu', function() { var el; - beforeEach(module('ionic.ui.sideMenu')); + beforeEach(module('ionic')); it('should register with $ionicSideMenuDelegate', inject(function($compile, $rootScope, $ionicSideMenuDelegate) { var deregisterSpy = jasmine.createSpy('deregister'); @@ -23,6 +23,21 @@ describe('Ionic Angular Side Menu', function() { el.scope().$destroy(); expect(deregisterSpy).toHaveBeenCalled(); })); + + it('should canDragContent', inject(function($compile, $rootScope) { + var el = $compile('
')($rootScope.$new()); + $rootScope.$apply(); + expect(el.controller('ionSideMenus').canDragContent()).toBe(true); + expect(el.scope().dragContent).toBe(true); + + el.controller('ionSideMenus').canDragContent(false); + expect(el.controller('ionSideMenus').canDragContent()).toBe(false); + expect(el.scope().dragContent).toBe(false); + + el.controller('ionSideMenus').canDragContent(true); + expect(el.controller('ionSideMenus').canDragContent()).toBe(true); + expect(el.scope().dragContent).toBe(true); + })); }); describe('Ionic Side Menu Content Directive', function () { diff --git a/js/ext/angular/test/directive/ionicTabBar.unit.js b/js/ext/angular/test/directive/ionicTabBar.unit.js index ad2d807f56..4f1e40dc4e 100644 --- a/js/ext/angular/test/directive/ionicTabBar.unit.js +++ b/js/ext/angular/test/directive/ionicTabBar.unit.js @@ -224,6 +224,22 @@ describe('tabs', function() { return element; } + it('should register with given handle and deregister on destroy', inject(function($ionicTabsDelegate) { + var deregisterSpy = jasmine.createSpy('deregister'); + spyOn($ionicTabsDelegate, '_registerInstance').andCallFake(function() { + return deregisterSpy; + }); + var el = setup('delegate-handle="banana"'); + + expect($ionicTabsDelegate._registerInstance) + .toHaveBeenCalledWith(el.controller('ionTabs'), 'banana'); + + expect(deregisterSpy).not.toHaveBeenCalled(); + el.scope().$destroy(); + expect(deregisterSpy).toHaveBeenCalled(); + })); + + it('should $hasTabs and $hasTabsTop', function() { var el = setup(); var scope = el.scope(); @@ -239,18 +255,6 @@ describe('tabs', function() { expect(scope.$hasTabsTop).toBe(false); }); - it('should bind controller to scope.$ionicTabsController by default', function() { - var el = setup(); - expect(el.controller('ionTabs')).toBeTruthy(); //sanity - expect(el.scope().$ionicTabsController).toBe(el.controller('ionTabs')); - }); - - it('should bind controller to scope[attr.model]', function() { - var el = setup('model="superman"'); - expect(el.controller('ionTabs')).toBeTruthy(); - expect(el.scope().superman).toBe(el.controller('ionTabs')); - }); - it('should transclude content with same scope', function() { var el = setup('', '
'); expect(el[0].querySelector('.tabs .content')).toBeTruthy(); diff --git a/js/views/headerBarView.js b/js/views/headerBarView.js index 614d84ef52..5d03b88e37 100644 --- a/js/views/headerBarView.js +++ b/js/views/headerBarView.js @@ -1,14 +1,6 @@ (function(ionic) { 'use strict'; - /** - * @ngdoc controller - * @name ionicBar - * @module ionic - * @description - * Controller for the {@link ionic.directive:ionHeaderBar} and - * {@link ionic.directive:ionFooterBar} directives. - */ ionic.views.HeaderBar = ionic.views.View.inherit({ initialize: function(opts) { this.el = opts.el; @@ -20,16 +12,6 @@ this.align(); }, - /** - * @ngdoc method - * @name ionicBar#align - * @description - * Aligns the title text with the buttons in the bar - * so that the title size is maximized and aligned correctly - * as much as possible. - * @param {string=} direction Which direction to align the title towards. - * Available: 'left', 'right', 'center'. Default: 'center'. - */ align: function(align) { align || (align = this.alignTitle);