<!DOCTYPE html>
<html ng-app="app">
<head>
    <title>ngAnimate (1.4.8) transition example with UI-Router</title>

    <link href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css" rel="stylesheet" />
    <link href="app-content/app.less" rel="stylesheet/less" type="text/css" />
</head>
<body>
    <!-- header -->
    <header>
        <ul class="nav nav-tabs">
            <li ng-class="{active: currentState.match('^overview')}"><a ui-sref="overview">Overview</a></li>
            <li ng-class="{active: currentState.match('^product')}"><a ui-sref="products">Products</a></li>
        </ul>
    </header>

    <!-- main -->
    <main ui-view></main>

    <!-- footer -->
    <footer>
        <p>
            <a href="http://jasonwatmore.com/post/2016/01/20/Angular-ngAnimate-Tutorial-with-UI-Router.aspx">Angular ngAnimate Tutorial with UI Router</a>
        </p>
        <p>
            <a href="http://jasonwatmore.com">JasonWatmore.com</a>
        </p>
    </footer>

    <!-- less.js -->
    <script src="//cdnjs.cloudflare.com/ajax/libs/less.js/2.5.3/less.min.js"></script>

    <!-- angular -->
    <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
    <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular-animate.min.js"></script>

    <!-- ui router -->
    <script src="//cdnjs.cloudflare.com/ajax/libs/angular-ui-router/0.2.15/angular-ui-router.min.js"></script>

    <!-- application scripts -->
    <script src="app.js"></script>
    <script src="app-services/product.service.js"></script>
    <script src="overview/main.controller.js"></script>
    <script src="products/main.controller.js"></script>
    <script src="products/add-edit.controller.js"></script>
</body>
</html>
/* GENERAL STYLES AND CLASSES 
--------------------------------------------------------------------- */
body {
  padding: 5px;
}

/* HEADER STYLES
--------------------------------------------------------------------- */
header {
}

/* MAIN STYLES
--------------------------------------------------------------------- */
main {
    padding: 0 20px;
    min-height: 300px;

    .products-table {
        margin-top: 20px;
        
        .delete-column {
            width: 60px;
        }
    }


    /* TRANSITION ANIMATIONS FOR MAIN VIEW 
    ------------------------------------------*/

    /* start 'enter' transition */
    &.ng-enter {
        /* transition on enter for .5s */
        transition: .5s;

        /* start with opacity 0 (invisible) */
        opacity: 0;
    }

    /* end 'enter' transition */
    &.ng-enter-active {
        /* end with opacity 1 (fade in) */
        opacity: 1;
    }


    /* side form */
    .view-side-form {
        .side-form {
            position: fixed;
            top: 0;
            left: 0;
            right: 0;
            bottom: 0;

            .content {
                position: absolute;
                z-index: 100;
                top: 0;
                right: 0;
                width: 80%;
                height: 100%;
                overflow: auto;
                background: #fff;
                padding: 20px;
                border-left: 1px solid #e0e0e0;
            }

            .background {
                background: #000;
                opacity: .8;
                width: 100%;
                height: 100%;
            }
        }


        /* TRANSITION ANIMATIONS FOR SIDE FORM VIEW
        ------------------------------------------*/

        /* animations for side form view */
        &.ng-enter, 
        &.ng-leave {
            /* transition on enter and leave for .5s */
            transition: .5s;
        }

        /* start 'enter' transition */
        &.ng-enter {
            .side-form {
                .content {
                    /* start with content 80% off the right of the page */
                    right: -80%;
                }

                .background {
                    /* start with background opacity 0 (invisible) */
                    opacity: 0;
                }
            }
        }

        /* end 'enter' transition */
        &.ng-enter-active {
            .side-form {
                .content {
                    /* transition the right position which 
                       slides the content into view */
                    transition: right .5s;

                    /* end with content aligned to the right of the page */
                    right: 0;
                }

                .background {
                    /* transition the background opacity to fades it in */
                    transition: opacity .5s;

                    /* end with background opacity 0.8 to give the overlay effect */
                    opacity: .8;
                }
            }
        }

        /* end 'leave' transition */
        &.ng-leave-active {
            .side-form {
                .content {
                    /* transition the right position which 
                       slides the content out of view */
                    transition: right .5s;
                    
                    /* end transition with the content completely off the page */
                    right: -100%;
                }

                .background {
                    /* transition the background opacity to fades it out */
                    transition: opacity .5s;

                    /* end with background opacity 0 to hide it */
                    opacity: 0;
                }
            }
        }
    }
}

/* FOOTER STYLES
--------------------------------------------------------------------- */
footer {
    text-align: center;
    margin-top: 20px;
    padding: 20px;
    border-top: 1px solid #ddd;
}
(function () {
    'use strict';

    angular
        .module('app', ['ui.router', 'ngAnimate'])
        .config(config)
        .run(run);

    function config($stateProvider, $urlRouterProvider) {
        // default route
        $urlRouterProvider.otherwise("/");

        $stateProvider
            .state('overview', {
                url: '/',
                templateUrl: 'overview/main.html',
                controller: 'Overview.MainController',
                controllerAs: 'vm'
            })
            .state('products', {
                url: '/products',
                templateUrl: 'products/main.html',
                controller: 'Products.MainController',
                controllerAs: 'vm'
            })
                .state('products.add', {
                    url: '/add',
                    templateUrl: 'products/add-edit.html',
                    controller: 'Products.AddEditController',
                    controllerAs: 'vm'
                })
                .state('products.edit', {
                    url: '/edit/:id',
                    templateUrl: 'products/add-edit.html',
                    controller: 'Products.AddEditController',
                    controllerAs: 'vm'
                });
    }

    function run($rootScope, ProductService) {
        // add some initial products
        if(ProductService.GetAll().length === 0) {
            ProductService.Save({ name: 'Boardies', price: '25.00' });
            ProductService.Save({ name: 'Singlet', price: '9.50' });
            ProductService.Save({ name: 'Thongs (Flip Flops)', price: '12.95' });
        }

        // track current state for active tab
        $rootScope.$on('$stateChangeSuccess', function (event, toState, toParams, fromState, fromParams) {
            $rootScope.currentState = toState.name;
        });
    }

})();
<h1>Overview</h1>
<p>ngAnimate (1.4.8) transition example with UI-Router</p>
<h1>Products</h1>

<a ui-sref="products.add" class="btn btn-default">Add Product</a>

<table class="table products-table">
    <tr>
        <th>Name</th>
        <th>Price</th>
        <th class="delete-column"></th>
    </tr>
    <tr ng-repeat="product in vm.products">
        <td><a ui-sref="products.edit({ id: product.id })">{{product.name}}</a></td>
        <td>${{product.price}}</td>
        <td><a ng-click="vm.deleteProduct(product.id)" class="btn btn-xs btn-danger">Delete</a></td>
    </tr>
</table>

<div ui-view class="view-side-form"></div>
(function () {
    'use strict';

    angular
        .module('app')
        .controller('Overview.MainController', Controller);

    function Controller() {
        var vm = this;
    }

})();
(function () {
    'use strict';

    angular
        .module('app')
        .controller('Products.MainController', Controller);

    function Controller($scope, ProductService) {
        var vm = this;

        vm.products = [];
        vm.deleteProduct = deleteProduct;

        initController();

        function initController() {
            loadProducts();

            // reload products when updated
            $scope.$on('products-updated', loadProducts);
        }
        
        function loadProducts() {
            vm.products = ProductService.GetAll();
        }

        function deleteProduct(id) {
            ProductService.Delete(id);
            loadProducts();
        }
    }

})();
<div class="side-form">
    <div class="content">
        <h1>{{vm.title}}</h1>
        <div class="form-container">
            <form method="post">
                <div class="form-group">
                    <label for="name">Name</label>
                    <input type="text" id="name" class="form-control" ng-model="vm.product.name" required />
                </div>
                <div class="form-group">
                    <label for="name">Price</label>
                    <input type="text" id="price" class="form-control" ng-model="vm.product.price" required />
                </div>
                <div class="form-group">
                    <a class="btn btn-default" ui-sref="products">Cancel</a>
                    <button class="btn btn-primary" ng-click="vm.saveProduct()">Save</button>
                </div>
            </form>
        </div>
    </div>
    <div class="background"></div>
</div>
(function () {
    'use strict';

    angular
        .module('app')
        .controller('Products.AddEditController', Controller);

    function Controller($scope, $state, $stateParams, ProductService) {
        var vm = this;

        vm.title = 'Add Product';
        vm.product = {};
        vm.saveProduct = saveProduct;

        initController();

        function initController() {
            if ($stateParams.id) {
                vm.title = 'Edit Product';
                vm.product = ProductService.GetById($stateParams.id);
            }
        }

        function saveProduct() {
          console.log('vm.product', vm.product);
            // save product
            ProductService.Save(vm.product);

            // redirect to users view
            $state.go('products');

            // emit event so list controller can refresh
            $scope.$emit('products-updated');
        }
    }

})();
(function () {
    'use strict';

    angular
        .module('app')
        .factory('ProductService', Service);

    function Service($filter) {

        var service = {};

        service.GetAll = GetAll;
        service.GetById = GetById;
        service.Save = Save;
        service.Delete = Delete;

        return service;

        function GetAll() {
            return getProducts();
        }

        function GetById(id) {
            var filtered = $filter('filter')(getProducts(), { id: id });
            var product = filtered.length ? filtered[0] : null;
            
            return product;
        }

        function Save(product) {
            var products = getProducts();

            if (product.id) {
                // update existing product
                
                for (var i = 0; i < products.length; i++) {
                    if (products[i].id === product.id) {
                        products[i] = product;
                        break;
                    }
                }
                setProducts(products);
            } else {
                // create new product
                
                // assign id
                var lastProduct = products[products.length - 1] || { id: 0 };
                product.id = lastProduct.id + 1;
    
                // save to local storage
                products.push(product);
                setProducts(products);
            }

            return;
        }

        function Delete(id) {
            var products = getProducts();
            for (var i = 0; i < products.length; i++) {
                var product = products[i];
                if (product.id === id) {
                    products.splice(i, 1);
                    break;
                }
            }
            setProducts(products);
            
            return;
        }

        // private functions

        function getProducts() {
            if (!localStorage.products) {
                localStorage.products = JSON.stringify([]);
            }

            return JSON.parse(localStorage.products);
        }

        function setProducts(products) {
            localStorage.products = JSON.stringify(products);
        }
    }
})();