问题
I'm coming from the Asp.net MVC world where route constraints are really useful. I'm currently developing an Angular JS SPA that incorporates client side routing.
I would also like to add parameter constraints to my client-side routes as in:
$.routeProvider.when("/post/:id/:name?")
where I'd like to constrain my :id
parameter to only be valid when these are numbers.
I haven't found anything similar in Angular Docs (which are horrible to say the least). Can it be done? And maybe also parameter defaults?
回答1:
There's no built-in way to set route constraints, but you can do it pretty easily yourself by using the resolve feature.
module.config(function ($routeProvider) {
$routeProvider.when('/post/:id/:name', {
controller: 'PostCtrl',
resolve: {
id: function ($q, $route) {
var deferred = $q.defer(),
id = parseInt($route.current.params.id, 10);
if (!isNaN(id)) {
deferred.resolve(id);
} else {
deferred.reject('Id is not a number');
}
return deferred.promise;
},
// This isn't really needed, you can extract the name using
// $routeParams.name as you usually would in the controller.
name: function ($route) {
return $route.current.params.name;
}
}
});
});
By doing it this way you can inject id
and name
into PostCtrl
:
module.controller('PostCtrl', function ($scope, $routeParams, id, name) {
});
id
in this case will be a number. You can of course also use $routeParams.id
and $routeParams.name
but those are always strings.
In case something else than a number is passed, then the promise is rejected and you need to handle that, usually in the 'MainCtrl' or 'AppCtrl' if you're doing a SPA.
module.controller('AppCtrl', function ($scope, $log) {
$scope.$on('$routeChangeError', function (ev, current, previous, rejection) {
$log.error('Error changing route: ' + rejection);
// Will print: Error changing route: Id is not a number
});
});
Update
You can use resolve in combination with the $routeChangeError
event to validate the route and redirect on error. The resolve properties are always run, even if they aren't injected in the controller.
resolve: {
validation: function ($q, $route) {
var deferred = $q.defer(),
id = parseInt($route.current.params.id, 10);
if (!isNaN(id)) {
deferred.resolve();
} else {
deferred.reject('VALIDATION FAILED');
}
return deferred.promise;
}
}
In your home controller simply add:
module.controller('AppCtrl', function ($scope, $location) {
$scope.$on('$routeChangeError', function (ev, current, previous, rejection) {
if (rejection === 'VALIDATION FAILED') {
$location.path('/home');
}
});
});
This is a quite hacky way to solve it and you might be better of using ui-router (I haven't tried that one). Unfortunately it's not possible to listen to $routeChangeStart
and abort if needed. Otherwise this could've been solved a lot nicer.
回答2:
Angular route source change - most transparent constraints
There's a pull request in AngularJS source (made by me) that adds regular expression constraints to route parameters. It works a simple as providing this kind of route definition:
/:parameter:regularexpression
An example would be
/profile/:id:[1-9]\\d*/edit
This means that id
has to be a number not starting with a 0.
Additional requirements
There are of course some additional requirements to these parameters:
parameter can't be optional or greedy - if it ends with
?
or*
this character will become part of regular expression (as these are both regular expression tokens with their respective meaningregular expression constraint can't use regular expression capture groups as
(
and)
get escaped as part of regular expression constraint - this already works this way so if your parameter name includes(
or)
they will both be escapedregular expression constraint can't include
/
as these are route segment delimiters
回答3:
One way is to use ui-router, which allows to specify url using regex.
来源:https://stackoverflow.com/questions/21205937/angular-route-parameters-contraints