Skip to content

Commit 193ac2e

Browse files
cristHian Gzchristopherthielen
cristHian Gz
authored andcommitted
feat(uiSref): Bind ui-sref to other DOM events (PR #3343)
Closes #3169
1 parent d71bad0 commit 193ac2e

File tree

2 files changed

+190
-11
lines changed

2 files changed

+190
-11
lines changed

src/directives/stateDirectives.ts

+30-11
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import { ng as angular } from "../angular";
1212
import { IAugmentedJQuery, ITimeoutService, IScope, IInterpolateService } from "angular";
1313

1414
import {
15-
Obj, extend, forEach, tail, isString, isObject, parse, noop, unnestR, identity, uniqR, inArray, removeFrom,
15+
Obj, extend, forEach, tail, isString, isObject, isArray, parse, noop, unnestR, identity, uniqR, inArray, removeFrom,
1616
RawParams, PathNode, StateOrName, StateService, StateDeclaration, UIRouter
1717
} from "ui-router-core";
1818
import { UIViewData } from "./viewDirective";
@@ -96,6 +96,31 @@ function defaultOpts(el: IAugmentedJQuery, $state: StateService) {
9696
};
9797
}
9898

99+
/** @hidden */
100+
function bindEvents(element: IAugmentedJQuery, scope: IScope, hookFn: (e: JQueryMouseEventObject) => void, uiStateOpts: any): void {
101+
let events;
102+
103+
if (uiStateOpts) {
104+
events = uiStateOpts.event;
105+
}
106+
107+
if (!isArray(events)) {
108+
events = ['click'];
109+
}
110+
111+
let on = element.on ? 'on' : 'bind';
112+
for (let event of events) {
113+
element[on](event, hookFn);
114+
}
115+
116+
scope.$on('$destroy', function() {
117+
let off = element.off ? 'off' : 'unbind';
118+
for (let event of events) {
119+
element[off](event, hookFn);
120+
}
121+
});
122+
}
123+
99124
/**
100125
* `ui-sref`: A directive for linking to a state
101126
*
@@ -157,7 +182,7 @@ function defaultOpts(el: IAugmentedJQuery, $state: StateService) {
157182
*
158183
* ### Transition Options
159184
* You can specify [[TransitionOptions]] to pass to [[StateService.go]] by using the `ui-sref-opts` attribute.
160-
* Options are restricted to `location`, `inherit`, and `reload`.
185+
* Options are restricted to `location`, `inherit`, `reload`, and `event`.
161186
*
162187
* #### Example:
163188
* ```html
@@ -262,10 +287,7 @@ uiSref = ['$uiRouter', '$timeout',
262287

263288
if (!type.clickable) return;
264289
hookFn = clickHook(element, $state, $timeout, type, getDef);
265-
element[element.on ? 'on' : 'bind']("click", hookFn);
266-
scope.$on('$destroy', function () {
267-
element[element.off ? 'off' : 'unbind']("click", hookFn);
268-
});
290+
bindEvents(element, scope, hookFn, rawDef.uiStateOpts);
269291
}
270292
};
271293
}];
@@ -318,7 +340,7 @@ uiSref = ['$uiRouter', '$timeout',
318340
*
319341
* ### Transition Options
320342
* You can specify [[TransitionOptions]] to pass to [[StateService.go]] by using the `ui-state-opts` attribute.
321-
* Options are restricted to `location`, `inherit`, and `reload`.
343+
* Options are restricted to `location`, `inherit`, `reload`, and `event`.
322344
* The value of the `ui-state-opts` is `$watch`ed and evaluated as an expression.
323345
*
324346
* #### Example:
@@ -390,10 +412,7 @@ uiState = ['$uiRouter', '$timeout',
390412

391413
if (!type.clickable) return;
392414
hookFn = clickHook(element, $state, $timeout, type, getDef);
393-
element[element.on ? 'on' : 'bind']("click", hookFn);
394-
scope.$on('$destroy', function () {
395-
element[element.off ? 'off' : 'unbind']("click", hookFn);
396-
});
415+
bindEvents(element, scope, hookFn, rawDef.uiStateOpts);
397416
}
398417
};
399418
}];

test/stateDirectivesSpec.js

+160
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,18 @@ describe('uiStateRef', function() {
7272
el[0].dispatchEvent(e);
7373
}
7474

75+
function triggerHTMLEvent(name) {
76+
var event = document.createEvent('HTMLEvents');
77+
event.initEvent(name, false, true);
78+
el[0].dispatchEvent(event);
79+
}
80+
81+
function triggerMouseEvent(name) {
82+
var event = document.createEvent('MouseEvents');
83+
event.initEvent(name, true, true);
84+
el[0].dispatchEvent(event);
85+
}
86+
7587
describe('links with promises', function() {
7688

7789
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() {
523535
expect(transitionOptions.reload).toEqual(true);
524536
expect(transitionOptions.absolute).toBeUndefined();
525537
}));
538+
539+
describe('option event', function() {
540+
it('should bind click event by default', inject(function($compile, $state, $timeout) {
541+
expect($state.current.name).toBe('top');
542+
543+
el = angular.element('<a ui-state="state"></a>');
544+
545+
scope.state = 'contacts';
546+
$compile(el)(scope);
547+
scope.$digest();
548+
549+
triggerClick(el);
550+
$timeout.flush();
551+
552+
expect($state.current.name).toBe('contacts');
553+
}));
554+
555+
it('should bind single HTML events', inject(function($compile, $state, $timeout) {
556+
expect($state.current.name).toEqual('top');
557+
558+
el = angular.element('<input type="text" ui-state="state" ui-state-opts="{ event: [\'change\'] }">');
559+
560+
scope.state = 'contacts';
561+
$compile(el)(scope);
562+
scope.$digest();
563+
564+
triggerHTMLEvent('change');
565+
$timeout.flush();
566+
567+
expect($state.current.name).toEqual('contacts');
568+
}));
569+
570+
it('should bind multiple HTML events', inject(function($compile, $state, $timeout) {
571+
expect($state.current.name).toEqual('top');
572+
573+
el = angular.element('<input type="text" ui-state="state" ui-state-opts="{ event: [\'change\', \'blur\'] }">');
574+
575+
scope.state = 'contacts';
576+
$compile(el)(scope);
577+
scope.$digest();
578+
579+
triggerHTMLEvent('change');
580+
$timeout.flush();
581+
expect($state.current.name).toEqual('contacts');
582+
583+
$state.go('top');
584+
scope.$digest();
585+
586+
expect($state.current.name).toEqual('top');
587+
588+
triggerHTMLEvent('blur');
589+
$timeout.flush();
590+
expect($state.current.name).toEqual('contacts');
591+
}));
592+
593+
it('should bind multiple Mouse events', inject(function($compile, $state, $timeout) {
594+
expect($state.current.name).toEqual('top');
595+
596+
el = angular.element('<a ui-state="state" ui-state-opts="{ event: [\'mouseover\', \'mousedown\'] }">');
597+
598+
scope.state = 'contacts';
599+
$compile(el)(scope);
600+
scope.$digest();
601+
602+
triggerMouseEvent('mouseover');
603+
$timeout.flush();
604+
expect($state.current.name).toEqual('contacts');
605+
606+
$state.go('top');
607+
scope.$digest();
608+
609+
expect($state.current.name).toEqual('top');
610+
611+
triggerMouseEvent('mousedown');
612+
$timeout.flush();
613+
expect($state.current.name).toEqual('contacts');
614+
}));
615+
});
526616
});
527617

528618
describe('forms', function() {
@@ -597,6 +687,76 @@ describe('uiStateRef', function() {
597687
expect($state.$current.name).toBe("contacts");
598688
}));
599689
});
690+
691+
describe('option event', function() {
692+
it('should bind click event by default', inject(function($rootScope, $compile, $state, $timeout) {
693+
el = angular.element('<a ui-sref="contacts"></a>');
694+
$compile(el)($rootScope);
695+
$rootScope.$digest();
696+
697+
expect($state.current.name).toEqual('top');
698+
699+
triggerClick(el);
700+
$timeout.flush();
701+
702+
expect($state.current.name).toEqual('contacts');
703+
}));
704+
705+
it('should bind single HTML events', inject(function($rootScope, $compile, $state, $timeout) {
706+
el = angular.element('<input type="text" ui-sref="contacts" ui-sref-opts="{ event: [\'change\'] }">');
707+
$compile(el)($rootScope);
708+
$rootScope.$digest();
709+
710+
expect($state.current.name).toEqual('top');
711+
712+
triggerHTMLEvent('change');
713+
$timeout.flush();
714+
715+
expect($state.current.name).toEqual('contacts');
716+
}));
717+
718+
it('should bind multiple HTML events', inject(function($rootScope, $compile, $state, $timeout) {
719+
el = angular.element('<input type="text" ui-sref="contacts" ui-sref-opts="{ event: [\'change\', \'blur\'] }">');
720+
$compile(el)($rootScope);
721+
$rootScope.$digest();
722+
723+
expect($state.current.name).toEqual('top');
724+
725+
triggerHTMLEvent('change');
726+
$timeout.flush();
727+
expect($state.current.name).toEqual('contacts');
728+
729+
$state.go('top');
730+
$rootScope.$digest();
731+
732+
expect($state.current.name).toEqual('top');
733+
734+
triggerHTMLEvent('blur');
735+
$timeout.flush();
736+
expect($state.current.name).toEqual('contacts');
737+
}));
738+
739+
it('should bind multiple Mouse events', inject(function($rootScope, $compile, $state, $timeout) {
740+
el = angular.element('<a ui-sref="contacts" ui-sref-opts="{ event: [\'mouseover\', \'mousedown\'] }">');
741+
$compile(el)($rootScope);
742+
$rootScope.$digest();
743+
744+
expect($state.current.name).toEqual('top');
745+
746+
triggerMouseEvent('mouseover');
747+
$timeout.flush();
748+
expect($state.current.name).toEqual('contacts');
749+
750+
$state.go('top');
751+
$rootScope.$digest();
752+
753+
expect($state.current.name).toEqual('top');
754+
755+
triggerMouseEvent('mousedown');
756+
$timeout.flush();
757+
expect($state.current.name).toEqual('contacts');
758+
}));
759+
});
600760
});
601761

602762
describe('uiSrefActive', function() {

0 commit comments

Comments
 (0)