angular.module('plunker', ['ui.router'])
.config(['$stateProvider', function ($stateProvider) {
$stateProvider
.state('main', {
abstract: true,
templateUrl: 'views/main.html',
controller: 'MainCtrl'
})
.state('main.home', {
url: '',
})
.state('main.posts', {
url: '/posts',
templateUrl: 'views/posts.html',
controller: 'PostsCtrl',
resolve: {
posts: function(Posts) {
return Posts.query();
}
}
})
.state('main.post', {
url: '/post/:id',
templateUrl: 'views/post-edit.html',
controller: 'PostEditCtrl',
resolve: {
post: function($stateParams, $state, $timeout, Posts) {
var post = new Posts({
id: $stateParams.id
}),
promise = post.load();
promise.catch(function() {
$timeout(function() {
$state.go('main.posts');
}, 1)
});
return promise;
}
}
});
}])
.service('Posts', function($q, $timeout) {
var fixtures = [{
id: 1,
title: 'Lorem ipsum dolor',
content: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit'
}, {
id: 2,
title: 'sit amet, consectetur',
content: 'Phasellus facilisis sem sed massa semper, et interdum mi fermentum'
}, {
id: 3,
title: 'adipiscing elit',
content: 'Curabitur feugiat lacus in aliquet sodales'
}, {
id: 4,
title: '[Error] Cras diam dui',
error: true
} ,{
id: 5,
title: 'tincidunt quis orci',
content: 'Curabitur nec purus eget quam hendrerit aliquam facilisis eget neque'
} ,{
id: 6,
title: '[Error] ac, semper condimentum',
error: true
} ,{
id: 7,
title: 'elit',
content: 'Phasellus convallis dolor et nisl pellentesque vehicula'
}];
function Posts(config) {
angular.extend(this, config);
}
Posts.prototype.load = function() {
var deferred = $q.defer(),
self = this;
$timeout(function() {
angular.extend(self, fixtures[+self.id-1])
if (self.error) {
deferred.reject('No content');
} else {
deferred.resolve(self);
}
}, 2000);
return deferred.promise;
};
Posts.query = function() {
var deferred = $q.defer();
$timeout(function() {
fixtures.forEach(function(p) {
delete p.loading;
});
deferred.resolve(fixtures)
}, 2000);
return deferred.promise;
}
return Posts;
})
.controller('PostsCtrl', function($scope, posts) {
$scope.posts = posts;
$scope.$on('$stateChangeStart', function(event, toState, toParams) {
if ('main.post' === toState.name) {
$scope.posts.some(function(p) {
if (+toParams.id === +p.id) {
p.loading = true;
return true;
}
});
}
});
$scope.$on('$stateChangeSuccess', function(event, toState) {
if ('main.post' === toState.name) {
$scope.posts.forEach(function(p) {
delete p.loading;
});
}
});
$scope.$on('$stateChangeError', function(event, toState) {
if ('main.post' === toState.name) {
$scope.posts.forEach(function(p) {
delete p.loading;
});
}
});
})
.controller('PostEditCtrl', function($scope, post) {
$scope.post = post;
})
.controller('MainCtrl', function($scope, $location, $timeout) {
$scope.location = $location;
$scope.isViewLoading = true;
$scope.$on('$stateChangeStart', function() {
$scope.isViewLoading = true;
});
$scope.$on('$stateChangeSuccess', function() {
$scope.isViewLoading = false;
});
$scope.$on('$stateChangeError', function() {
$scope.isViewLoading = false;
$scope.loadingError = true;
$timeout(
function() {
$scope.loadingError = false;
},
2000
);
});
});
<!DOCTYPE html>
<html ng-app="plunker">
<head>
<meta charset="utf-8" />
<title>AngularJS Plunker</title>
<link data-require="bootstrap-css@3.1.1" data-semver="3.1.1" rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css" />
<script>document.write('<base href="' + document.location + '" />');</script>
<link rel="stylesheet" href="style.css" />
<script data-require="angular.js@1.2.x" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.16/angular.min.js" data-semver="1.2.16"></script>
<script data-require="ui-router@*" data-semver="0.2.8" src="http://angular-ui.github.io/ui-router/release/angular-ui-router.js"></script>
<script data-require="jquery@*" data-semver="2.0.3" src="http://code.jquery.com/jquery-2.0.3.min.js"></script>
<script data-require="bootstrap@*" data-semver="3.1.1" src="//netdna.bootstrapcdn.com/bootstrap/3.1.1/js/bootstrap.min.js"></script>
<script src="app.js"></script>
</head>
<body>
<ui-view></ui-view>
</body>
</html>
.loading-modal {
bottom: 0;
background: transparent;
z-index: 1;
}
.loading-modal,
.alert-error {
position: absolute;
top: 0;
left: 0;
right: 0;
font-weight: bold;
text-align: center;
color: green;
}
.alert-error {
color: red;
}
@-webkit-keyframes spin {
from {
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
}
to {
-webkit-transform: rotate(360deg);
transform: rotate(360deg);
}
}
.spin {
-webkit-animation-name: spin;
-webkit-animation-duration: 1000ms;
-webkit-animation-iteration-count: infinite;
-webkit-animation-timing-function: linear;
}
<p ng-if="loading" class="loading">Wait for it…</p>
<div ng-if="!loading">
<table class="table">
<thead>
<tr>
<th style="width: 3em;"></th>
<th>Titre</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="post in posts">
<td>
<span ng-show="post.loading"
class="spin glyphicon glyphicon-refresh"></span>
</td>
<td>
<a ui-sref="main.post({id: post.id})">
{{post.title}}
</a>
</td>
</tr>
</tbody>
</table>
</div>
<div class="form-group">
<label for="post.title">Titre :</label>
<input ng-model="post.title"
class="form-control"
id="post.title">
</div>
<div class="form-group">
<label for="post.content">Content :</label>
<textarea ng-model="post.content"
class="form-control"
id="post.content"></textarea>
</div>
<nav class="navbar navbar-default" role="navigation">
<a ui-sref="main.posts" class="container">Display posts list</a>
</nav>
<div ng-show="isViewLoading" class="loading-modal">
Wait for it…
</div>
<div ng-show="loadingError" class="alert-error">
Une erreur est survenue.
</div>
<p>Current URL : {{location.$$url||'/'}}</p>
<ui-view></ui-view>