Skip to content
This repository was archived by the owner on May 29, 2019. It is now read-only.

Commit ace3787

Browse files
committed
fix(carousel): delay select until $digest stabilizes
- Defer selection of active slide until after transition has been reset to avoid selection occuring before the transition finishes, preventing a new slide from being chosen - Buffer slides if transition is currently going on to navigate to last slide selected after
1 parent a8b0d45 commit ace3787

File tree

2 files changed

+83
-13
lines changed

2 files changed

+83
-13
lines changed

Diff for: src/carousel/carousel.js

+39-10
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
angular.module('ui.bootstrap.carousel', [])
22

3-
.controller('UibCarouselController', ['$scope', '$element', '$interval', '$animate', function($scope, $element, $interval, $animate) {
3+
.controller('UibCarouselController', ['$scope', '$element', '$interval', '$timeout', '$animate', function($scope, $element, $interval, $timeout, $animate) {
44
var self = this,
55
slides = self.slides = $scope.slides = [],
66
SLIDE_DIRECTION = 'uib-slideDirection',
77
currentIndex = -1,
8-
currentInterval, isPlaying;
8+
currentInterval, isPlaying, bufferedTransitions = [];
99
self.currentSlide = null;
1010

1111
var destroyed = false;
@@ -19,6 +19,8 @@ angular.module('ui.bootstrap.carousel', [])
1919
//Prevent this user-triggered transition from occurring if there is already one in progress
2020
if (nextSlide && nextSlide !== self.currentSlide && !$scope.$currentTransition) {
2121
goNext(nextSlide, nextIndex, direction);
22+
} else if (nextSlide && nextSlide !== self.currentSlide && $scope.$currentTransition) {
23+
bufferedTransitions.push(nextSlide);
2224
}
2325
};
2426

@@ -40,6 +42,14 @@ angular.module('ui.bootstrap.carousel', [])
4042
if (phase === 'close') {
4143
$scope.$currentTransition = null;
4244
$animate.off('addClass', element);
45+
if (bufferedTransitions.length) {
46+
var nextSlide = bufferedTransitions.pop();
47+
var nextIndex = $scope.indexOfSlide(nextSlide);
48+
var nextDirection = nextIndex > self.getCurrentIndex() ? 'next' : 'prev';
49+
clearBufferedTransitions();
50+
51+
goNext(nextSlide, nextIndex, nextDirection);
52+
}
4353
}
4454
});
4555
}
@@ -134,6 +144,13 @@ angular.module('ui.bootstrap.carousel', [])
134144
function resetTransition(slides) {
135145
if (!slides.length) {
136146
$scope.$currentTransition = null;
147+
clearBufferedTransitions();
148+
}
149+
}
150+
151+
function clearBufferedTransitions() {
152+
while (bufferedTransitions.length) {
153+
bufferedTransitions.shift();
137154
}
138155
}
139156

@@ -155,6 +172,10 @@ angular.module('ui.bootstrap.carousel', [])
155172
slides.push(slide);
156173
//if this is the first slide or the slide is set to active, select it
157174
if (slides.length === 1 || slide.active) {
175+
if ($scope.$currentTransition) {
176+
$scope.$currentTransition = null;
177+
}
178+
158179
self.select(slides[slides.length - 1]);
159180
if (slides.length === 1) {
160181
$scope.play();
@@ -170,22 +191,30 @@ angular.module('ui.bootstrap.carousel', [])
170191
return +a.index > +b.index;
171192
});
172193
}
194+
195+
var bufferedIndex = bufferedTransitions.indexOf(slide);
196+
if (bufferedIndex !== -1) {
197+
bufferedTransitions.splice(bufferedIndex, 1);
198+
}
173199
//get the index of the slide inside the carousel
174200
var index = slides.indexOf(slide);
175201
slides.splice(index, 1);
176-
if (slides.length > 0 && slide.active) {
177-
if (index >= slides.length) {
178-
self.select(slides[index - 1]);
179-
} else {
180-
self.select(slides[index]);
202+
$timeout(function() {
203+
if (slides.length > 0 && slide.active) {
204+
if (index >= slides.length) {
205+
self.select(slides[index - 1]);
206+
} else {
207+
self.select(slides[index]);
208+
}
209+
} else if (currentIndex > index) {
210+
currentIndex--;
181211
}
182-
} else if (currentIndex > index) {
183-
currentIndex--;
184-
}
212+
});
185213

186214
//clean the currentSlide when no more slide
187215
if (slides.length === 0) {
188216
self.currentSlide = null;
217+
clearBufferedTransitions();
189218
}
190219
};
191220

Diff for: src/carousel/test/carousel.spec.js

+44-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
describe('carousel', function() {
1+
fdescribe('carousel', function() {
22
beforeEach(module('ui.bootstrap.carousel', function($compileProvider, $provide) {
33
angular.forEach(['ngSwipeLeft', 'ngSwipeRight'], makeMock);
44
function makeMock(name) {
@@ -12,15 +12,18 @@ describe('carousel', function() {
1212
});
1313
}
1414
}));
15+
beforeEach(module('ngAnimateMock'));
1516
beforeEach(module('uib/template/carousel/carousel.html', 'uib/template/carousel/slide.html'));
1617

17-
var $rootScope, $compile, $controller, $interval, $templateCache;
18-
beforeEach(inject(function(_$rootScope_, _$compile_, _$controller_, _$interval_, _$templateCache_) {
18+
var $rootScope, $compile, $controller, $interval, $templateCache, $timeout, $animate;
19+
beforeEach(inject(function(_$rootScope_, _$compile_, _$controller_, _$interval_, _$templateCache_, _$timeout_, _$animate_) {
1920
$rootScope = _$rootScope_;
2021
$compile = _$compile_;
2122
$controller = _$controller_;
2223
$interval = _$interval_;
2324
$templateCache = _$templateCache_;
25+
$timeout = _$timeout_;
26+
$animate = _$animate_;
2427
}));
2528

2629
describe('basics', function() {
@@ -291,11 +294,13 @@ describe('carousel', function() {
291294
scope.$apply('slides[2].active = true');
292295
testSlideActive(2);
293296
scope.$apply('slides.splice(0,1)');
297+
$timeout.flush(0);
294298
expect(elm.find('div.item').length).toBe(2);
295299
testSlideActive(1);
296300
$interval.flush(scope.interval);
297301
testSlideActive(0);
298302
scope.$apply('slides.splice(1,1)');
303+
$timeout.flush(0);
299304
expect(elm.find('div.item').length).toBe(1);
300305
testSlideActive(0);
301306
});
@@ -325,6 +330,39 @@ describe('carousel', function() {
325330
testSlideActive(1);
326331
});
327332

333+
it('should buffer the slides if transition is clicked and only transition to the last requested', function() {
334+
var carouselScope = elm.children().scope();
335+
336+
testSlideActive(0);
337+
carouselScope.$currentTransition = null;
338+
carouselScope.select(carouselScope.slides[1]);
339+
$animate.flush();
340+
341+
testSlideActive(1);
342+
343+
carouselScope.$currentTransition = true;
344+
carouselScope.select(carouselScope.slides[2]);
345+
scope.$apply();
346+
347+
testSlideActive(1);
348+
349+
carouselScope.select(carouselScope.slides[0]);
350+
scope.$apply();
351+
352+
testSlideActive(1);
353+
354+
carouselScope.$currentTransition = null;
355+
$interval.flush(scope.interval);
356+
$animate.flush();
357+
358+
testSlideActive(2);
359+
360+
$interval.flush(scope.interval);
361+
$animate.flush();
362+
363+
testSlideActive(0);
364+
});
365+
328366
it('issue 1414 - should not continue running timers after scope is destroyed', function() {
329367
testSlideActive(0);
330368
$interval.flush(scope.interval);
@@ -468,13 +506,16 @@ describe('carousel', function() {
468506
it('should remove slide and change active slide if needed', function() {
469507
expect(ctrl.slides.length).toBe(4);
470508
ctrl.removeSlide(ctrl.slides[0]);
509+
$timeout.flush(0);
471510
expect(ctrl.slides.length).toBe(3);
472511
expect(ctrl.currentSlide).toBe(ctrl.slides[0]);
473512
ctrl.select(ctrl.slides[2]);
474513
ctrl.removeSlide(ctrl.slides[2]);
514+
$timeout.flush(0);
475515
expect(ctrl.slides.length).toBe(2);
476516
expect(ctrl.currentSlide).toBe(ctrl.slides[1]);
477517
ctrl.removeSlide(ctrl.slides[0]);
518+
$timeout.flush(0);
478519
expect(ctrl.slides.length).toBe(1);
479520
expect(ctrl.currentSlide).toBe(ctrl.slides[0]);
480521
});

0 commit comments

Comments
 (0)