diff --git a/Gruntfile.js b/Gruntfile.js index 66de24c05..74d80b9c7 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -93,6 +93,18 @@ module.exports = function (grunt) { options: { dest: 'CHANGELOG.md' } + }, + ngdocs: { + options: { + dest: 'site', + html5Mode: false, + title: 'UI Router', + startPage: '/api', + }, + api: { + src: ['src/**/*.js'], + title: 'API Reference' + } } }); diff --git a/package.json b/package.json index b5e79face..7dd3a92eb 100644 --- a/package.json +++ b/package.json @@ -18,10 +18,12 @@ "web": "https://github.com/ksperling" } ], - "maintainers": [{ - "name": "AngularUI", - "web": "https://github.com/angular-ui?tab=members" - }], + "maintainers": [ + { + "name": "AngularUI", + "web": "https://github.com/angular-ui?tab=members" + } + ], "repository": { "type": "git", "url": "https://github.com/angular-ui/ui-router.git" @@ -58,6 +60,7 @@ "karma": "~0.10.4", "karma-phantomjs-launcher": "~0.1.0", "load-grunt-tasks": "~0.2.0", - "grunt-conventional-changelog": "~1.0.0" + "grunt-conventional-changelog": "~1.0.0", + "grunt-ngdocs": "~0.1.7" } } diff --git a/src/common.js b/src/common.js index 323d31e03..1dd8489e2 100644 --- a/src/common.js +++ b/src/common.js @@ -164,9 +164,55 @@ function filterByKeys(keys, values) { return filtered; } +/** + * @ngdoc overview + * @name ui.router.util + * + * @description + * + */ angular.module('ui.router.util', ['ng']); + +/** + * @ngdoc overview + * @name ui.router.router + * + * @requires ui.router.util + * + * @description + * + */ angular.module('ui.router.router', ['ui.router.util']); + +/** + * @ngdoc overview + * @name ui.router.router + * + * @requires ui.router.router + * @requires ui.router.util + * + * @description + * + */ angular.module('ui.router.state', ['ui.router.router', 'ui.router.util']); + +/** + * @ngdoc overview + * @name ui.router + * + * @requires ui.router.state + * + * @description + * + */ angular.module('ui.router', ['ui.router.state']); +/** + * @ngdoc overview + * @name ui.router.compat + * + * @requires ui.router + * + * @description + * + */ angular.module('ui.router.compat', ['ui.router']); - diff --git a/src/compat.js b/src/compat.js index 63d092d93..ad30839d7 100644 --- a/src/compat.js +++ b/src/compat.js @@ -1,3 +1,21 @@ +/** + * @ngdoc object + * @name ui.router.compat.$routeProvider + * + * @requires ui.router.state.$stateProvider + * @requires ui.router.router.$urlRouterProvider + * + * @description + * `$routeProvider` of the `ui.router.compat` module overwrites the existing + * `routeProvider` from the core. This is done to provide compatibility between + * the UI Router and the core router. + * + * It also provides a `when()` method to register routes that map to certain urls. + * Behind the scenes it actually delegates either to + * {@link ui.router.router.$urlRouterProvider $urlRouterProvider} or to the + * {@link ui.router.state.$stateProvider $stateProvider} to postprocess the given + * router definition object. + */ $RouteProvider.$inject = ['$stateProvider', '$urlRouterProvider']; function $RouteProvider( $stateProvider, $urlRouterProvider) { @@ -17,6 +35,32 @@ function $RouteProvider( $stateProvider, $urlRouterProvider) { } this.when = when; + /** + * @ngdoc function + * @name ui.router.compat.$routeProvider#when + * @methodOf ui.router.compat.$routeProvider + * + * @description + * Registers a route with a given route definition object. The route definition + * object has the same interface the angular core route definition object has. + * + * @example + *
+ * var app = angular.module('app', ['ui.router.compat']); + * + * app.config(function ($routeProvider) { + * $routeProvider.when('home', { + * controller: function () { ... }, + * templateUrl: 'path/to/template' + * }); + * }); + *+ * + * @param {string} url URL as string + * @param {object} route Route definition object + * + * @return {object} $routeProvider - $routeProvider instance + */ function when(url, route) { /*jshint validthis: true */ if (route.redirectTo != null) { @@ -47,6 +91,24 @@ function $RouteProvider( $stateProvider, $urlRouterProvider) { return this; } + /** + * @ngdoc object + * @name ui.router.compat.$route + * + * @requires ui.router.state.$state + * @requires $rootScope + * @requires $routeParams + * + * @property {object} routes - Array of registered routes. + * @property {object} params - Current route params as object. + * @property {string} current - Name of the current route. + * + * @description + * The `$route` service provides interfaces to access defined routes. It also let's + * you access route params through `$routeParams` service, so you have fully + * control over all the stuff you would actually get from angular's core `$route` + * service. + */ this.$get = $get; $get.$inject = ['$state', '$rootScope', '$routeParams']; function $get( $state, $rootScope, $routeParams) { diff --git a/src/state.js b/src/state.js index ed9c985ef..fffa02e66 100644 --- a/src/state.js +++ b/src/state.js @@ -1,3 +1,25 @@ +/** + * @ngdoc object + * @name ui.router.state.$stateProvider + * + * @requires ui.router.router.$urlRouterProvider + * @requires ui.router.util.$urlMatcherFactoryProvider + * @requires $locationProvider + * + * @description + * The new `$stateProvider` works similar to Angular's v1 router, but it focuses purely + * on state. + * + * A state corresponds to a "place" in the application in terms of the overall UI and + * navigation. A state describes (via the controller / template / view properties) what + * the UI looks like and does at that place. + * + * States often have things in common, and the primary way of factoring out these + * commonalities in this model is via the state hierarchy, i.e. parent/child states aka + * nested states. + * + * The `$stateProvider` provides interfaces to declare these states for your app. + */ $StateProvider.$inject = ['$urlRouterProvider', '$urlMatcherFactoryProvider', '$locationProvider']; function $StateProvider( $urlRouterProvider, $urlMatcherFactory, $locationProvider) { @@ -207,9 +229,96 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory, $ root.navigable = null; - // .decorator() - // .decorator(name) - // .decorator(name, function) + /** + * @ngdoc function + * @name ui.router.state.$stateProvider#decorator + * @methodOf ui.router.state.$stateProvider + * + * @description + * Allows you to extend (carefully) or override (at your own peril) the + * `stateBuilder` object used internally by `$stateProvider`. This can be used + * to add custom functionality to ui-router, for example inferring templateUrl + * based on the state name. + * + * When passing only a name, it returns the current (original or decorated) builder + * function that matches `name`. + * + * The builder functions that can be decorated are listed below. Though not all + * necessarily have a good use case for decoration, that is up to you to decide. + * + * In addition, users can attach custom decorators, which will generate new + * properties within the state's internal definition. There is currently no clear + * use-case for this beyond accessing internal states (i.e. $state.$current), + * however, expect this to become increasingly relevant as we introduce additional + * meta-programming features. + * + * **Warning**: Decorators should not be interdependent because the order of + * execution of the builder functions in nondeterministic. Builder functions + * should only be dependent on the state definition object and super function. + * + * + * Existing builder functions and current return values: + * + * - parent - `{object}` - returns the parent state object. + * - data - `{object}` - returns state data, including any inherited data that is not + * overridden by own values (if any). + * - url - `{object}` - returns a UrlMatcher or null. + * - navigable - returns closest ancestor state that has a URL (aka is + * navigable). + * - params - `{object}` - returns an array of state params that are ensured to + * be a super-set of parent's params. + * - views - `{object}` - returns a views object where each key is an absolute view + * name (i.e. "viewName@stateName") and each value is the config object + * (template, controller) for the view. Even when you don't use the views object + * explicitly on a state config, one is still created for you internally. + * So by decorating this builder function you have access to decorating template + * and controller properties. + * - ownParams - `{object}` - returns an array of params that belong to the state, + * not including any params defined by ancestor states. + * - path - `{string}` - returns the full path from the root down to this state. + * Needed for state activation. + * - includes - `{object}` - returns an object that includes every state that + * would pass a '$state.includes()' test. + * + * @example + *
+ * // Override the internal 'views' builder with a function that takes the state + * // definition, and a reference to the internal function being overridden: + * $stateProvider.decorator('views', function ($state, parent) { + * var result = {}, + * views = parent(state); + * + * angular.forEach(view, function (config, name) { + * var autoName = (state.name + '.' + name).replace('.', '/'); + * config.templateUrl = config.templateUrl || '/partials/' + autoName + '.html'; + * result[name] = config; + * }); + * return result; + * }); + * + * $stateProvider.state('home', { + * views: { + * 'contact.list': { controller: 'ListController' }, + * 'contact.item': { controller: 'ItemController' } + * } + * }); + * + * // ... + * + * $state.go('home'); + * // Auto-populates list and item views with /partials/home/contact/list.html, + * // and /partials/home/contact/item.html, respectively. + *+ * + * @param {string} name The name of the builder function to decorate. + * @param {object} func A function that is responsible for decorating the original + * builder function. The function receives two parameters: + * + * - `{object}` - state - The state config object. + * - `{object}` - super - The original builder function. + * + * @return {object} $stateProvider - $stateProvider instance + */ this.decorator = decorator; function decorator(name, func) { /*jshint validthis: true */ diff --git a/src/urlRouter.js b/src/urlRouter.js index 6bc0ac89a..8bc72fc60 100644 --- a/src/urlRouter.js +++ b/src/urlRouter.js @@ -1,4 +1,18 @@ - +/** + * @ngdoc object + * @name ui.router.router.$urlRouterProvider + * + * @requires ui.router.util.$urlMatcherFactoryProvider + * + * @description + * `$urlRouterProvider` has the responsibility of watching `$location`. + * When `$location` changes it runs through a list of rules one by one until a + * match is found. `$urlRouterProvider` is used behind the scenes anytime you specify + * a url in a state configuration. All urls are compiled into a UrlMatcher object. + * + * There are several methods on `$urlRouterProvider` that make it useful to use directly + * in your module config. + */ $UrlRouterProvider.$inject = ['$urlMatcherFactoryProvider']; function $UrlRouterProvider( $urlMatcherFactory) { var rules = [], @@ -17,6 +31,37 @@ function $UrlRouterProvider( $urlMatcherFactory) { }); } + /** + * @ngdoc function + * @name ui.router.router.$urlRouterProvider#rule + * @methodOf ui.router.router.$urlRouterProvider + * + * @description + * Defines rules that are used by `$urlRouterProvider to find matches for + * specific URLs. + * + * @example + *
+ * var app = angular.module('app', ['ui.router.router']); + * + * app.config(function ($urlRouterProvider) { + * // Here's an example of how you might allow case insensitive urls + * $urlRouterProvider.rule(function ($injector, $location) { + * var path = $location.path(), + * normalized = path.toLowerCase(); + * + * if (path !== normalized) { + * return normalized; + * } + * }); + * }); + *+ * + * @param {object} rule Handler function that takes `$injector` and `$location` + * services as arguments. You can use them to return a valid path as a string. + * + * @return {object} $urlRouterProvider - $urlRouterProvider instance + */ this.rule = function (rule) { if (!isFunction(rule)) throw new Error("'rule' must be a function"); @@ -24,6 +69,37 @@ function $UrlRouterProvider( $urlMatcherFactory) { return this; }; + /** + * @ngdoc object + * @name ui.router.router.$urlRouterProvider#otherwise + * @methodOf ui.router.router.$urlRouterProvider + * + * @description + * Defines a path that is used when an invalied route is requested. + * + * @example + *
+ * var app = angular.module('app', ['ui.router.router']); + * + * app.config(function ($urlRouterProvider) { + * // if the path doesn't match any of the urls you configured + * // otherwise will take care of routing the user to the + * // specified url + * $urlRouterProvider.otherwise('/index'); + * + * // Example of using function rule as param + * $urlRouterProvider.otherwise(function ($injector, $location) { + * ... + * }); + * }); + *+ * + * @param {string|object} rule The url path you want to redirect to or a function + * rule that returns the url path. The function version is passed two params: + * `$injector` and `$location` services. + * + * @return {object} $urlRouterProvider - $urlRouterProvider instance + */ this.otherwise = function (rule) { if (isString(rule)) { @@ -42,6 +118,43 @@ function $UrlRouterProvider( $urlMatcherFactory) { return isDefined(result) ? result : true; } + /** + * @ngdoc function + * @name ui.router.router.$urlRouterProvider#when + * @methodOf ui.router.router.$urlRouterProvider + * + * @description + * Registers a handler for a given url matching. if handle is a string, it is + * treated as a redirect, and is interpolated according to the syyntax of match + * (i.e. like String.replace() for RegExp, or like a UrlMatcher pattern otherwise). + * + * If the handler is a function, it is injectable. It gets invoked if `$location` + * matches. You have the option of inject the match object as `$match`. + * + * The handler can return + * + * - **falsy** to indicate that the rule didn't match after all, then `$urlRouter` + * will continue trying to find another one that matches. + * - **string** which is treated as a redirect and passed to `$location.url()` + * - **void** or any **truthy** value tells `$urlRouter` that the url was handled. + * + * @example + *
+ * var app = angular.module('app', ['ui.router.router']); + * + * app.config(function ($urlRouterProvider) { + * $urlRouterProvider.when($state.url, function ($match, $stateParams) { + * if ($state.$current.navigable !== state || + * !equalForKeys($match, $stateParams) { + * $state.transitionTo(state, $match, false); + * } + * }); + * }); + *+ * + * @param {string|object} what The incoming path that you want to redirect. + * @param {string|object} handler The path you want to redirect your user to. + */ this.when = function (what, handler) { var redirect, handlerIsString = isString(handler); @@ -88,6 +201,17 @@ function $UrlRouterProvider( $urlMatcherFactory) { throw new Error("invalid 'what' in when()"); }; + /** + * @ngdoc object + * @name ui.router.router.$urlRouter + * + * @requires $location + * @requires $rootScope + * @requires $injector + * + * @description + * + */ this.$get = [ '$location', '$rootScope', '$injector', function ($location, $rootScope, $injector) { @@ -113,6 +237,23 @@ function $UrlRouterProvider( $urlMatcherFactory) { $rootScope.$on('$locationChangeSuccess', update); return { + /** + * @ngdoc function + * @name ui.router.router.$urlRouter#sync + * @methodOf ui.router.router.$urlRouter + * + * @description + * Checks registered rules until first rule is handled. + * + * @example + *
+ * var app = angular.module('app', ['ui.router.router']); + * + * app.run(function ($urlRouter) { + * $urlRouter.sync(); + * }); + *+ */ sync: function () { update(); }