Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

Commit a9352c1

Browse files
shahatapetebacondarwin
authored andcommitted
feat($location): allow to location to be changed during $locationChangeStart
Closes #9607 Closes #9678
1 parent 6f19a6f commit a9352c1

File tree

3 files changed

+199
-5
lines changed

3 files changed

+199
-5
lines changed

src/ng/location.js

+20-5
Original file line numberDiff line numberDiff line change
@@ -829,11 +829,19 @@ function $LocationProvider() {
829829
$rootScope.$evalAsync(function() {
830830
var oldUrl = $location.absUrl();
831831
var oldState = $location.$$state;
832+
var defaultPrevented;
832833

833834
$location.$$parse(newUrl);
834835
$location.$$state = newState;
835-
if ($rootScope.$broadcast('$locationChangeStart', newUrl, oldUrl,
836-
newState, oldState).defaultPrevented) {
836+
837+
defaultPrevented = $rootScope.$broadcast('$locationChangeStart', newUrl, oldUrl,
838+
newState, oldState).defaultPrevented;
839+
840+
// if the location was changed by a `$locationChangeStart` handler then stop
841+
// processing this location change
842+
if ($location.absUrl() !== newUrl) return;
843+
844+
if (defaultPrevented) {
837845
$location.$$parse(oldUrl);
838846
$location.$$state = oldState;
839847
setBrowserUrlWithFallback(oldUrl, false, oldState);
@@ -857,13 +865,20 @@ function $LocationProvider() {
857865
initializing = false;
858866

859867
$rootScope.$evalAsync(function() {
860-
if ($rootScope.$broadcast('$locationChangeStart', $location.absUrl(), oldUrl,
861-
$location.$$state, oldState).defaultPrevented) {
868+
var newUrl = $location.absUrl();
869+
var defaultPrevented = $rootScope.$broadcast('$locationChangeStart', newUrl, oldUrl,
870+
$location.$$state, oldState).defaultPrevented;
871+
872+
// if the location was changed by a `$locationChangeStart` handler then stop
873+
// processing this location change
874+
if ($location.absUrl() !== newUrl) return;
875+
876+
if (defaultPrevented) {
862877
$location.$$parse(oldUrl);
863878
$location.$$state = oldState;
864879
} else {
865880
if (urlOrStateChanged) {
866-
setBrowserUrlWithFallback($location.absUrl(), currentReplace,
881+
setBrowserUrlWithFallback(newUrl, currentReplace,
867882
oldState === $location.$$state ? null : $location.$$state);
868883
}
869884
afterLocationChange(oldUrl, oldState);

test/ng/locationSpec.js

+149
Original file line numberDiff line numberDiff line change
@@ -1690,6 +1690,95 @@ describe('$location', function() {
16901690
expect($browser.url()).toEqual('http://server/');
16911691
}));
16921692

1693+
it('should allow redirect during $locationChangeStart',
1694+
inject(function($location, $browser, $rootScope, $log) {
1695+
$rootScope.$on('$locationChangeStart', function(event, newUrl, oldUrl) {
1696+
$log.info('before', newUrl, oldUrl, $browser.url());
1697+
if (newUrl === 'http://server/#/somePath') {
1698+
$location.url('/redirectPath');
1699+
}
1700+
});
1701+
$rootScope.$on('$locationChangeSuccess', function(event, newUrl, oldUrl) {
1702+
$log.info('after', newUrl, oldUrl, $browser.url());
1703+
});
1704+
1705+
$location.url('/somePath');
1706+
$rootScope.$apply();
1707+
1708+
expect($log.info.logs.shift()).
1709+
toEqual(['before', 'http://server/#/somePath', 'http://server/', 'http://server/']);
1710+
expect($log.info.logs.shift()).
1711+
toEqual(['before', 'http://server/#/redirectPath', 'http://server/', 'http://server/']);
1712+
expect($log.info.logs.shift()).
1713+
toEqual(['after', 'http://server/#/redirectPath', 'http://server/',
1714+
'http://server/#/redirectPath']);
1715+
1716+
expect($location.url()).toEqual('/redirectPath');
1717+
expect($browser.url()).toEqual('http://server/#/redirectPath');
1718+
})
1719+
);
1720+
1721+
it('should allow redirect during $locationChangeStart even if default prevented',
1722+
inject(function($location, $browser, $rootScope, $log) {
1723+
$rootScope.$on('$locationChangeStart', function(event, newUrl, oldUrl) {
1724+
$log.info('before', newUrl, oldUrl, $browser.url());
1725+
if (newUrl === 'http://server/#/somePath') {
1726+
event.preventDefault();
1727+
$location.url('/redirectPath');
1728+
}
1729+
});
1730+
$rootScope.$on('$locationChangeSuccess', function(event, newUrl, oldUrl) {
1731+
$log.info('after', newUrl, oldUrl, $browser.url());
1732+
});
1733+
1734+
$location.url('/somePath');
1735+
$rootScope.$apply();
1736+
1737+
expect($log.info.logs.shift()).
1738+
toEqual(['before', 'http://server/#/somePath', 'http://server/', 'http://server/']);
1739+
expect($log.info.logs.shift()).
1740+
toEqual(['before', 'http://server/#/redirectPath', 'http://server/', 'http://server/']);
1741+
expect($log.info.logs.shift()).
1742+
toEqual(['after', 'http://server/#/redirectPath', 'http://server/',
1743+
'http://server/#/redirectPath']);
1744+
1745+
expect($location.url()).toEqual('/redirectPath');
1746+
expect($browser.url()).toEqual('http://server/#/redirectPath');
1747+
})
1748+
);
1749+
1750+
it('should allow multiple redirect during $locationChangeStart',
1751+
inject(function($location, $browser, $rootScope, $log) {
1752+
$rootScope.$on('$locationChangeStart', function(event, newUrl, oldUrl) {
1753+
$log.info('before', newUrl, oldUrl, $browser.url());
1754+
if (newUrl === 'http://server/#/somePath') {
1755+
$location.url('/redirectPath');
1756+
} else if (newUrl === 'http://server/#/redirectPath') {
1757+
$location.url('/redirectPath2');
1758+
}
1759+
});
1760+
$rootScope.$on('$locationChangeSuccess', function(event, newUrl, oldUrl) {
1761+
$log.info('after', newUrl, oldUrl, $browser.url());
1762+
});
1763+
1764+
$location.url('/somePath');
1765+
$rootScope.$apply();
1766+
1767+
expect($log.info.logs.shift()).
1768+
toEqual(['before', 'http://server/#/somePath', 'http://server/', 'http://server/']);
1769+
expect($log.info.logs.shift()).
1770+
toEqual(['before', 'http://server/#/redirectPath', 'http://server/', 'http://server/']);
1771+
expect($log.info.logs.shift()).
1772+
toEqual(['before', 'http://server/#/redirectPath2', 'http://server/', 'http://server/']);
1773+
expect($log.info.logs.shift()).
1774+
toEqual(['after', 'http://server/#/redirectPath2', 'http://server/',
1775+
'http://server/#/redirectPath2']);
1776+
1777+
expect($location.url()).toEqual('/redirectPath2');
1778+
expect($browser.url()).toEqual('http://server/#/redirectPath2');
1779+
})
1780+
);
1781+
16931782
it ('should fire $locationChangeSuccess event when change from browser location bar',
16941783
inject(function($log, $location, $browser, $rootScope) {
16951784
$rootScope.$apply(); // clear initial $locationChangeStart
@@ -1715,6 +1804,66 @@ describe('$location', function() {
17151804
})
17161805
);
17171806

1807+
it('should allow redirect during browser url change',
1808+
inject(function($location, $browser, $rootScope, $log) {
1809+
$rootScope.$on('$locationChangeStart', function(event, newUrl, oldUrl) {
1810+
$log.info('before', newUrl, oldUrl, $browser.url());
1811+
if (newUrl === 'http://server/#/somePath') {
1812+
$location.url('/redirectPath');
1813+
}
1814+
});
1815+
$rootScope.$on('$locationChangeSuccess', function(event, newUrl, oldUrl) {
1816+
$log.info('after', newUrl, oldUrl, $browser.url());
1817+
});
1818+
1819+
$browser.url('http://server/#/somePath');
1820+
$browser.poll();
1821+
1822+
expect($log.info.logs.shift()).
1823+
toEqual(['before', 'http://server/#/somePath', 'http://server/',
1824+
'http://server/#/somePath']);
1825+
expect($log.info.logs.shift()).
1826+
toEqual(['before', 'http://server/#/redirectPath', 'http://server/#/somePath',
1827+
'http://server/#/somePath']);
1828+
expect($log.info.logs.shift()).
1829+
toEqual(['after', 'http://server/#/redirectPath', 'http://server/#/somePath',
1830+
'http://server/#/redirectPath']);
1831+
1832+
expect($location.url()).toEqual('/redirectPath');
1833+
expect($browser.url()).toEqual('http://server/#/redirectPath');
1834+
})
1835+
);
1836+
1837+
it('should allow redirect during browser url change even if default prevented',
1838+
inject(function($location, $browser, $rootScope, $log) {
1839+
$rootScope.$on('$locationChangeStart', function(event, newUrl, oldUrl) {
1840+
$log.info('before', newUrl, oldUrl, $browser.url());
1841+
if (newUrl === 'http://server/#/somePath') {
1842+
event.preventDefault();
1843+
$location.url('/redirectPath');
1844+
}
1845+
});
1846+
$rootScope.$on('$locationChangeSuccess', function(event, newUrl, oldUrl) {
1847+
$log.info('after', newUrl, oldUrl, $browser.url());
1848+
});
1849+
1850+
$browser.url('http://server/#/somePath');
1851+
$browser.poll();
1852+
1853+
expect($log.info.logs.shift()).
1854+
toEqual(['before', 'http://server/#/somePath', 'http://server/',
1855+
'http://server/#/somePath']);
1856+
expect($log.info.logs.shift()).
1857+
toEqual(['before', 'http://server/#/redirectPath', 'http://server/#/somePath',
1858+
'http://server/#/somePath']);
1859+
expect($log.info.logs.shift()).
1860+
toEqual(['after', 'http://server/#/redirectPath', 'http://server/#/somePath',
1861+
'http://server/#/redirectPath']);
1862+
1863+
expect($location.url()).toEqual('/redirectPath');
1864+
expect($browser.url()).toEqual('http://server/#/redirectPath');
1865+
})
1866+
);
17181867

17191868
it('should listen on click events on href and prevent browser default in hashbang mode', function() {
17201869
module(function() {

test/ngRoute/routeSpec.js

+30
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,36 @@ describe('$route', function() {
7070
});
7171
});
7272

73+
it('should allow redirects while handling $routeChangeStart', function() {
74+
module(function($routeProvider) {
75+
$routeProvider.when('/some', {
76+
id: 'some', template: 'Some functionality'
77+
});
78+
$routeProvider.when('/redirect', {
79+
id: 'redirect'
80+
});
81+
});
82+
module(provideLog);
83+
inject(function($route, $location, $rootScope, $compile, log) {
84+
$rootScope.$on('$routeChangeStart', function(event, next, current) {
85+
if (next.id === 'some') {
86+
$location.path('/redirect');
87+
}
88+
});
89+
$compile('<div><div ng-view></div></div>')($rootScope);
90+
$rootScope.$on('$routeChangeStart', log.fn('routeChangeStart'));
91+
$rootScope.$on('$routeChangeError', log.fn('routeChangeError'));
92+
$rootScope.$on('$routeChangeSuccess', log.fn('routeChangeSuccess'));
93+
$rootScope.$apply(function() {
94+
$location.path('/some');
95+
});
96+
97+
expect($route.current.id).toBe('redirect');
98+
expect($location.path()).toBe('/redirect');
99+
expect(log).toEqual(['routeChangeStart', 'routeChangeStart', 'routeChangeSuccess']);
100+
});
101+
});
102+
73103
it('should route and fire change event', function() {
74104
var log = '',
75105
lastRoute,

0 commit comments

Comments
 (0)