<!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);
}
}
})();