├── .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 | 
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 |
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 |
--------------------------------------------------------------------------------