diff --git a/src/directives/stateDirectives.ts b/src/directives/stateDirectives.ts
index f58f2247b..89a4af07f 100644
--- a/src/directives/stateDirectives.ts
+++ b/src/directives/stateDirectives.ts
@@ -12,7 +12,7 @@ import { ng as angular } from "../angular";
import { IAugmentedJQuery, ITimeoutService, IScope, IInterpolateService } from "angular";
import {
- Obj, extend, forEach, tail, isString, isObject, parse, noop, unnestR, identity, uniqR, inArray, removeFrom,
+ Obj, extend, forEach, tail, isString, isObject, isArray, parse, noop, unnestR, identity, uniqR, inArray, removeFrom,
RawParams, PathNode, StateOrName, StateService, StateDeclaration, UIRouter
} from "ui-router-core";
import { UIViewData } from "./viewDirective";
@@ -96,6 +96,31 @@ function defaultOpts(el: IAugmentedJQuery, $state: StateService) {
};
}
+/** @hidden */
+function bindEvents(element: IAugmentedJQuery, scope: IScope, hookFn: (e: JQueryMouseEventObject) => void, uiStateOpts: any): void {
+ let events;
+
+ if (uiStateOpts) {
+ events = uiStateOpts.event;
+ }
+
+ if (!isArray(events)) {
+ events = ['click'];
+ }
+
+ let on = element.on ? 'on' : 'bind';
+ for (let event of events) {
+ element[on](event, hookFn);
+ }
+
+ scope.$on('$destroy', function() {
+ let off = element.off ? 'off' : 'unbind';
+ for (let event of events) {
+ element[off](event, hookFn);
+ }
+ });
+}
+
/**
* `ui-sref`: A directive for linking to a state
*
@@ -157,7 +182,7 @@ function defaultOpts(el: IAugmentedJQuery, $state: StateService) {
*
* ### Transition Options
* You can specify [[TransitionOptions]] to pass to [[StateService.go]] by using the `ui-sref-opts` attribute.
- * Options are restricted to `location`, `inherit`, and `reload`.
+ * Options are restricted to `location`, `inherit`, `reload`, and `event`.
*
* #### Example:
* ```html
@@ -262,10 +287,7 @@ uiSref = ['$uiRouter', '$timeout',
if (!type.clickable) return;
hookFn = clickHook(element, $state, $timeout, type, getDef);
- element[element.on ? 'on' : 'bind']("click", hookFn);
- scope.$on('$destroy', function () {
- element[element.off ? 'off' : 'unbind']("click", hookFn);
- });
+ bindEvents(element, scope, hookFn, rawDef.uiStateOpts);
}
};
}];
@@ -318,7 +340,7 @@ uiSref = ['$uiRouter', '$timeout',
*
* ### Transition Options
* You can specify [[TransitionOptions]] to pass to [[StateService.go]] by using the `ui-state-opts` attribute.
- * Options are restricted to `location`, `inherit`, and `reload`.
+ * Options are restricted to `location`, `inherit`, `reload`, and `event`.
* The value of the `ui-state-opts` is `$watch`ed and evaluated as an expression.
*
* #### Example:
@@ -390,10 +412,7 @@ uiState = ['$uiRouter', '$timeout',
if (!type.clickable) return;
hookFn = clickHook(element, $state, $timeout, type, getDef);
- element[element.on ? 'on' : 'bind']("click", hookFn);
- scope.$on('$destroy', function () {
- element[element.off ? 'off' : 'unbind']("click", hookFn);
- });
+ bindEvents(element, scope, hookFn, rawDef.uiStateOpts);
}
};
}];
diff --git a/test/stateDirectivesSpec.js b/test/stateDirectivesSpec.js
index 5541578bd..2f96d992c 100644
--- a/test/stateDirectivesSpec.js
+++ b/test/stateDirectivesSpec.js
@@ -72,6 +72,18 @@ describe('uiStateRef', function() {
el[0].dispatchEvent(e);
}
+ function triggerHTMLEvent(name) {
+ var event = document.createEvent('HTMLEvents');
+ event.initEvent(name, false, true);
+ el[0].dispatchEvent(event);
+ }
+
+ function triggerMouseEvent(name) {
+ var event = document.createEvent('MouseEvents');
+ event.initEvent(name, true, true);
+ el[0].dispatchEvent(event);
+ }
+
describe('links with promises', function() {
it('should update the href when promises on parameters change before scope is applied', inject(function($rootScope, $compile, $q) {
@@ -523,6 +535,84 @@ describe('uiStateRef', function() {
expect(transitionOptions.reload).toEqual(true);
expect(transitionOptions.absolute).toBeUndefined();
}));
+
+ describe('option event', function() {
+ it('should bind click event by default', inject(function($compile, $state, $timeout) {
+ expect($state.current.name).toBe('top');
+
+ el = angular.element('');
+
+ scope.state = 'contacts';
+ $compile(el)(scope);
+ scope.$digest();
+
+ triggerClick(el);
+ $timeout.flush();
+
+ expect($state.current.name).toBe('contacts');
+ }));
+
+ it('should bind single HTML events', inject(function($compile, $state, $timeout) {
+ expect($state.current.name).toEqual('top');
+
+ el = angular.element('');
+
+ scope.state = 'contacts';
+ $compile(el)(scope);
+ scope.$digest();
+
+ triggerHTMLEvent('change');
+ $timeout.flush();
+
+ expect($state.current.name).toEqual('contacts');
+ }));
+
+ it('should bind multiple HTML events', inject(function($compile, $state, $timeout) {
+ expect($state.current.name).toEqual('top');
+
+ el = angular.element('');
+
+ scope.state = 'contacts';
+ $compile(el)(scope);
+ scope.$digest();
+
+ triggerHTMLEvent('change');
+ $timeout.flush();
+ expect($state.current.name).toEqual('contacts');
+
+ $state.go('top');
+ scope.$digest();
+
+ expect($state.current.name).toEqual('top');
+
+ triggerHTMLEvent('blur');
+ $timeout.flush();
+ expect($state.current.name).toEqual('contacts');
+ }));
+
+ it('should bind multiple Mouse events', inject(function($compile, $state, $timeout) {
+ expect($state.current.name).toEqual('top');
+
+ el = angular.element('');
+
+ scope.state = 'contacts';
+ $compile(el)(scope);
+ scope.$digest();
+
+ triggerMouseEvent('mouseover');
+ $timeout.flush();
+ expect($state.current.name).toEqual('contacts');
+
+ $state.go('top');
+ scope.$digest();
+
+ expect($state.current.name).toEqual('top');
+
+ triggerMouseEvent('mousedown');
+ $timeout.flush();
+ expect($state.current.name).toEqual('contacts');
+ }));
+ });
});
describe('forms', function() {
@@ -597,6 +687,76 @@ describe('uiStateRef', function() {
expect($state.$current.name).toBe("contacts");
}));
});
+
+ describe('option event', function() {
+ it('should bind click event by default', inject(function($rootScope, $compile, $state, $timeout) {
+ el = angular.element('');
+ $compile(el)($rootScope);
+ $rootScope.$digest();
+
+ expect($state.current.name).toEqual('top');
+
+ triggerClick(el);
+ $timeout.flush();
+
+ expect($state.current.name).toEqual('contacts');
+ }));
+
+ it('should bind single HTML events', inject(function($rootScope, $compile, $state, $timeout) {
+ el = angular.element('');
+ $compile(el)($rootScope);
+ $rootScope.$digest();
+
+ expect($state.current.name).toEqual('top');
+
+ triggerHTMLEvent('change');
+ $timeout.flush();
+
+ expect($state.current.name).toEqual('contacts');
+ }));
+
+ it('should bind multiple HTML events', inject(function($rootScope, $compile, $state, $timeout) {
+ el = angular.element('');
+ $compile(el)($rootScope);
+ $rootScope.$digest();
+
+ expect($state.current.name).toEqual('top');
+
+ triggerHTMLEvent('change');
+ $timeout.flush();
+ expect($state.current.name).toEqual('contacts');
+
+ $state.go('top');
+ $rootScope.$digest();
+
+ expect($state.current.name).toEqual('top');
+
+ triggerHTMLEvent('blur');
+ $timeout.flush();
+ expect($state.current.name).toEqual('contacts');
+ }));
+
+ it('should bind multiple Mouse events', inject(function($rootScope, $compile, $state, $timeout) {
+ el = angular.element('');
+ $compile(el)($rootScope);
+ $rootScope.$digest();
+
+ expect($state.current.name).toEqual('top');
+
+ triggerMouseEvent('mouseover');
+ $timeout.flush();
+ expect($state.current.name).toEqual('contacts');
+
+ $state.go('top');
+ $rootScope.$digest();
+
+ expect($state.current.name).toEqual('top');
+
+ triggerMouseEvent('mousedown');
+ $timeout.flush();
+ expect($state.current.name).toEqual('contacts');
+ }));
+ });
});
describe('uiSrefActive', function() {