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/* ...@@ -3,9 +3,6 @@ node_modules/*
node_modules/angular/* node_modules/angular/*
!node_modules/angular/angular.js !node_modules/angular/angular.js
node_modules/angular-loader/*
!node_modules/angular-loader/angular-loader.js
node_modules/requirejs/* node_modules/requirejs/*
!node_modules/requirejs/require.js !node_modules/requirejs/require.js
......
...@@ -6,9 +6,6 @@ ...@@ -6,9 +6,6 @@
<link rel="stylesheet" href="node_modules/todomvc-common/base.css"> <link rel="stylesheet" href="node_modules/todomvc-common/base.css">
<link rel="stylesheet" href="node_modules/todomvc-app-css/index.css"> <link rel="stylesheet" href="node_modules/todomvc-app-css/index.css">
<style>[ng-cloak] { display: none; }</style> <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> </head>
<body> <body>
<section id="todoapp" ng-controller="TodoController"> <section id="todoapp" ng-controller="TodoController">
......
/*global define*/ /*global require*/
'use strict'; 'use strict';
define(['angular'], function (angular) { require([
return angular.module('todomvc', []); '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 @@ ...@@ -7,9 +7,11 @@
* - exposes the model to the template and provides event handlers * - exposes the model to the template and provides event handlers
*/ */
define(['app', 'services/todoStorage'], function (app) { define([
return app.controller('TodoController', ['$scope', '$location', 'todoStorage', 'filterFilter', 'angular'
function TodoController($scope, $location, todoStorage, filterFilter) { ], function (angular) {
return ['$scope', '$location', 'todoStorage', 'filterFilter',
function ($scope, $location, todoStorage, filterFilter) {
var todos = $scope.todos = todoStorage.get(); var todos = $scope.todos = todoStorage.get();
$scope.newTodo = ''; $scope.newTodo = '';
...@@ -89,5 +91,5 @@ define(['app', 'services/todoStorage'], function (app) { ...@@ -89,5 +91,5 @@ define(['app', 'services/todoStorage'], function (app) {
}); });
}; };
} }
]); ];
}); });
/*global define*/ /*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) { define([
elem.bind('keydown', function (event) { 'angular'
if (event.keyCode === ESCAPE_KEY) { ], function (angular) {
scope.$apply(attrs.todoEscape); var moduleName = 'TodoEscapeDirective';
} angular
}); .module(moduleName, [])
.directive('todoEscape', function () {
var ESCAPE_KEY = 27;
scope.$on('$destroy', function () { return function (scope, elem, attrs) {
elem.unbind('keydown'); 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*/ /*global define*/
'use strict'; 'use strict';
/** /**
* Directive that places focus on the element it is applied to when the expression it binds to evaluates to true. * 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) { define([
return function (scope, elem, attrs) { 'angular'
scope.$watch(attrs.todoFocus, function (newval) { ], function (angular) {
if (newval) { var moduleName = 'TodoFocusDirective';
$timeout(function () { angular
elem[0].focus(); .module(moduleName, [])
}, 0, false); .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({ ...@@ -9,9 +9,6 @@ require.config({
angular: { angular: {
exports: 'angular' exports: 'angular'
} }
} },
}); deps: ['app']
require(['angular', 'app', 'controllers/todo', 'directives/todoFocus', 'directives/todoEscape'], function (angular) {
angular.bootstrap(document, ['todomvc']);
}); });
...@@ -4,18 +4,25 @@ ...@@ -4,18 +4,25 @@
/** /**
* Services that persists and retrieves TODOs from localStorage. * Services that persists and retrieves TODOs from localStorage.
*/ */
define(['app'], function (app) {
app.factory('todoStorage', function () { define([
var STORAGE_ID = 'todos-angularjs-requirejs'; 'angular'
], function (angular) {
var moduleName = 'TodoStorageModule';
angular
.module(moduleName, [])
.factory('todoStorage', function () {
var STORAGE_ID = 'todos-angularjs-requirejs';
return { return {
get: function () { get: function () {
return JSON.parse(localStorage.getItem(STORAGE_ID) || '[]'); return JSON.parse(localStorage.getItem(STORAGE_ID) || '[]');
}, },
put: function (todos) { put: function (todos) {
localStorage.setItem(STORAGE_ID, JSON.stringify(todos)); localStorage.setItem(STORAGE_ID, JSON.stringify(todos));
} }
}; };
}); });
return moduleName;
}); });
...@@ -2,7 +2,6 @@ ...@@ -2,7 +2,6 @@
"private": true, "private": true,
"dependencies": { "dependencies": {
"angular": "^1.3.13", "angular": "^1.3.13",
"angular-loader": "^1.3.13",
"requirejs": "^2.1.15", "requirejs": "^2.1.15",
"todomvc-app-css": "^1.0.0", "todomvc-app-css": "^1.0.0",
"todomvc-common": "^1.0.1" "todomvc-common": "^1.0.1"
......
...@@ -34,3 +34,10 @@ Get help from other AngularJS users: ...@@ -34,3 +34,10 @@ Get help from other AngularJS users:
* [AngularjS on Google +](https://plus.google.com/+AngularJS/posts) * [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)._ _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