Skip to content

Commit

Permalink
feat(playground/app): add todo app
Browse files Browse the repository at this point in the history
closes #20
  • Loading branch information
Hotell committed Dec 8, 2015
1 parent a606627 commit b90ae5f
Show file tree
Hide file tree
Showing 17 changed files with 259 additions and 123 deletions.
31 changes: 31 additions & 0 deletions playground/Readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# TODO app

Here is an example app, how to write component oriented code with angular 1 + Typescript + ng-metadata.

Architecture is separated on smart + dumb components + composition.

There is only one Smart component:
- `todo-app`

All others are dump.
- `<add-todo>`
- `<todo-list>`
- `todo-item`

We are using also pipe to showcase `@Pipe` usage. It could have been also component.
- `remainigTodos`

Here UI mockup with components described:

![TODO-app-mock](todo-app-components.png)

## Getting Started

1. Clone this repo

1. Run `npm install`

1. Open terminal and launch the app in the browser `npm run playground`

1. Open `http://127.0.0.1:8080` and feel how the Force Awakens inside ( yes I love Star Wars )!

40 changes: 40 additions & 0 deletions playground/app/add-todo.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { Component, Output } from 'ng-metadata/ng-metadata';

import { TodoModel } from './todo-app.component.ts';

@Component({
selector: 'add-todo',
templateUrl: './app/add-todo.html'
})
export class AddTodoCmp{

@Output() onAdd: ( todo: {todo:TodoModel} )=>void;

// set an empty Model for the <input>
label: string = '';

// the submit event for the <form> allows us to type and
// press enter instead of ng-click on the <button> element
// we capture $event and prevent default to prevent form submission
// and if the label has a length, we'll call binding expression.
//
// we'll then set this.label back to an empty String
addTodo( event: ng.IAngularEvent, label: string ) {

if ( label.length ) {

const todo = {
label,
complete: false
};

this.onAdd( { todo } );
this.label = '';

}

event.preventDefault();

}

}
16 changes: 16 additions & 0 deletions playground/app/add-todo.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<form name="ctrl.addTodoForm" novalidate ng-submit="ctrl.addTodo($event, ctrl.label);" element-ready>

<div>

<div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label">
<input class="mdl-textfield__input" type="text" id="sample3" ng-model="ctrl.label">
<label class="mdl-textfield__label" for="sample3">What needs to be done?</label>
</div>
<!-- Accent-colored raised button with ripple -->
<button type="submit" class="mdl-button mdl-js-button mdl-button--raised mdl-js-ripple-effect mdl-button--accent">
Add
</button>

</div>

</form>
70 changes: 0 additions & 70 deletions playground/app/app.component.ts

This file was deleted.

44 changes: 0 additions & 44 deletions playground/app/app.html

This file was deleted.

17 changes: 14 additions & 3 deletions playground/app/app.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,20 @@
//import * as angular from 'angular';
import 'angular';
import { provide, makeDirective } from 'ng-metadata/ng-metadata';
import { AppCmp } from './app.component';
import { provide, makeDirective, makePipe } from 'ng-metadata/ng-metadata';
import { TodoAppCmp } from './todo-app.component';
import { AddTodoCmp } from './add-todo.component';
import { TodoListCmp } from './todo-list.component';
import { TodoItemCmp } from './todo-item.component';
import { RemainingTodosPipe } from './remainingTodos.pipe';

import { ElementReadyDirective } from './element-ready.directive';

export const AppModule = angular.module( 'app', [] )
.directive( provide( AppCmp ), makeDirective( AppCmp ) )
.directive( provide( TodoAppCmp ), makeDirective( TodoAppCmp ) )
.directive( provide( AddTodoCmp ), makeDirective( AddTodoCmp ) )
.directive( provide( TodoListCmp ), makeDirective( TodoListCmp ) )
.directive( provide( TodoItemCmp ), makeDirective( TodoItemCmp ) )

.filter( provide( RemainingTodosPipe ), makePipe( RemainingTodosPipe ) )

.directive( provide( ElementReadyDirective ), makeDirective( ElementReadyDirective ) );
2 changes: 1 addition & 1 deletion playground/app/element-ready.directive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export class ElementReadyDirective implements AfterContentInit {

} );

this.$scope.$watch( componentHandler.upgradeAllRegistered )
//this.$scope.$watch( componentHandler.upgradeAllRegistered )

}

Expand Down
26 changes: 26 additions & 0 deletions playground/app/remainingTodos.pipe.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { Pipe } from 'ng-metadata/ng-metadata';
import { TodoModel } from './todo-app.component.ts';

@Pipe( {
name: 'remainingTodos'
} )
export class RemainingTodosPipe {

transform( todos: TodoModel[] ): string {


// method to iterate the todo items and return
// a filtered Array of incomplete items
// we then capture the length to display 1 of 3
// for example
const remaining = todos.filter( ( item: TodoModel )=> {
return !item.complete;
} ).length;

const total = todos.length;

return `( ${ remaining } of ${ total } )`;

}

}
54 changes: 54 additions & 0 deletions playground/app/todo-app.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { Component } from 'ng-metadata/ng-metadata';

export type TodoModel = {
label: string,
complete: boolean
}

@Component( {
selector: 'todo-app',
templateUrl: './app/todo-app.html'
} )
export class TodoAppCmp{

// have some dummy data for the todo list
// complete property with Boolean values to display
// finished todos
todos: TodoModel[] = [
{
label: 'Learn Angular',
complete: false
}, {
label: 'Deploy to S3',
complete: true
}, {
label: 'Rewrite Todo Component',
complete: true
}
];

constructor() {}

createTodo( todo: TodoModel ) {

this.todos.unshift( todo );

}

// we simply splice it from the Array using the $index
removeTodo( todo: TodoModel ) {

const idx = this.todos.indexOf( todo );
this.todos.splice( idx, 1 );

}

markAsDone( todo: TodoModel ) {

const todoToMarkAsDone = this.todos.filter( (todoItem)=>todoItem.label===todo.label );
todoToMarkAsDone[0].complete = todo.complete;

}


}
17 changes: 17 additions & 0 deletions playground/app/todo-app.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<h1>Todos yo!</h1>

<h3>Todo List remaining: {{ ctrl.todos | remainingTodos }}</h3>

<add-todo on-add="ctrl.createTodo(todo)"></add-todo>

<todo-list>
<todo-item
ng-repeat="item in ctrl.todos"
todo="item"
on-done="ctrl.markAsDone(todo)"
idx="$index">
<button class="mdl-button mdl-js-button mdl-button--accent" ng-click="ctrl.removeTodo(item)">
Remove
</button>
</todo-item>
</todo-list>
29 changes: 29 additions & 0 deletions playground/app/todo-item.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { Component, Input, Output } from 'ng-metadata/ng-metadata';

import { TodoModel } from './todo-app.component.ts';

@Component({
selector: 'todo-item',
templateUrl: './app/todo-item.html'
})
export class TodoItemCmp{

@Input('todo') _todo: TodoModel;
@Input() idx: number;
@Output() onDone: ( todo: {todo:TodoModel} )=>void;

todo: TodoModel;

constructor(){

this.todo = angular.copy( this._todo );

}

done(todo: TodoModel) {

this.onDone( { todo } );

}

}
15 changes: 15 additions & 0 deletions playground/app/todo-item.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<label for="{{ctrl.idx}}"
class="mdl-checkbox mdl-js-checkbox mdl-js-ripple-effect"
ng-class="{'todo__list--complete': ctrl.todo.complete }"
element-ready>

<input
type="checkbox"
id="{{ctrl.idx}}"
class="mdl-checkbox__input"
ng-model="ctrl.todo.complete"
ng-change="ctrl.done(ctrl.todo)">
<span class="mdl-checkbox__label">{{ ctrl.todo.label }}</span>

</label>
<ng-transclude></ng-transclude>
8 changes: 8 additions & 0 deletions playground/app/todo-list.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { Component } from 'ng-metadata/ng-metadata';

@Component({
selector: 'todo-list',
templateUrl: './app/todo-list.html'
})
export class TodoListCmp{
}
3 changes: 3 additions & 0 deletions playground/app/todo-list.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<ul class="demo-list">
<li ng-transclude=""></li>
</ul>
Loading

0 comments on commit b90ae5f

Please sign in to comment.