<!DOCTYPE html>
<html>
<head>
<link data-require="bootstrap-css@3.x" data-semver="3.1.1" rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css" />
<script data-require="jquery@*" data-semver="2.1.1" src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script data-require="angular.js@*" data-semver="1.3.0-beta.5" src="https://code.angularjs.org/1.3.0-beta.5/angular.js"></script>
<script data-require="angular-animate@*" data-semver="1.2.16" src="http://code.angularjs.org/1.2.16/angular-animate.js"></script>
<script data-require="angular-route@*" data-semver="1.2.16" src="http://code.angularjs.org/1.2.16/angular-route.js"></script>
<script data-require="underscore.js@*" data-semver="1.6.0" src="//cdnjs.cloudflare.com/ajax/libs/underscore.js/1.6.0/underscore-min.js"></script>
<link rel="stylesheet" href="style.css" />
<link rel="stylesheet" href="animations.css" />
<script src="script.js"></script>
<script src="animations.js"></script>
</head>
<body ng-app="coursesApp">
<div class="container">
<div class="row page-header">
<h2>Virtual Training</h2>
</div>
<div data-ng-view="" id="div-view" class="view-slide-in"></div>
</div>
<script type="text/ng-template" id="courseDetails.html">
<div class="thumbnail">
<div class="caption"><strong>{{course.title}}</strong></div>
<div ng-show="viewDetails">
<div style="text-align:center;">{{course.topic}}</div>
<span style="margin-right: 9px;">Level: {{course.level}}</span>
<span class="right">Category: {{course.category}}</span>
<br />
<span class="left" style="margin-right: 9px;">Rated: {{course.rating}}/5</span>
</div>
<div ng-hide="viewDetails">
Total Views: {{course.totalViews}} <br />
Last Week Views: {{course.lastWeekViews}} <br />
</div>
<div class="left"><a href="#/course/{{course.id}}">View Course</a></div>
<div class="right">
<button class="right btn-link" ng-show="!viewDetails">Details</button>
<button class="right btn-link" ng-show="viewDetails">Statistics</button>
</div>
</div>
<br />
</script>
</body>
</html>
// Code goes here
var app=angular.module('coursesApp',['ngRoute','ngAnimate','courseAnimations']);
app.config(function($routeProvider){
$routeProvider.when('/',{
templateUrl:'home.html',
controller:'HomeCtrl'
})
.when('/course/:id',{
templateUrl:'course.html',
controller:'ViewCourseCtrl'
});
});
app.factory('coursesDataSvc', function(){
var courses = [
{
'id':1,
'title':'Introduction to Angular JS',
'rating':4,
'numberOfRatings':100,
'totalViews':1000,
'lastWeekViews':100,
'category':'JavaScript',
'level':'100',
'topics':[
'What is Angular JS?',
'Basics and Data binding',
'Building blocks',
'Services, Values and Factories',
'AJAX and Promises',
'Routing'
]
},
{
'id':2,
'title':'Advanced Angular JS',
'rating':4.5,
'numberOfRatings':150,
'totalViews':1200,
'lastWeekViews':140,
'category':'JavaScript',
'level':'300',
'topics':[
'Directives',
'Animations',
'Unit Testing',
'End-to-end Testing'
]
},
{
'id':3,
'title':'ASP.NET MVC Fundamentals',
'rating':4,
'numberOfRatings':100,
'totalViews':1000,
'lastWeekViews':100,
'category':'ASP.NET',
'level':'200',
'topics':[
'Web forms vs MVC',
'Why MVC?',
'Model',
'View',
'Controller',
'Going further...'
]
},
{
'id':4,
'title':'ASP.NET in VS 2013',
'rating':4,
'numberOfRatings':100,
'totalViews':1000,
'lastWeekViews':100,
'category':'ASP.NET',
'level':'300',
'topics':[
'Overview',
'OWIN and Katana',
'Updates to Web Forms',
'Updates to MVC and Razor Views',
'Web API 2 and OData',
'Tooling Support'
]
},
{
'id':5,
'title':'Async in C#',
'rating':4.2,
'numberOfRatings':100,
'totalViews':1000,
'lastWeekViews':100,
'category':'C#',
'level':'400',
'topics':[
'Introduction',
'Evolution of async with C# and .NET',
'Task Parallel Library',
'Using async and await',
'Patterns and Best Practices'
]
},
{
'id':6,
'title':'LINQ',
'rating':3.8,
'numberOfRatings':100,
'totalViews':1000,
'lastWeekViews':100,
'category':'C#',
'level':'200',
'topics':[
'C# Language improvements',
'Basic LINQ Queries',
'Queries in Lambda Expression Syntax',
'Deferred and Immediate Execution',
'LINQ in Layered Applications',
'Expressions and LINQ to Remote'
]
},
{
'id':7,
'title':'SQL Server Fundamentals',
'rating':4.5,
'numberOfRatings':100,
'totalViews':1000,
'lastWeekViews':100,
'category':'SQL Server',
'level':'100',
'topics':[
'Overview',
'Creating tables and constraints',
'Basic CRUD Operations',
'Join Queries',
'Apply, Merge and CTE',
'Transactions',
'Query Tuning'
]
},
{
'id':8,
'title':'ASP.NET Web API OData',
'rating':3.3,
'numberOfRatings':100,
'totalViews':1000,
'lastWeekViews':100,
'category':'ASP.NET',
'level':'300',
'topics':[
'REST and OData',
'OData in Web API using ODataController',
'OData using EntitySetController',
'Consuming OData Services from .NET and JavaScript clients'
]
},
{
'id':9,
'title':'Trasactions in SQL Server',
'rating':4.8,
'category':'SQL Server',
'level':'400',
'topics':[
'Intro to Transact-SQL',
'Basic Transactions',
'Transactions in procedures and triggers',
'Error Handling',
'Transaction best practices'
]
},
{
'id':10,
'title':'Intro to Node.js',
'rating':3,
'numberOfRatings':100,
'totalViews':1000,
'lastWeekViews':100,
'category':'JavaScript',
'level':'200',
'topics':[
'What is Node.js?',
'Asynchronous actions and Event loop',
'Accessing file system',
'Accessing Databases',
'Unit testing',
'Deploying Node.js Apps'
]
}
];
function getAllCourses(){
return courses;
}
function getCourse(id){
var filtered = _.filter(courses, function(c){
return c.id === id;
});
return filtered[0];
}
return {
getAllCourses:getAllCourses,
getCourse:getCourse
};
});
app.controller('HomeCtrl', function($scope, coursesDataSvc){
$scope.courses = coursesDataSvc.getAllCourses();
});
app.controller('ViewCourseCtrl',function($scope, $routeParams, coursesDataSvc){
if ($routeParams.id) {
$scope.course = coursesDataSvc.getCourse(parseInt($routeParams.id));
}
else{
$scope.course = coursesDataSvc.getCourse(1);
}
});
app.directive('courseDetails', function($animate){
return {
scope: true,
templateUrl:'courseDetails.html',
link: function(scope, elem, attrs){
scope.viewDetails = true;
elem.find('button').bind('click', function(){
$animate.addClass(elem, "switching", function(){
scope.viewDetails = !scope.viewDetails;
scope.$apply();
elem.removeClass("switching");
});
});
}
};
});
/* Styles go here */
.view-container {
position: relative;
}
.view-list {
margin-top: 1%;
}
.view-list > article h1 {
font-size: 125%;
line-height: 120%;
margin-bottom: 2%;
}
.view-list > article > div > img {
height: 80px;
float: left;
margin-right: 4%;
}
.view-list > article > div > time {
font-family: "Segoe UI", Arial, Helvetica, sans-serif;
font-weight: bold;
float: right;
margin-left: 2%;
text-transform: uppercase;
}
.view-list > article > div > address {
font-family: "Segoe UI", Arial, Helvetica, sans-serif;
font-size: 95%;
font-weight: bold;
margin: 0;
text-transform: uppercase;
}
.view-list > .article-container-full-width img {
float: none;
display: block;
margin: auto;
}
.view-list > .article-container-full-width address {
text-align: center;
}
.article-left-content {
border-bottom: solid 1px rgba(0, 0, 0, 0.08);
box-shadow: 0 1px rgba(255,255,255,.8);
min-height: 100px;
padding: 20px;
}
.article-container-full-width {
margin: 6px;
float: left;
width: 110px;
height: 140px;
border-bottom: solid 0 transparent;
padding: 1.5% .5%;
}
small.right {
clear: right;
float: right;
}
#ng-view {
position: relative;
}
span[data-ng-show] {
color: #FA787E;
}
.thumbnail {
padding-bottom: 20px;
margin: 10px;
background-color: #DBDBDB;
height: 150px;
}
.thumbnail .caption {
text-align: center;
padding-top: 20px;
font-family: 'Comic Sans MS';
}
.voting-button {
cursor: pointer;
border: none;
background: none;
}
.right {
clear: right;
float: right;
}
<div class="row">
Filter Courses: <input type="text" ng-model="courseFilter" />
Sort By: <select ng-model="sortExpression">
<option value="level">Level</option>
<option value="topic">Topic</option>
<option value="category">Category</option>
<option value="rating">Rating</option>
</select>
<div class="right"><input type="checkbox" ng-model="showFirstCourses" /> Show First 3 Courses</div>
</div>
<br />
<br />
<div class="row">
<div data-ng-repeat="course in courses | filter: courseFilter | orderBy: sortExpression"
ng-hide="showFirstCourses && $index > 2" class="repeat-animation hide-animation col-md-3">
<div course-details class="det-anim" style="margin-bottom: 2px;" title="{{course.title}}"></div>
</div>
</div>
<div class="row">
<h2>{{course.title}}</h2>
<br />
<br />
<div ng-repeat="topic in course.topics">
<a href="#" onclick="return false;"><i class="glyphicon glyphicon-play"></i></a> <span>{{topic}}</span>
</div>
<div class="pull-right">
<a href="#/">Back to Course List</a>
</div>
</div>
.det-anim.switching{
transition: all 1s linear;
-webkit-transition:all 0.5s linear;
-moz-transition: all 0.5s linear;
-o-transition: all 0.5s linear;
position: relative;
opacity:0.5;
left:-20px;
}
var courseAppAnimations = angular.module('courseAnimations',['ngAnimate']);
courseAppAnimations.animation('.view-slide-in', function(){
return{
enter: function(element, done){
element.css({
opacity:0.5,
position:"relative",
top:"10px",
left:"20px"
})
.animate({top:0,
left:0,
opacity: 1}, 1000,done);
}
};
});
courseAppAnimations.animation('.repeat-animation', function() {
return {
enter : function(element, done) {
console.log("entering...");
var width = element.width();
element.css({
position:'relative',
left:-10,
opacity:0
});
element.animate({
left:0,
opacity:1
}, done);
},
leave : function(element, done) {
element.css({
position:'relative',
left:0,
opacity:1
});
element.animate({
left:-10,
opacity:0
}, done);
},
move : function(element, done) {
element.css({
left:"2px",
opacity:0.5
});
element.animate({
left:"0px",
opacity:1
}, done);
}
};
});
courseAppAnimations.animation('.hide-animation', function() {
return {
beforeAddClass : function(element, className, done) {
if(className == 'ng-hide') {
element.animate({
opacity:0
},500, done);
}
else {
done();
}
},
removeClass : function(element, className, done) {
if(className == 'ng-hide') {
element.css('opacity',0);
element.animate({
opacity:1
},500, done);
}
else {
done();
}
}
};
});
courseAppAnimations.animation('.js-anim', function(){
return{
beforeAddClass: function(element, className, done){
if(className == 'switching') {
element.animate({
opacity:0
},1000, function(){
element.css({opacity:1});
done();
});
}
else {
done();
}
}
}
});