WarsawJS

We talk about JavaScript. Each month in Warsaw, Poland.

Large Application Routing
using AngularJS UI-Router

Piotr Błaszczak @MyFlowPL
myflowpl.github.io/talk-ui-router

Piotr Błaszczak

JavaScript & PHP Developer at Nowoczesna Firma S.A.

7 years of experience in web development

Agenda

  1. SPA, routing, AngularJS, UI-Router
  2. Setup & configuration
  3. States inheritance
  4. $state, ui-view, ui-sref, ui-sref-active
  5. Resolve & data
  6. Named views
  7. Events
  8. 404 not found, Authentication, Authorization
  9. Animations

Single Page Application

Routing

AngularJS

AngularJS

UI-Router

Setup

Configuration

State definition

$stateProvider .state('welcome', { url: "/", templateUrl: "views/welcome.html" }) .state('contact', { url: "/contact", templateUrl: "views/contact.html", controller: 'ContactController' }) Demo

Inheritance

index.html (has root ui-view)
views/products.html (has ui-view)
views/product.html

Inheritance - state definition

$stateProvider .state('products', { url: "/products", templateUrl: "views/products.html", controller: 'products' }) .state('products.product', { url: "/product/:id", templateUrl: "views/product.html", controller: 'product' })

Inheritance - template

views/products.html

<ul class="left"> <li ng-repeat="product in products"> <a ui-sref="products.product({id: product.id})"> {{product.name}} </a> </li> </ul> <ui-view class="center"></ui-view> Demo

$state service

$state.go('contact.detail') $state.go('^') //will go to a parent state. $state.go('^.sibling') //will go to a sibling state. $state.go('.child.grandchild') //will go to a grandchild state.

with state params

// assume state url is: /contact/detail/:id $state.go('contact.detail', {id:44}) // wil redirect to: /contact/detail/44

ui-sref directive

ui-sref='stateName' - Navigate to state, no params. 'stateName' can be any valid absolute or relative state, following the same syntax rules as $state.go()

ui-sref='stateName({param: value, param: value})' - Navigate to state, with params.

ui-sref-active directive

ui-sref-active='class1 class2' - classes "class1" and "class2" are each added to the directive element when the related ui-sref's state is active, and removed when it is inactive.

<ul> <li ui-sref-active="active" class="item"> <a href ui-sref="app.user({user: name})">{{name}}</a> </li> <!-- ... --> </ul>

$stateParams service

// If you had a url on your state of: url: '/users/:id/details/{type}/{repeat:[0-9]+}?from&to' // Then you navigated your browser to: '/users/123/details//0' // Your $stateParams object would be { id:'123', type:'', repeat:'0' } // Then you navigated your browser to: '/users/123/details/default/0?from=there&to=here' // Your $stateParams object would be { id:'123', type:'default', repeat:'0', from:'there', to:'here' }

Named Views


Demo

Named views Configuration

// home page .state('welcome', { url: "/", templateUrl: "views/welcome.html" }) // main application layout .state('app', { abstract: true, views: { "": { templateUrl: "views/layout.html" }, "navbar@app": { templateUrl: "views/navbar.html" } } }) // products page layout .state('products', { url: "/products", parent: 'app', views: { "": { templateUrl: "views/products.html" }, "content@products": { template: "Default product list page, ready for your customization" }, "menu@products": { templateUrl: "views/menu.html", controller: 'ProductsController' } }, resolve: { products: function(){ return [ {id:1, name: 'AngularJS', features:['routing', 'services', 'directives', 'data binding']}, {id:2, name: 'ReactJS', features:['faster', 'routing', 'data binding']}, {id:3, name: 'EmberJS', features:['fast', 'routing', 'data binding']}, ] } } }) // product features list .state('products.product', { url: "/product/:id", views: { "content@products": { templateUrl: "views/feature-list.html", controller: 'FeatureListController' } } }) // single feature view .state('products.product.feature', { url: "/feature/:index", views: { "content@products": { templateUrl: "views/feature.html", controller: 'FeatureController' } } }) // feature edit in parent template .state('products.product.feature-edit', { url: "/feature-edit/:index", views: { "navbar@app": { template: "<b>Edit Tools hoses here</b>" }, "@app": { templateUrl: "views/feature-edit.html", controller: 'FeatureEditController' } } }) // feature edit in modal .state('products.product.feature-view', { url: "/feature-view/:index", views: { "modal@": { templateUrl: "views/modal-layout.html" }, "@products.product.feature-view": { templateUrl: "views/feature-edit.html", controller: 'FeatureEditController' } } }) // contact page rendeed in app layout .state('contact', { url: "/contact", parent: 'app', views: { "": { templateUrl: "views/contact.html" } } })

Named views Controllers

function ProductsController($scope, products) { $scope.products = products; }) function FeatureListController($scope, $stateParams, products, $filter) { $scope.id = $stateParams.id; $scope.product = $filter('filter')(products, $stateParams.id)[0]; } function FeatureController($scope, $stateParams, products, $filter) { $scope.id = $stateParams.id; $scope.product = $filter('filter')(products, $stateParams.id)[0]; if($scope.product) { $scope.feature = $scope.product.features[$stateParams.index] } } function FeatureEditController($scope, $stateParams, products, $filter) { $scope.id = $stateParams.id; $scope.product = $filter('filter')(products, $stateParams.id)[0]; if($scope.product) { $scope.index = $stateParams.index; } })

resolve

.state('products', { ... resolve: { user: function($http){ return $http.get('/user/profile') } }, controller: function($scope, user) { $scope.user = user; } })

data

.state('products', { ... data: { acl: ['admin'] } }) $rootScope.$on('$stateChangeStart', function(event, toState){ console.log(toState.data.acl); // Would print "['admin']" when 'parent' is activated })

events

$rootScope.$on('$stateChangeStart', function(event, toState){ console.log(toState.name); }) $stateChangeSuccess //fired once the state transition is complete. $stateChangeStart //fired when the transition begins. $stateNotFound //fired when a state cannot be found by its name. $stateChangeError //fired when an error occurs during transition.

Authentication

$rootScope.$on('$stateChangeStart', function(event, toState){ if(toState.data.acl && !userSrv.has(toState.data.acl)) { event.preventDefault(); $state.go('login'); } })

Animations

You can animate state change on ui-view with ngAnimate module just like any other angular element with animation support

ui-router + ngAnimate + Animate.css

Animations with ui-router

<div ui-view class="view-change-animation"></div>
.view-change-animation.ng-enter {
        -webkit-animation: bounceInLeft 1s;
        animation: bounceInLeft 1s;
}
.view-change-animation.ng-leave {
        -webkit-animation: bounceOutRight 2s;
        animation: bounceOutRight 2s;
}
Demo         Demo 2         Nestet Views Animation

Thank You !!!

Piotr Błaszczak

        pedro.blaszczak@gmail.com
        github.com/MyFlowPL
        @MyFlowPL
        FB/piotr.blaszczak

See you next month at WarsawJS



Questions & Comments

Demo