diff --git a/sample/app/app.js b/sample/app/app.js new file mode 100644 index 000000000..eacc40127 --- /dev/null +++ b/sample/app/app.js @@ -0,0 +1,96 @@ +// Make sure to include the `ui.router` module as a dependency +angular.module('uiRouterSample', [ + 'uiRouterSample.contacts', + 'uiRouterSample.contacts.service', + 'uiRouterSample.utils.service', + 'ui.router', + 'ngAnimate' +]) + +.run( + [ '$rootScope', '$state', '$stateParams', + function ($rootScope, $state, $stateParams) { + + // It's very handy to add references to $state and $stateParams to the $rootScope + // so that you can access them from any scope within your applications.For example, + //
  • will set the
  • // to active whenever + // 'contacts.list' or one of its decendents is active. + $rootScope.$state = $state; + $rootScope.$stateParams = $stateParams; + } + ] +) + +.config( + [ '$stateProvider', '$urlRouterProvider', + function ($stateProvider, $urlRouterProvider) { + + ///////////////////////////// + // Redirects and Otherwise // + ///////////////////////////// + + // Use $urlRouterProvider to configure any redirects (when) and invalid urls (otherwise). + $urlRouterProvider + + // The `when` method says if the url is ever the 1st param, then redirect to the 2nd param + // Here we are just setting up some convenience urls. + .when('/c?id', '/contacts/:id') + .when('/user/:id', '/contacts/:id') + + // If the url is ever invalid, e.g. '/asdf', then redirect to '/' aka the home state + .otherwise('/'); + + + ////////////////////////// + // State Configurations // + ////////////////////////// + + // Use $stateProvider to configure your states. + $stateProvider + + ////////// + // Home // + ////////// + + .state("home", { + + // Use a url of "/" to set a states as the "index". + url: "/", + + // Example of an inline template string. By default, templates + // will populate the ui-view within the parent state's template. + // For top level states, like this one, the parent template is + // the index.html file. So this template will be inserted into the + // ui-view within index.html. + template: '

    Welcome to the UI-Router Demo

    ' + + '

    Use the menu above to navigate. ' + + 'Pay attention to the $state and $stateParams values below.

    ' + + '

    Click these links—Alice or ' + + 'Bob—to see a url redirect in action.

    ' + + }) + + /////////// + // About // + /////////// + + .state('about', { + url: '/about', + + // Showing off how you could return a promise from templateProvider + templateProvider: ['$timeout', + function ( $timeout) { + return $timeout(function () { + return '

    UI-Router Resources

    '; + }, 100); + }] + }) + } + ] +); diff --git a/sample/app/contacts/contacts-service.js b/sample/app/contacts/contacts-service.js new file mode 100644 index 000000000..7125d93c2 --- /dev/null +++ b/sample/app/contacts/contacts-service.js @@ -0,0 +1,22 @@ +angular.module('uiRouterSample.contacts.service', [ + +]) + +// A RESTful factory for retreiving contacts from 'contacts.json' +.factory('contacts', ['$http', function ($http, utils) { + var path = 'assets/contacts.json'; + var contacts = $http.get(path).then(function (resp) { + return resp.data.contacts; + }); + + var factory = {}; + factory.all = function () { + return contacts; + }; + factory.get = function (id) { + return contacts.then(function(){ + return utils.findById(contacts, id); + }) + }; + return factory; +}]); \ No newline at end of file diff --git a/sample/contacts.detail.html b/sample/app/contacts/contacts.detail.html similarity index 100% rename from sample/contacts.detail.html rename to sample/app/contacts/contacts.detail.html diff --git a/sample/contacts.detail.item.edit.html b/sample/app/contacts/contacts.detail.item.edit.html similarity index 100% rename from sample/contacts.detail.item.edit.html rename to sample/app/contacts/contacts.detail.item.edit.html diff --git a/sample/contacts.detail.item.html b/sample/app/contacts/contacts.detail.item.html similarity index 100% rename from sample/contacts.detail.item.html rename to sample/app/contacts/contacts.detail.item.html diff --git a/sample/contacts.html b/sample/app/contacts/contacts.html similarity index 100% rename from sample/contacts.html rename to sample/app/contacts/contacts.html diff --git a/sample/app/contacts/contacts.js b/sample/app/contacts/contacts.js new file mode 100644 index 000000000..6414f578a --- /dev/null +++ b/sample/app/contacts/contacts.js @@ -0,0 +1,206 @@ +angular.module('uiRouterSample.contacts', [ + 'ui.router' +]) + +.config( + [ '$stateProvider', '$urlRouterProvider', + function ($stateProvider, $urlRouterProvider) { + $stateProvider + ////////////// + // Contacts // + ////////////// + .state('contacts', { + + // With abstract set to true, that means this state can not be explicitly activated. + // It can only be implicitly activated by activating one of it's children. + abstract: true, + + // This abstract state will prepend '/contacts' onto the urls of all its children. + url: '/contacts', + + // Example of loading a template from a file. This is also a top level state, + // so this template file will be loaded and then inserted into the ui-view + // within index.html. + templateUrl: 'app/contacts/contacts.html', + + // Use `resolve` to resolve any asynchronous controller dependencies + // *before* the controller is instantiated. In this case, since contacts + // returns a promise, the controller will wait until contacts.all() is + // resolved before instantiation. Non-promise return values are considered + // to be resolved immediately. + resolve: { + contacts: ['contacts', + function( contacts){ + return contacts.all(); + }] + }, + + // You can pair a controller to your template. There *must* be a template to pair with. + controller: ['$scope', '$state', 'contacts', 'utils', + function ( $scope, $state, contacts, utils) { + + // Add a 'contacts' field in this abstract parent's scope, so that all + // child state views can access it in their scopes. Please note: scope + // inheritance is not due to nesting of states, but rather choosing to + // nest the templates of those states. It's normal scope inheritance. + $scope.contacts = contacts; + + $scope.goToRandom = function () { + var randId = utils.newRandomKey($scope.contacts, "id", $state.params.contactId); + + // $state.go() can be used as a high level convenience method + // for activating a state programmatically. + $state.go('contacts.detail', { contactId: randId }); + }; + }] + }) + + ///////////////////// + // Contacts > List // + ///////////////////// + + // Using a '.' within a state name declares a child within a parent. + // So you have a new state 'list' within the parent 'contacts' state. + .state('contacts.list', { + + // Using an empty url means that this child state will become active + // when its parent's url is navigated to. Urls of child states are + // automatically appended to the urls of their parent. So this state's + // url is '/contacts' (because '/contacts' + ''). + url: '', + + // IMPORTANT: Now we have a state that is not a top level state. Its + // template will be inserted into the ui-view within this state's + // parent's template; so the ui-view within contacts.html. This is the + // most important thing to remember about templates. + templateUrl: 'app/contacts/contacts.list.html' + }) + + /////////////////////// + // Contacts > Detail // + /////////////////////// + + // You can have unlimited children within a state. Here is a second child + // state within the 'contacts' parent state. + .state('contacts.detail', { + + // Urls can have parameters. They can be specified like :param or {param}. + // If {} is used, then you can also specify a regex pattern that the param + // must match. The regex is written after a colon (:). Note: Don't use capture + // groups in your regex patterns, because the whole regex is wrapped again + // behind the scenes. Our pattern below will only match numbers with a length + // between 1 and 4. + + // Since this state is also a child of 'contacts' its url is appended as well. + // So its url will end up being '/contacts/{contactId:[0-9]{1,8}}'. When the + // url becomes something like '/contacts/42' then this state becomes active + // and the $stateParams object becomes { contactId: 42 }. + url: '/{contactId:[0-9]{1,4}}', + + // If there is more than a single ui-view in the parent template, or you would + // like to target a ui-view from even higher up the state tree, you can use the + // views object to configure multiple views. Each view can get its own template, + // controller, and resolve data. + + // View names can be relative or absolute. Relative view names do not use an '@' + // symbol. They always refer to views within this state's parent template. + // Absolute view names use a '@' symbol to distinguish the view and the state. + // So 'foo@bar' means the ui-view named 'foo' within the 'bar' state's template. + views: { + + // So this one is targeting the unnamed view within the parent state's template. + '': { + templateUrl: 'app/contacts/contacts.detail.html', + controller: ['$scope', '$stateParams', 'utils', + function ( $scope, $stateParams, utils) { + $scope.contact = utils.findById($scope.contacts, $stateParams.contactId); + }] + }, + + // This one is targeting the ui-view="hint" within the unnamed root, aka index.html. + // This shows off how you could populate *any* view within *any* ancestor state. + 'hint@': { + template: 'This is contacts.detail populating the "hint" ui-view' + }, + + // This one is targeting the ui-view="menu" within the parent state's template. + 'menuTip': { + // templateProvider is the final method for supplying a template. + // There is: template, templateUrl, and templateProvider. + templateProvider: ['$stateParams', + function ( $stateParams) { + // This is just to demonstrate that $stateParams injection works for templateProvider. + // $stateParams are the parameters for the new state we're transitioning to, even + // though the global '$stateParams' has not been updated yet. + return '
    Contact ID: ' + $stateParams.contactId + ''; + }] + } + } + }) + + ////////////////////////////// + // Contacts > Detail > Item // + ////////////////////////////// + + .state('contacts.detail.item', { + + // So following what we've learned, this state's full url will end up being + // '/contacts/{contactId}/item/:itemId'. We are using both types of parameters + // in the same url, but they behave identically. + url: '/item/:itemId', + views: { + + // This is targeting the unnamed ui-view within the parent state 'contact.detail' + // We wouldn't have to do it this way if we didn't also want to set the 'hint' view below. + // We could instead just set templateUrl and controller outside of the view obj. + '': { + templateUrl: 'app/contacts/contacts.detail.item.html', + controller: ['$scope', '$stateParams', '$state', 'utils', + function ( $scope, $stateParams, $state, utils) { + $scope.item = utils.findById($scope.contact.items, $stateParams.itemId); + + $scope.edit = function () { + // Here we show off go's ability to navigate to a relative state. Using '^' to go upwards + // and '.' to go down, you can navigate to any relative state (ancestor or descendant). + // Here we are going down to the child state 'edit' (full name of 'contacts.detail.item.edit') + $state.go('.edit', $stateParams); + }; + }] + }, + + // Here we see we are overriding the template that was set by 'contact.detail' + 'hint@': { + template: ' This is contacts.detail.item overriding the "hint" ui-view' + } + } + }) + + ///////////////////////////////////// + // Contacts > Detail > Item > Edit // + ///////////////////////////////////// + + // Notice that this state has no 'url'. States do not require a url. You can use them + // simply to organize your application into "places" where each "place" can configure + // only what it needs. The only way to get to this state is via $state.go (or transitionTo) + .state('contacts.detail.item.edit', { + views: { + + // This is targeting the unnamed view within the 'contact.detail' state + // essentially swapping out the template that 'contact.detail.item' had + // had inserted with this state's template. + '@contacts.detail': { + templateUrl: 'app/contacts/contacts.detail.item.edit.html', + controller: ['$scope', '$stateParams', '$state', 'utils', + function ( $scope, $stateParams, $state, utils) { + $scope.item = utils.findById($scope.contact.items, $stateParams.itemId); + $scope.done = function () { + // Go back up. '^' means up one. '^.^' would be up twice, to the grandparent. + $state.go('^', $stateParams); + }; + }] + } + } + }); + } + ] +); \ No newline at end of file diff --git a/sample/contacts.list.html b/sample/app/contacts/contacts.list.html similarity index 100% rename from sample/contacts.list.html rename to sample/app/contacts/contacts.list.html diff --git a/sample/contacts.json b/sample/assets/contacts.json similarity index 100% rename from sample/contacts.json rename to sample/assets/contacts.json diff --git a/sample/common/utils/utils-service.js b/sample/common/utils/utils-service.js new file mode 100644 index 000000000..9d1fdb5ff --- /dev/null +++ b/sample/common/utils/utils-service.js @@ -0,0 +1,24 @@ +angular.module('uiRouterSample.utils.service', [ + +]) + +.factory('utils', function () { + return { + // Util for finding an object by its 'id' property among an array + findById: function findById(a, id) { + for (var i = 0; i < a.length; i++) { + if (a[i].id == id) return a[i]; + } + return null; + }, + + // Util for returning a randomKey from a collection that also isn't the current key + newRandomKey: function newRandomKey(coll, key, currentKey){ + var randKey; + do { + randKey = coll[Math.floor(coll.length * Math.random())][key]; + } while (randKey == currentKey); + return randKey; + } + }; +}); \ No newline at end of file diff --git a/sample/styles.css b/sample/css/styles.css similarity index 100% rename from sample/styles.css rename to sample/css/styles.css diff --git a/sample/factory.js b/sample/factory.js deleted file mode 100644 index 02083584c..000000000 --- a/sample/factory.js +++ /dev/null @@ -1,45 +0,0 @@ -angular.module('uiRouterSample') - - // A RESTful factory for retreiving contacts from 'contacts.json' - .factory('contacts', ['$http', function ($http, utils) { - var path = 'contacts.json'; - var contacts = $http.get(path).then(function (resp) { - return resp.data.contacts; - }); - - var factory = {}; - factory.all = function () { - return contacts; - }; - factory.get = function (id) { - return contacts.then(function(){ - return utils.findById(contacts, id); - }) - }; - return factory; - }]) - - .factory('utils', function () { - - return { - - // Util for finding an object by its 'id' property among an array - findById: function findById(a, id) { - for (var i = 0; i < a.length; i++) { - if (a[i].id == id) return a[i]; - } - return null; - }, - - // Util for returning a randomKey from a collection that also isn't the current key - newRandomKey: function newRandomKey(coll, key, currentKey){ - var randKey; - do { - randKey = coll[Math.floor(coll.length * Math.random())][key]; - } while (randKey == currentKey); - return randKey; - } - - }; - - }); \ No newline at end of file diff --git a/sample/index.html b/sample/index.html index 12e7a20cf..58de89a15 100644 --- a/sample/index.html +++ b/sample/index.html @@ -5,21 +5,30 @@ - + - + - - - - - - + + + + + + + + + ui-router diff --git a/sample/module.js b/sample/module.js deleted file mode 100644 index 2c28748d6..000000000 --- a/sample/module.js +++ /dev/null @@ -1,13 +0,0 @@ -// Make sure to include the `ui.router` module as a dependency -angular.module('uiRouterSample', ['ui.router', 'ngAnimate']) - .run( - [ '$rootScope', '$state', '$stateParams', - function ($rootScope, $state, $stateParams) { - - // It's very handy to add references to $state and $stateParams to the $rootScope - // so that you can access them from any scope within your applications.For example, - //
  • will set the
  • // to active whenever - // 'contacts.list' or one of its decendents is active. - $rootScope.$state = $state; - $rootScope.$stateParams = $stateParams; - }]); diff --git a/sample/states.js b/sample/states.js deleted file mode 100644 index c7a669bc4..000000000 --- a/sample/states.js +++ /dev/null @@ -1,270 +0,0 @@ -// Make sure to include the `ui.router` module as a dependency. -angular.module('uiRouterSample') - .config( - [ '$stateProvider', '$urlRouterProvider', - function ($stateProvider, $urlRouterProvider) { - - ///////////////////////////// - // Redirects and Otherwise // - ///////////////////////////// - - // Use $urlRouterProvider to configure any redirects (when) and invalid urls (otherwise). - $urlRouterProvider - - // The `when` method says if the url is ever the 1st param, then redirect to the 2nd param - // Here we are just setting up some convenience urls. - .when('/c?id', '/contacts/:id') - .when('/user/:id', '/contacts/:id') - - // If the url is ever invalid, e.g. '/asdf', then redirect to '/' aka the home state - .otherwise('/'); - - - ////////////////////////// - // State Configurations // - ////////////////////////// - - // Use $stateProvider to configure your states. - $stateProvider - - ////////// - // Home // - ////////// - - .state("home", { - - // Use a url of "/" to set a states as the "index". - url: "/", - - // Example of an inline template string. By default, templates - // will populate the ui-view within the parent state's template. - // For top level states, like this one, the parent template is - // the index.html file. So this template will be inserted into the - // ui-view within index.html. - template: '

    Welcome to the UI-Router Demo

    ' + - '

    Use the menu above to navigate. ' + - 'Pay attention to the $state and $stateParams values below.

    ' + - '

    Click these links—Alice or ' + - 'Bob—to see a url redirect in action.

    ' - - }) - - ////////////// - // Contacts // - ////////////// - - .state('contacts', { - - // With abstract set to true, that means this state can not be explicitly activated. - // It can only be implicitly activated by activating one of it's children. - abstract: true, - - // This abstract state will prepend '/contacts' onto the urls of all its children. - url: '/contacts', - - // Example of loading a template from a file. This is also a top level state, - // so this template file will be loaded and then inserted into the ui-view - // within index.html. - templateUrl: 'contacts.html', - - // Use `resolve` to resolve any asynchronous controller dependencies - // *before* the controller is instantiated. In this case, since contacts - // returns a promise, the controller will wait until contacts.all() is - // resolved before instantiation. Non-promise return values are considered - // to be resolved immediately. - resolve: { - contacts: ['contacts', - function( contacts){ - return contacts.all(); - }] - }, - - // You can pair a controller to your template. There *must* be a template to pair with. - controller: ['$scope', '$state', 'contacts', 'utils', - function ( $scope, $state, contacts, utils) { - - // Add a 'contacts' field in this abstract parent's scope, so that all - // child state views can access it in their scopes. Please note: scope - // inheritance is not due to nesting of states, but rather choosing to - // nest the templates of those states. It's normal scope inheritance. - $scope.contacts = contacts; - - $scope.goToRandom = function () { - var randId = utils.newRandomKey($scope.contacts, "id", $state.params.contactId); - - // $state.go() can be used as a high level convenience method - // for activating a state programmatically. - $state.go('contacts.detail', { contactId: randId }); - }; - }] - }) - - ///////////////////// - // Contacts > List // - ///////////////////// - - // Using a '.' within a state name declares a child within a parent. - // So you have a new state 'list' within the parent 'contacts' state. - .state('contacts.list', { - - // Using an empty url means that this child state will become active - // when its parent's url is navigated to. Urls of child states are - // automatically appended to the urls of their parent. So this state's - // url is '/contacts' (because '/contacts' + ''). - url: '', - - // IMPORTANT: Now we have a state that is not a top level state. Its - // template will be inserted into the ui-view within this state's - // parent's template; so the ui-view within contacts.html. This is the - // most important thing to remember about templates. - templateUrl: 'contacts.list.html' - }) - - /////////////////////// - // Contacts > Detail // - /////////////////////// - - // You can have unlimited children within a state. Here is a second child - // state within the 'contacts' parent state. - .state('contacts.detail', { - - // Urls can have parameters. They can be specified like :param or {param}. - // If {} is used, then you can also specify a regex pattern that the param - // must match. The regex is written after a colon (:). Note: Don't use capture - // groups in your regex patterns, because the whole regex is wrapped again - // behind the scenes. Our pattern below will only match numbers with a length - // between 1 and 4. - - // Since this state is also a child of 'contacts' its url is appended as well. - // So its url will end up being '/contacts/{contactId:[0-9]{1,8}}'. When the - // url becomes something like '/contacts/42' then this state becomes active - // and the $stateParams object becomes { contactId: 42 }. - url: '/{contactId:[0-9]{1,4}}', - - // If there is more than a single ui-view in the parent template, or you would - // like to target a ui-view from even higher up the state tree, you can use the - // views object to configure multiple views. Each view can get its own template, - // controller, and resolve data. - - // View names can be relative or absolute. Relative view names do not use an '@' - // symbol. They always refer to views within this state's parent template. - // Absolute view names use a '@' symbol to distinguish the view and the state. - // So 'foo@bar' means the ui-view named 'foo' within the 'bar' state's template. - views: { - - // So this one is targeting the unnamed view within the parent state's template. - '': { - templateUrl: 'contacts.detail.html', - controller: ['$scope', '$stateParams', 'utils', - function ( $scope, $stateParams, utils) { - $scope.contact = utils.findById($scope.contacts, $stateParams.contactId); - }] - }, - - // This one is targeting the ui-view="hint" within the unnamed root, aka index.html. - // This shows off how you could populate *any* view within *any* ancestor state. - 'hint@': { - template: 'This is contacts.detail populating the "hint" ui-view' - }, - - // This one is targeting the ui-view="menu" within the parent state's template. - 'menuTip': { - // templateProvider is the final method for supplying a template. - // There is: template, templateUrl, and templateProvider. - templateProvider: ['$stateParams', - function ( $stateParams) { - // This is just to demonstrate that $stateParams injection works for templateProvider. - // $stateParams are the parameters for the new state we're transitioning to, even - // though the global '$stateParams' has not been updated yet. - return '
    Contact ID: ' + $stateParams.contactId + ''; - }] - } - } - }) - - ////////////////////////////// - // Contacts > Detail > Item // - ////////////////////////////// - - .state('contacts.detail.item', { - - // So following what we've learned, this state's full url will end up being - // '/contacts/{contactId}/item/:itemId'. We are using both types of parameters - // in the same url, but they behave identically. - url: '/item/:itemId', - views: { - - // This is targeting the unnamed ui-view within the parent state 'contact.detail' - // We wouldn't have to do it this way if we didn't also want to set the 'hint' view below. - // We could instead just set templateUrl and controller outside of the view obj. - '': { - templateUrl: 'contacts.detail.item.html', - controller: ['$scope', '$stateParams', '$state', 'utils', - function ( $scope, $stateParams, $state, utils) { - $scope.item = utils.findById($scope.contact.items, $stateParams.itemId); - - $scope.edit = function () { - // Here we show off go's ability to navigate to a relative state. Using '^' to go upwards - // and '.' to go down, you can navigate to any relative state (ancestor or descendant). - // Here we are going down to the child state 'edit' (full name of 'contacts.detail.item.edit') - $state.go('.edit', $stateParams); - }; - }] - }, - - // Here we see we are overriding the template that was set by 'contact.detail' - 'hint@': { - template: ' This is contacts.detail.item overriding the "hint" ui-view' - } - } - }) - - ///////////////////////////////////// - // Contacts > Detail > Item > Edit // - ///////////////////////////////////// - - // Notice that this state has no 'url'. States do not require a url. You can use them - // simply to organize your application into "places" where each "place" can configure - // only what it needs. The only way to get to this state is via $state.go (or transitionTo) - .state('contacts.detail.item.edit', { - views: { - - // This is targeting the unnamed view within the 'contact.detail' state - // essentially swapping out the template that 'contact.detail.item' had - // had inserted with this state's template. - '@contacts.detail': { - templateUrl: 'contacts.detail.item.edit.html', - controller: ['$scope', '$stateParams', '$state', 'utils', - function ( $scope, $stateParams, $state, utils) { - $scope.item = utils.findById($scope.contact.items, $stateParams.itemId); - $scope.done = function () { - // Go back up. '^' means up one. '^.^' would be up twice, to the grandparent. - $state.go('^', $stateParams); - }; - }] - } - } - }) - - /////////// - // About // - /////////// - - .state('about', { - url: '/about', - - // Showing off how you could return a promise from templateProvider - templateProvider: ['$timeout', - function ( $timeout) { - return $timeout(function () { - return '

    UI-Router Resources

    '; - }, 100); - }] - }) - }]); diff --git a/sample/bootstrap.min.css b/sample/vendor/bootstrap.min.css similarity index 100% rename from sample/bootstrap.min.css rename to sample/vendor/bootstrap.min.css