You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
BREAKING CHANGE: Order URL Matching Rules by priority, not registration order
URL Rules can come from registered states' `.url`s, calling `.when()`, or calling `.rule()`.
It's possible that two or more URL Rules could match the URL.
### Previously
Previously, url rules were matched in the order in which they were registered.
The rule which was registered first would handle the URL change.
### Now
Now, the URL rules are sorted according to a sort function.
More specific rules are preferred over less specific rules
### Why
It's possible to have multiple url rules that match a given URL.
Consider the following states:
- `{ name: 'books', url: '/books/index' }''`
- `{ name: 'book', url: '/books/:bookId' }''`
Both states match when the url is `/books/index`.
Additionally, you might have some custom url rewrite rules such as:
`.when('/books/list', '/books/index')`.
The `book` state also matches when the rewrite rule is matched.
Previously, we simply used the first rule that matched. However, now that lazy loading is officially supported, it can be difficult for developers to ensure the rules are registered in the right order.
Instead, we now prioritize url rules by how specific they are. More specific rules are matched earlier than less specific rules.
We split the path on `/`. A static segment (such as `index` in the example) is more specific than a parameter (such as`:bookId`).
### More Details
The built-in rule sorting function (see `UrlRouter.defaultRuleSortFn`) sorts rules in this order:
- Explicit priority: `.when('/foo', '/bar', { priority: 1 })` (default priority is 0)
- Rule Type:
- UrlMatchers first (registered states and `.when(string, ...)`)
- then regular Expressions (`.when(regexp, ...)`)
- finally, everything else (`.rule()`)
- UrlMatcher specificity: static path segments are more specific than variables (see `UrlMatcher.compare`)
- Registration order (except for UrlMatcher based rules)
For complete control, a custom sort function can be registered with `UrlService.rules.sort(sortFn)`
### Query params
Because query parameters are optional, they are not considered during sorting.
For example, both these rules will match when the url is `'/foo/bar'`:
```
.when('/foo/bar', doSomething);
.when('/foo/bar?queryparam', doSomethingElse);
```
To choose the most specific rule, we match both rules, then choose the rule with the "best ratio" of matched optional parameters (see `UrlRuleFactory.fromUrlMatcher`)
This allows child states to be defined with only query params for a URL.
The state only activates when the query parameter is present.
```
.state('parent', { url: '/parent' });
.state('parent.child', { url: '?queryParam' });
```
## Restoring the previous behavior
For backwards compatibility, register a sort function which sorts by the registration order:
```js
myApp.config(function ($urlServiceProvider) {
function sortByRegistrationOrder(a, b) {
return a.$id - b.$id;
}
$urlServiceProvider.rules.sort(sortByRegistrationOrder);
});
```
---
feat(UrlRouter): sort url rules by specificity, not by registration order.
refactor(UrlMatcher): Include own matcher in matcher._cache.path
feat(UrlMatcher): Add comparison function by UrlMatcher specificity
refactor(UrlRule): Use interface for UrlRules instead of extending classes
0 commit comments