0

Any explanation why the sample code (ui-router/sample/index.html) for angular-ui-router (https://github.com/angular-ui/ui-router) looks like this. Specifically:

  1. Why the nested definitions of objects like controllers?
  2. Why the specification of dependencies like this:

    angular.module('sample', ['ui.compat']) .config( [ '$stateProvider', '$routeProvider', '$urlRouterProvider', function ($stateProvider, $routeProvider, $urlRouterProvider) {

thanks

<!doctype html>
<html lang="en" ng-app="sample"><head>
  <meta charset="utf-8">
  <link rel="stylesheet" type="text/css" href="bootstrap.min.css">
  <style type="text/css">
    .fade-enter-setup, .fade-leave-setup {
      transition: opacity 0.5s cubic-bezier(0.25, 0.46, 0.45, 0.94) 0s;
    }
    .fade-enter-setup,
    .fade-leave-setup.fade-leave-start {
      opacity: 0;
    }
    .fade-leave-setup,
    .fade-enter-setup.fade-enter-start {
      opacity: 1;
    }
  </style>
  <script src="../lib/angular-1.1.4.js"></script>
  <script src="../build/angular-ui-router.js"></script>

  <!-- could easily use a custom property of the state here instead of 'name' -->
  <title ng-bind="$state.current.name + ' - ui-router'">ui-router</title>
</head><body>
<div class="navbar navbar-fixed-top">
  <div class="navbar-inner"><div class="container">
    <a class="brand" href="#">ui-router</a>
    <ul class="nav">
      <li ng-class="{ active: $state.includes('contacts') }"><a href="#/contacts">Contacts</a></li>
      <li ng-class="{ active: $state.includes('about') }"><a href="#/about">About</a></li>
    </ul>
    <p class="navbar-text pull-right" ui-view="hint"></p>
  </div></div>
</div>
<div class="container" style="margin-top:60px" ui-view ng-animate="{enter:'fade-enter'}"></div>
<hr>
<pre>
  $state = {{$state.current.name}}
  $stateParams = {{$stateParams}}
</pre>
</body><script>

function findById(a, id) {
  for (var i=0; i<a.length; i++) {
    if (a[i].id == id) return a[i];
  }
}

angular.module('sample', ['ui.compat'])
  .config(
    [        '$stateProvider', '$routeProvider', '$urlRouterProvider',
    function ($stateProvider,   $routeProvider,   $urlRouterProvider) {
      $urlRouterProvider
        .when('/c?id', '/contacts/:id')
        .otherwise('/');

      $routeProvider
        .when('/user/:id', {
          redirectTo: '/contacts/:id',
        })
        .when('/', {
          template: '<p class="lead">Welcome to the ngStates sample</p><p>Use the menu above to navigate</p>' +
            '<p>Look at <a href="#/c?id=1">Alice</a> or <a href="#/user/42">Bob</a> to see a URL with a redirect in action.</p>',
        });

      $stateProvider
        .state('contacts', {
          url: '/contacts',
          abstract: true,
          templateUrl: 'contacts.html',
          controller:
            [        '$scope', '$state',
            function ($scope,   $state) {
              $scope.contacts = [{
                id: 1,
                name: "Alice",
                items: [{
                  id: 'a',
                  type: 'phone number',
                  value: '555-1234-1234',
                },{
                  id: 'b',
                  type: 'email',
                  value: 'alice@mailinator.com',
                }],
              }, {
                id: 42,
                name: "Bob",
                items: [{
                  id: 'a',
                  type: 'blog',
                  value: 'http://bob.blogger.com',
                },{
                  id: 'b',
                  type: 'fax',
                  value: '555-999-9999',
                }],
              }, {
                id: 123,
                name: "Eve",
                items: [{
                  id: 'a',
                  type: 'full name',
                  value: 'Eve Adamsdottir',
                }],
              }];

              $scope.goToRandom = function () {
                var contacts = $scope.contacts, id;
                do {
                  id = contacts[Math.floor(contacts.length * Math.random())].id;
                } while (id == $state.params.contactId);
                $state.transitionTo('contacts.detail', { contactId: id });
              };
            }],
        })
        .state('contacts.list', {
          // parent: 'contacts',
          url: '',
          templateUrl: 'contacts.list.html',
        })
        .state('contacts.detail', {
          // parent: 'contacts',
          url: '/{contactId}',
          resolve: {
            something:
              [        '$timeout', '$stateParams',
              function ($timeout,   $stateParams) {
                return $timeout(function () { return "Asynchronously resolved data (" + $stateParams.contactId + ")" }, 10);
              }],
          },
          views: {
            '': {
              templateUrl: 'contacts.detail.html',
              controller:
                [        '$scope', '$stateParams', 'something',
                function ($scope,   $stateParams,   something) {
                  $scope.something = something;
                  $scope.contact = findById($scope.contacts, $stateParams.contactId);
                }],
            },
            'hint@': {
              template: 'This is contacts.detail populating the view "hint@"',
            },
            'menu': {
              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 '<hr><small class="muted">Contact ID: ' + $stateParams.contactId + '</small>';
                }],
            },
          },
        })
        .state('contacts.detail.item', {
          // parent: 'contacts.detail',
          url: '/item/:itemId',
          views: {
            '': {
              templateUrl: 'contacts.detail.item.html',
              controller:
                [        '$scope', '$stateParams', '$state',
                function ($scope,   $stateParams,   $state) {
                  $scope.item = findById($scope.contact.items, $stateParams.itemId);
                  $scope.edit = function () {
                    $state.transitionTo('contacts.detail.item.edit', $stateParams);
                  };
                }],
            },
            'hint@': {
              template: 'Overriding the view "hint@"',
            },
          },
        })
        .state('contacts.detail.item.edit', {
          views: {
            '@contacts.detail': {
              templateUrl: 'contacts.detail.item.edit.html',
              controller:
                [        '$scope', '$stateParams', '$state',
                function ($scope,   $stateParams,   $state) {
                  $scope.item = findById($scope.contact.items, $stateParams.itemId);
                  $scope.done = function () {
                    $state.transitionTo('contacts.detail.item', $stateParams);
                  };
                }],
            },
          },
        })
        .state('about', {
          url: '/about',
          templateProvider:
            [        '$timeout',
            function ($timeout) {
              return $timeout(function () { return "Hello world" }, 100);
            }],
        })
        .state('empty', {
          url: '/empty',
          templateUrl: 'empty.html',
          controller:
            [        '$scope', '$state',
            function ($scope,   $state) {
              // Using an object to access it via ng-model from child scope
              $scope.data = {
                initialViewTitle: "I am an initial view"
              }
              $scope.changeInitialViewTitle = function($event) {
                $state.transitionTo('empty.emptycontent');
              };
              $scope.showInitialView = function($event) {
                $state.transitionTo('empty');
              };
          }]
        })
        .state('empty.emptycontent', {
          url: '/content',
          views: {
            'emptycontent': {
              templateUrl: 'empty.content.html'
            }
          }
        });
    }])
    .run(
      [        '$rootScope', '$state', '$stateParams',
      function ($rootScope,   $state,   $stateParams) {
        $rootScope.$state = $state;
        $rootScope.$stateParams = $stateParams;
      }]);
</script></html>
justingordon
  • 12,553
  • 12
  • 72
  • 116

2 Answers2

0

ui-router doesn't fully support this. You may check this library for nested routing: http://angular-route-segment.com

It provides the functionality for creating tree-like routing hierarchy which can be changed without losing the state.

$routeSegmentProvider.

when('/section1',          's1.home').
when('/section1/prefs',    's1.prefs').
when('/section1/:id',      's1.itemInfo.overview').
when('/section1/:id/edit', 's1.itemInfo.edit').
when('/section2',          's2').

segment('s1', {
    templateUrl: 'templates/section1.html',
    controller: MainCtrl}).

within().

    segment('home', {
        templateUrl: 'templates/section1/home.html'}).

    segment('itemInfo', {
        templateUrl: 'templates/section1/item.html',
        controller: Section1ItemCtrl,
        dependencies: ['id']}).

    within().

        segment('overview', {
            templateUrl: 'templates/section1/item/overview.html'}).

        segment('edit', {
             templateUrl: 'templates/section1/item/edit.html'}).

        up().

    segment('prefs', {
        templateUrl: 'templates/section1/prefs.html'}).

    up().

segment('s2', {
    templateUrl: 'templates/section2.html',
    controller: MainCtrl});
artch
  • 4,487
  • 2
  • 28
  • 35
  • 1
    Thanks! I won't have a chance to verify this for a few days. If anybody els confirms this answer, I'll mark as correct. I'm leaning more toward emberjs because of some issues like this. – justingordon Aug 14 '13 at 23:06
  • Also, here is an example of library usage: http://artch.ru/angular-route-segment/example/ – artch Aug 15 '13 at 05:48
  • yet another defective routing with forced templateUrl and tree in js. – puchu Feb 03 '14 at 13:17
  • @artch use routie, luke – puchu Feb 04 '14 at 14:17
  • @puchu what does it have to do with AngularJS? – artch Feb 04 '14 at 17:33
  • @artch why routing should have anything with angular? Do you leave in angular-centered universe? – puchu Feb 04 '14 at 17:37
  • @puchu Well, I would recommend you to learn AngularJS a bit then. AngularJS is not just a library, it is a framework, and yes, it creates its own so called "Realm" which should be used by every part of the application. [See here:](http://docs.angularjs.org/guide/scope) Scopes provide APIs ($apply) to propagate any model changes through the system into the view from outside of the "Angular realm" (controllers, services, Angular event handlers). – artch Feb 05 '14 at 07:13
  • @artch something wrong with you. Angularjs is a defective implementation of MVVM pattern. Routing has nothing to do with this pattern. – puchu Feb 05 '14 at 09:46
  • @puchu this SO question was about routing in AngularJS application. Routing in AngularJS application is impossible without using AngularJS. Your own view on AngularJS itself is off-topic here. No offense, but you better would learn more on question subject prior to answering it. – artch Feb 05 '14 at 14:54
  • @puchu OK. Sorry man, don't have much time to prove the obvious. I don't know what is your experience with AngularJS, but I'm pretty sure that if you would try to write your own medium-sized AngularJS single page app big with complex routing, and you see these things on your own very quickly. – artch Feb 06 '14 at 09:15
  • @puchu Sure man! It's really great you are so strong and cool! However, this is stackoverflow, and the question is about angular. So keep your hate to your club of angular haters plz. It is off-topic here. – artch Feb 06 '14 at 13:38
0

The nesting is only one way of doing the example. you could write

                'navTitle@': {
                templateUrl : 'pages/mypage.html',
                controller: 'myController',
            },

and then just define myController anywhere else you want in your app.

function myController ($scope) {

};

as far as dependencies question...thats one way of injecting in dependancies in angular so you can reuse the code in other places.

    .factory('appLoading', function($rootScope, $state) {
    return {
        loading : function() {
            $rootScope.status = 'loading';
            if(!$rootScope.$$phase) $rootScope.$apply();
        },
        ready : function(delay) {
            function ready() {
        $rootScope.status = 'ready';
        $rootScope.title = $state.current.data.title;
        if(!$rootScope.$$phase) $rootScope.$apply();

    }

}
};
})

then if i wanted to call this loader in a module it would go inside []

like inside the onExit of a state inside ui-router...

    onExit: ['appLoading',
function ( appLoading) {
    appLoading.loading();
}],
Zuriel
  • 1,848
  • 1
  • 19
  • 32