Commit be58d7ce authored by Leonardo Giovanetti's avatar Leonardo Giovanetti Committed by Arthur Verschaeve

Updates to `AngularJS+RequireJS` app

Replaces `angular-loader` with a better RequireJS setup

Also adds tests (#254)

Closes #1277
Fixes #1276
parent fcb062f4
......@@ -3,9 +3,6 @@ node_modules/*
node_modules/angular/*
!node_modules/angular/angular.js
node_modules/angular-loader/*
!node_modules/angular-loader/angular-loader.js
node_modules/requirejs/*
!node_modules/requirejs/require.js
......
......@@ -6,9 +6,6 @@
<link rel="stylesheet" href="node_modules/todomvc-common/base.css">
<link rel="stylesheet" href="node_modules/todomvc-app-css/index.css">
<style>[ng-cloak] { display: none; }</style>
<!-- Include angular-loader to allow modules to be loaded in any order. -->
<script src="node_modules/angular-loader/angular-loader.js"></script>
</head>
<body>
<section id="todoapp" ng-controller="TodoController">
......
/*global define*/
/*global require*/
'use strict';
define(['angular'], function (angular) {
return angular.module('todomvc', []);
require([
'angular'
], function (angular) {
require([
'controllers/todo',
'directives/todoFocus',
'directives/todoEscape',
'services/todoStorage'
], function (todoCtrl, todoFocusDir, todoEscapeDir, todoStorageSrv) {
angular
.module('todomvc', [todoFocusDir, todoEscapeDir, todoStorageSrv])
.controller('TodoController', todoCtrl);
angular.bootstrap(document, ['todomvc']);
});
});
......@@ -7,9 +7,11 @@
* - exposes the model to the template and provides event handlers
*/
define(['app', 'services/todoStorage'], function (app) {
return app.controller('TodoController', ['$scope', '$location', 'todoStorage', 'filterFilter',
function TodoController($scope, $location, todoStorage, filterFilter) {
define([
'angular'
], function (angular) {
return ['$scope', '$location', 'todoStorage', 'filterFilter',
function ($scope, $location, todoStorage, filterFilter) {
var todos = $scope.todos = todoStorage.get();
$scope.newTodo = '';
......@@ -89,5 +91,5 @@ define(['app', 'services/todoStorage'], function (app) {
});
};
}
]);
];
});
/*global define*/
define(['app'], function (app) {
'use strict';
'use strict';
app.directive('todoEscape', function () {
var ESCAPE_KEY = 27;
/**
* Directive that catches the "Escape" key on the element applied to and evaluates the expression it binds to.
*/
return function (scope, elem, attrs) {
elem.bind('keydown', function (event) {
if (event.keyCode === ESCAPE_KEY) {
scope.$apply(attrs.todoEscape);
}
});
define([
'angular'
], function (angular) {
var moduleName = 'TodoEscapeDirective';
angular
.module(moduleName, [])
.directive('todoEscape', function () {
var ESCAPE_KEY = 27;
scope.$on('$destroy', function () {
elem.unbind('keydown');
});
};
});
return function (scope, elem, attrs) {
elem.bind('keydown', function (event) {
if (event.keyCode === ESCAPE_KEY) {
scope.$apply(attrs.todoEscape);
}
});
scope.$on('$destroy', function () {
elem.unbind('keydown');
});
};
});
return moduleName;
});
/*global define*/
'use strict';
/**
* Directive that places focus on the element it is applied to when the expression it binds to evaluates to true.
*/
define(['app'], function (app) {
app.directive('todoFocus', ['$timeout', function ($timeout) {
return function (scope, elem, attrs) {
scope.$watch(attrs.todoFocus, function (newval) {
if (newval) {
$timeout(function () {
elem[0].focus();
}, 0, false);
}
});
};
}]);
define([
'angular'
], function (angular) {
var moduleName = 'TodoFocusDirective';
angular
.module(moduleName, [])
.directive('todoFocus', ['$timeout', function ($timeout) {
return function (scope, elem, attrs) {
scope.$watch(attrs.todoFocus, function (newval) {
if (newval) {
$timeout(function () {
elem[0].focus();
}, 0, false);
}
});
};
}]);
return moduleName;
});
......@@ -9,9 +9,6 @@ require.config({
angular: {
exports: 'angular'
}
}
});
require(['angular', 'app', 'controllers/todo', 'directives/todoFocus', 'directives/todoEscape'], function (angular) {
angular.bootstrap(document, ['todomvc']);
},
deps: ['app']
});
......@@ -4,18 +4,25 @@
/**
* Services that persists and retrieves TODOs from localStorage.
*/
define(['app'], function (app) {
app.factory('todoStorage', function () {
var STORAGE_ID = 'todos-angularjs-requirejs';
define([
'angular'
], function (angular) {
var moduleName = 'TodoStorageModule';
angular
.module(moduleName, [])
.factory('todoStorage', function () {
var STORAGE_ID = 'todos-angularjs-requirejs';
return {
get: function () {
return JSON.parse(localStorage.getItem(STORAGE_ID) || '[]');
},
return {
get: function () {
return JSON.parse(localStorage.getItem(STORAGE_ID) || '[]');
},
put: function (todos) {
localStorage.setItem(STORAGE_ID, JSON.stringify(todos));
}
};
});
put: function (todos) {
localStorage.setItem(STORAGE_ID, JSON.stringify(todos));
}
};
});
return moduleName;
});
......@@ -2,7 +2,6 @@
"private": true,
"dependencies": {
"angular": "^1.3.13",
"angular-loader": "^1.3.13",
"requirejs": "^2.1.15",
"todomvc-app-css": "^1.0.0",
"todomvc-common": "^1.0.1"
......
......@@ -34,3 +34,10 @@ Get help from other AngularJS users:
* [AngularjS on Google +](https://plus.google.com/+AngularJS/posts)
_If you have other helpful links to share, or find any of the links above no longer work, please [let us know](https://github.com/tastejs/todomvc/issues)._
## Unit Tests
To run the test suite, run these commands:
npm install
npm test
'use strict';
module.exports = function (grunt) {
grunt.loadNpmTasks('grunt-contrib-jasmine');
grunt.initConfig({
jasmine: {
unit: {
options: {
specs: [
'unit/**/*Spec.js'
],
template: require('grunt-template-jasmine-requirejs'),
templateOptions: {
requireConfigFile: '../js/main.js',
requireConfig: {
baseUrl: '../js',
paths: {
jquery: '../test/node_modules/jquery/dist/jquery',
'angular-mocks': '../test/node_modules/angular-mocks/angular-mocks'
},
shim: {
'angular-mocks': ['angular']
},
deps: ['']
}
},
summary: true
}
}
}
});
};
{
"private": true,
"devDependencies": {
"grunt": "^0.4.5",
"grunt-contrib-jasmine": "^0.8.1",
"grunt-template-jasmine-requirejs": "^0.2.0",
"angular-mocks": "^1.3.13",
"jquery": "^2.1.4"
},
"scripts": {
"test": "grunt jasmine"
}
}
define([
'directives/todoEscape',
'jquery',
'angular-mocks'
], function (todoEscapeDir, jQuery) {
'use strict';
beforeEach(module(todoEscapeDir));
var triggerKeyDown = function (element, keyCode) {
var e = jQuery.Event("keydown");
e.keyCode = keyCode;
element.triggerHandler(e);
};
describe('todoEscape directive', function () {
var scope, compile, browser;
beforeEach(inject(function ($rootScope, $compile, $browser) {
scope = $rootScope.$new();
compile = $compile;
browser = $browser;
}));
it('should evaluate the expression binded to the directive', function () {
var someValue = false,
el = angular.element('<input todo-escape="doSomething()">');
scope.doSomething = function () {
someValue = !someValue;
};
compile(el)(scope);
triggerKeyDown(el, 27);
expect(someValue).toBe(true);
});
});
});
define([
'directives/todoFocus',
'angular-mocks'
], function (todoFocusDir) {
'use strict';
beforeEach(module(todoFocusDir));
describe('todoFocus directive', function () {
var scope, compile, browser;
beforeEach(inject(function ($rootScope, $compile, $browser) {
scope = $rootScope.$new();
compile = $compile;
browser = $browser;
}));
it('should focus on truthy expression', function () {
var el = angular.element('<input todo-focus="focus">');
scope.focus = false;
compile(el)(scope);
expect(browser.deferredFns.length).toBe(0);
scope.$apply(function () {
scope.focus = true;
});
expect(browser.deferredFns.length).toBe(1);
});
});
});
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment