├── .babelrc ├── .editorconfig ├── .gitignore ├── angular2-todo.png ├── gulpfile.babel.js ├── index.html ├── package.json ├── readme.md └── source ├── Task.ts ├── TodoApp.ts ├── bootstrap.ts ├── components ├── NewTodo.ts ├── TodoList.ts └── TodoView.ts ├── less └── styles.less └── services └── TasksService.ts /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015"] 3 | } 4 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | end_of_line = lf 6 | insert_final_newline = true 7 | 8 | [*.{js,es6,jsx,ts,tsx,py,html,css,less,sass,php}] 9 | indent_style = space 10 | indent_size = 4 11 | charset = utf-8 12 | trim_trailing_whitespace = true 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | node_modules/ 3 | *.log 4 | dist/* 5 | -------------------------------------------------------------------------------- /angular2-todo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/artemdemo/angular2-todo/29fb1bf354abc60a50a61144b12c423574496527/angular2-todo.png -------------------------------------------------------------------------------- /gulpfile.babel.js: -------------------------------------------------------------------------------- 1 | import gulp from 'gulp'; 2 | import gutil from 'gulp-util'; 3 | import less from 'gulp-less'; 4 | import browserify from 'browserify'; 5 | import source from 'vinyl-source-stream'; 6 | import tsify from 'tsify'; 7 | 8 | gulp.task('browserify', () => { 9 | return browserify({ 10 | entries: 'source/bootstrap.ts', 11 | debug: true 12 | }) 13 | .plugin(tsify, { 14 | target: 'es5', 15 | experimentalDecorators: true 16 | }) 17 | .bundle() 18 | .on('error', function(err){ 19 | gutil.log(gutil.colors.red.bold('[browserify error]')); 20 | gutil.log(err.message); 21 | this.emit('end'); 22 | }) 23 | .pipe(source('bundle.js')) 24 | .pipe(gulp.dest('./dist')); 25 | }); 26 | 27 | gulp.task('less', function () { 28 | return gulp.src('./source/less/styles.less') 29 | .pipe(less()) 30 | .on('error', function(err) { 31 | // Handle less errors, but do not stop watch task 32 | gutil.log(gutil.colors.red.bold('[Less error]')); 33 | gutil.log(gutil.colors.bgRed('filename:') +' '+ err.filename); 34 | gutil.log(gutil.colors.bgRed('lineNumber:') +' '+ err.lineNumber); 35 | gutil.log(gutil.colors.bgRed('extract:') +' '+ err.extract.join(' ')); 36 | this.emit('end'); 37 | }) 38 | .pipe(gulp.dest('./dist/css')) 39 | }); 40 | 41 | gulp.task('watch', () => { 42 | gulp.watch('./source/**/*.ts', ['browserify']); 43 | gulp.watch('./source/less/*.less', ['less']); 44 | }); 45 | 46 | gulp.task('build', ['browserify', 'less']); 47 | gulp.task('default', ['build', 'watch']); 48 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Angular2 ToDo 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 |
14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular2-todo", 3 | "version": "1.0.0", 4 | "description": "Angular2 ToDo example", 5 | "main": "index.js", 6 | "scripts": { 7 | "build": "gulp build", 8 | "watch": "gulp watch", 9 | "test": "echo \"Error: no test specified\" && exit 1" 10 | }, 11 | "author": "Artem Demo", 12 | "license": "ISC", 13 | "devDependencies": { 14 | "babel-core": "^6.4.5", 15 | "babel-preset-es2015": "^6.3.13", 16 | "browserify": "^13.0.0", 17 | "gulp": "^3.9.0", 18 | "gulp-less": "^3.0.5", 19 | "gulp-util": "^3.0.7", 20 | "tsify": "^0.14.1", 21 | "vinyl-source-stream": "^1.1.0" 22 | }, 23 | "dependencies": { 24 | "angular2": "^2.0.0-beta.13", 25 | "bootstrap": "^3.3.6", 26 | "es6-shim": "^0.35.0", 27 | "reflect-metadata": "^0.1.3", 28 | "rxjs": "^5.0.0-beta.4", 29 | "zone.js": "^0.6.8" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | #Angular 2 - ToDo 2 | 3 | Simple ToDo project. 4 | 5 | 6 | ## Lets go 7 | 8 | ``` 9 | $ npm run build && npm run watch 10 | ``` 11 | 12 | ![alt tag](https://github.com/artemdemo/angular2-todo/blob/master/angular2-todo.png) 13 | 14 | ## Useful links 15 | 16 | * Angular 2 Observable Data Services 17 | https://coryrylan.com/blog/angular-2-observable-data-services 18 | -------------------------------------------------------------------------------- /source/Task.ts: -------------------------------------------------------------------------------- 1 | export class Task { 2 | constructor( 3 | public name: string, 4 | public description: string, 5 | public done: boolean 6 | ) {} 7 | } 8 | -------------------------------------------------------------------------------- /source/TodoApp.ts: -------------------------------------------------------------------------------- 1 | import {Component} from 'angular2/core'; 2 | import {TodoList} from './components/TodoList'; 3 | import {TodoView} from './components/TodoView'; 4 | 5 | @Component({ 6 | selector: 'todo-app', 7 | directives: [TodoList, TodoView], 8 | template: ` 9 |
10 |
11 | 12 |
13 |
14 | 15 |
16 |
17 | ` 18 | }) 19 | export class TodoApp { 20 | constructor() {} 21 | } 22 | -------------------------------------------------------------------------------- /source/bootstrap.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | import 'zone.js/dist/zone'; 4 | import 'reflect-metadata'; 5 | 6 | import {bootstrap} from 'angular2/platform/browser'; 7 | 8 | import {TodoApp} from './TodoApp'; 9 | import {TasksService} from './services/TasksService'; 10 | 11 | bootstrap(TodoApp, [ 12 | TasksService 13 | ]); 14 | -------------------------------------------------------------------------------- /source/components/NewTodo.ts: -------------------------------------------------------------------------------- 1 | import {Component, Inject} from 'angular2/core'; 2 | 3 | import {TasksService, ITask} from '../services/TasksService'; 4 | import {Task} from '../Task'; 5 | 6 | @Component({ 7 | selector: 'new-todo', 8 | template: ` 9 |
11 |
12 | 17 |
18 |
19 | 22 |
23 | 24 |
25 | ` 26 | }) 27 | export class NewTodo { 28 | 29 | public task = new Task('', '', false); 30 | 31 | constructor(@Inject(TasksService) private TasksService) {} 32 | 33 | addNewTask() { 34 | this.TasksService.addTask(this.task.name, this.task.description); 35 | this.task.name = ''; 36 | this.task.description = ''; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /source/components/TodoList.ts: -------------------------------------------------------------------------------- 1 | import {Component, Inject} from 'angular2/core'; 2 | import {NewTodo} from './NewTodo'; 3 | 4 | import {TasksService} from '../services/TasksService'; 5 | 6 | @Component({ 7 | selector: 'todo-list', 8 | directives: [NewTodo], 9 | template: ` 10 | 19 | 20 | 21 | ` 22 | }) 23 | export class TodoList { 24 | 25 | private todos = []; 26 | 27 | constructor(@Inject(TasksService) private TasksService) { 28 | TasksService.tasks.subscribe(newTasks => this.todos = newTasks); 29 | TasksService.fetchTasks(); 30 | } 31 | 32 | selectNewTask(task) { 33 | this.TasksService.selectTask(task); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /source/components/TodoView.ts: -------------------------------------------------------------------------------- 1 | import {Component, Inject} from 'angular2/core'; 2 | 3 | import {TasksService, ITask} from '../services/TasksService'; 4 | 5 | @Component({ 6 | selector: 'todo-view', 7 | template: ` 8 |
9 |

{{ selectedTask.name }}

10 |

{{ selectedTask.description }}

11 | 19 | 22 |
23 | ` 24 | }) 25 | export class TodoView { 26 | private selectedTask; 27 | 28 | constructor(@Inject(TasksService) private TasksService) { 29 | TasksService.selectedTask.subscribe(newSelectedTask => this.selectedTask = newSelectedTask) 30 | } 31 | 32 | toggleTaskDone(task: ITask) { 33 | this.TasksService.toggleDone(task.id); 34 | } 35 | 36 | removeSelectedTask(task: ITask) { 37 | this.TasksService.removeTask(task.id); 38 | this.selectedTask = null; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /source/less/styles.less: -------------------------------------------------------------------------------- 1 | @import (inline) '../../node_modules/bootstrap/dist/css/bootstrap.min.css'; 2 | @import (inline) '../../node_modules/bootstrap/dist/css/bootstrap-theme.min.css'; 3 | 4 | .container { 5 | margin: 20px auto; 6 | } 7 | 8 | .task-done { 9 | text-decoration: line-through; 10 | } 11 | -------------------------------------------------------------------------------- /source/services/TasksService.ts: -------------------------------------------------------------------------------- 1 | import {Injectable} from 'angular2/core'; 2 | import {Observable} from 'rxjs/Observable'; 3 | 4 | export interface ITask { 5 | id: string; 6 | name: string; 7 | description: string; 8 | done: boolean; 9 | } 10 | 11 | @Injectable() 12 | export class TasksService { 13 | 14 | public tasks: Observable; 15 | private _tasksObserver: any; 16 | private _tasks: ITask[]; 17 | 18 | public selectedTask: Observable; 19 | private _selectedTaskObserver: any; 20 | 21 | constructor() { 22 | this.setDefaultTask(); 23 | 24 | this.selectedTask = new Observable(observer => 25 | this._selectedTaskObserver = observer); 26 | 27 | this.tasks = new Observable(observer => 28 | this._tasksObserver = observer); 29 | } 30 | 31 | addTask(taskName: string, description: string = '') { 32 | this._tasks.push({ 33 | id: this.UUID(), 34 | name: taskName, 35 | description: description, 36 | done: false 37 | }); 38 | this._tasksObserver.next(this._tasks); 39 | } 40 | 41 | toggleDone(taskId: string) { 42 | this._tasks.forEach((task: ITask, i) => { 43 | if(task.id == taskId) { 44 | this._tasks[i].done = ! task.done; 45 | } 46 | }); 47 | this._tasksObserver.next(this._tasks); 48 | } 49 | 50 | selectTask(task) { 51 | this._selectedTaskObserver.next(task); 52 | } 53 | 54 | removeTask(taskId: string) { 55 | this._tasks = this._tasks.filter(task => task.id != taskId); 56 | this._tasksObserver.next(this._tasks); 57 | } 58 | 59 | fetchTasks() { 60 | this._tasksObserver.next(this._tasks); 61 | } 62 | 63 | private setDefaultTask() { 64 | this._tasks = [ 65 | { 66 | id: "47665aae-4079-45ee-a789-e8145e1cde1e", 67 | name: "Went to ECMAScript 6 conference", 68 | description: "Great intro to ES6 features. The problem that still remains though is tooling. I find it pretty hard to decide on what to use, even though there are not many. I tried 6to5 and got pretty good results but Traceur is maintained by Google which makes the way to go hard to choose. I would be happy to hear some opinions on this.", 69 | done: false 70 | }, 71 | { 72 | id: "bd3cd2f3-1889-4877-afa2-81fcea66c089", 73 | name: "Learn Angular 2", 74 | description: "Angular 2 is the next version of Google's massively popular MV* framework for building complex applications in the browser (and beyond).", 75 | done: false 76 | }, 77 | { 78 | id: "256866d3-551c-431e-82bb-34cb65f59596", 79 | name: "Buy book about TypeScript", 80 | description: "TypeScript Revealed is a quick 100-page guide to Anders Hejlsberg's new take on JavaScript. With this brief, fast-paced introduction to TypeScript, .NET, Web and Windows 8 application developers who are already familiar with JavaScript will easily get up to speed with TypeScript", 81 | done: true 82 | } 83 | ]; 84 | } 85 | 86 | private UUID() { 87 | return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace( 88 | /[xy]/g, 89 | (c) => { 90 | let r = Math.random()*16|0, v = c === 'x' ? r : (r&0x3|0x8); 91 | return v.toString(16); 92 | }); 93 | } 94 | } 95 | --------------------------------------------------------------------------------