-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(app): an implementation of TodoMVC using ng-forward
- Loading branch information
1 parent
13137fe
commit 931b534
Showing
18 changed files
with
819 additions
and
177 deletions.
There are no files selected for viewing
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
import {Component, View} from 'ng-forward'; | ||
|
||
@Component({ | ||
selector: 'filters', | ||
properties: ['status'] | ||
}) | ||
@View({ | ||
template: | ||
` | ||
<ul class="filters"> | ||
<li><a ng-class="{selected: filters.status == ''}" href="#/">All</a></li> | ||
<li><a ng-class="{selected: filters.status == 'active'}" href="#/active">Active</a></li> | ||
<li><a ng-class="{selected: filters.status == 'completed'}" href="#/completed">Completed</a></li> | ||
</ul> | ||
` | ||
}) | ||
export default class Filters { | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
import {Component, View, EventEmitter} from 'ng-forward'; | ||
import Filters from './Filters'; | ||
|
||
@Component({ | ||
selector: 'footer', | ||
properties: ['status', 'remainingCount', 'completedCount'], | ||
events: ['clearCompleted'] | ||
}) | ||
@View({ | ||
directives: [Filters], | ||
template: | ||
` | ||
<span class="todo-count"><strong>{{footer.remainingCount}}</strong> | ||
<ng-pluralize count="footer.remainingCount" when="{ one: 'item left', other: 'items left' }"></ng-pluralize> | ||
</span> | ||
<filters [status]="footer.status"></filters> | ||
<button class="clear-completed" ng-click="footer.clearCompleted.next()" ng-show="footer.completedCount">Clear completed</button> | ||
` | ||
}) | ||
export default class Footer { | ||
constructor() { | ||
this.clearCompleted = new EventEmitter(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
import {Component, View, EventEmitter, Inject} from 'ng-forward'; | ||
import FocusOn from '../directives/FocusOn'; | ||
|
||
const ESC_KEY = 27; | ||
const ENTER_KEY = 13; | ||
|
||
@Component({ | ||
selector: 'text-editor', | ||
properties: ['value', 'placeholder', 'inputClasses', 'focusOn'], | ||
events: ['start', 'enter', 'end', 'abort'] | ||
}) | ||
@View({ | ||
directives: [FocusOn], | ||
template: | ||
`<input class="{{textEditor.inputClasses}}" placeholder="{{textEditor.placeholder}}" | ||
ng-model="textEditor.value" | ||
(keyup)="textEditor.keyup($event.keyCode)" | ||
(focus)="textEditor.start.next()" | ||
(blur)="textEditor.end.next()" | ||
autofocus focus-on="textEditor.focusOn">` | ||
}) | ||
@Inject('$element') | ||
export default class TextEditor { | ||
constructor($element) { | ||
this.$input = $element.find('input')[0]; | ||
this.start = new EventEmitter(); | ||
this.enter = new EventEmitter(); | ||
this.end = new EventEmitter(); | ||
this.abort = new EventEmitter(); | ||
} | ||
|
||
keyup(keyCode) { | ||
if(keyCode === ENTER_KEY) { | ||
this.enter.next(); | ||
} | ||
if(keyCode === ESC_KEY) { | ||
this.abort.next(); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
<section class="todoapp"> | ||
|
||
<header class="header"> | ||
<h1>todos</h1> | ||
<text-editor | ||
(enter)="todoApp.addTodo()" | ||
(abort)="todoApp.newTodoTitle = ''()" | ||
[(value)]="todoApp.newTodoTitle" | ||
placeholder="What needs to be done?" | ||
input-classes="new-todo"></text-editor> | ||
</header> | ||
|
||
<section class="main" ng-show="todoApp.todoStore.todos.length" ng-cloak> | ||
|
||
<input class="toggle-all" | ||
type="checkbox" | ||
ng-model="todoApp.allChecked" | ||
ng-change="todoApp.todoStore.setAllTo(todoApp.allChecked)"> | ||
<label for="toggle-all">Mark all as complete</label> | ||
|
||
<ul class="todo-list"> | ||
<li ng-repeat="todo in todoApp.getFilteredTodos() track by $index" | ||
ng-class="{completed: todo.completed, editing: todo.editing}"> | ||
|
||
<todo-view | ||
[todo]="todo" | ||
(title-change)="todoApp.todoStore.save()" | ||
(completed-change)="todoApp.todoStore.save()" | ||
(remove)="todoApp.todoStore.remove(todo)"></todo-view> | ||
</li> | ||
</ul> | ||
|
||
</section> | ||
|
||
<footer class="footer" | ||
[remaining-count]="todoApp.todoStore.countRemaining()" | ||
[completed-count]="todoApp.todoStore.countCompleted()" | ||
(clear-completed)="todoApp.todoStore.removeCompleted()" | ||
ng-show="todoApp.todoStore.todos.length" | ||
ng-cloak></footer> | ||
</section> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
import {Inject, Component, View} from 'ng-forward'; | ||
import {TodoStore} from '../services/TodoStore'; | ||
import Footer from './Footer'; | ||
import TextEditor from './TextEditor'; | ||
import TodoView from './TodoView'; | ||
|
||
|
||
@Component({ | ||
selector: 'todo-app', | ||
bindings: [TodoStore] | ||
}) | ||
@View({ | ||
directives: [TextEditor, TodoView, Footer], | ||
template: require('./TodoApp.html') | ||
}) | ||
@Inject(TodoStore, '$location') | ||
export default class TodoApp { | ||
|
||
todoStore: TodoStore; | ||
newTodoTitle: string; | ||
|
||
constructor(todoStore, $location) { | ||
this.$location = $location; | ||
this.todoStore = todoStore; | ||
this.newTodoTitle = ''; | ||
} | ||
|
||
addTodo() { | ||
let newTodoTitle = this.newTodoTitle && this.newTodoTitle.trim(); | ||
if (newTodoTitle) { | ||
this.todoStore.add(newTodoTitle); | ||
this.newTodoTitle = ''; | ||
} | ||
} | ||
|
||
getFilteredTodos() { | ||
switch(this.$location.path()) { | ||
case '/completed': | ||
return this.todoStore.todos.filter(todo => todo.completed); | ||
case '/active': | ||
return this.todoStore.todos.filter(todo => !todo.completed); | ||
default: | ||
return this.todoStore.todos; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
import {Component, View, EventEmitter} from 'ng-forward'; | ||
|
||
@Component({ | ||
selector: 'todo-view', | ||
properties: ['todo'], | ||
events: ['titleChange', 'completedChange', 'remove'] | ||
}) | ||
@View({ | ||
template: | ||
` | ||
<div class="view"> | ||
<input class="toggle" type="checkbox" ng-model="todoView.todo.completed" ng-change="todoView.completedChange.next()"> | ||
<label ng-dblclick="todoView.todo.editing = true" ng-hide="todoView.todo.editing">{{todoView.todo.title}}</label> | ||
<button class="destroy" ng-click="todoView.remove.next()"></button> | ||
</div> | ||
<text-editor | ||
ng-show="todoView.todo.editing" | ||
[focus-on]="todoView.todo.editing" | ||
(start)="todoView.saveOriginal()" | ||
(end)="todoView.updateTitle()" | ||
(enter)="todoView.updateTitle()" | ||
(abort)="todoView.resetTodo()" | ||
[(value)]="todoView.todo.title" | ||
input-classes="edit"></text-editor> | ||
` | ||
}) | ||
export default class TodoView { | ||
constructor() { | ||
this.titleChange = new EventEmitter(); | ||
this.completedChange = new EventEmitter(); | ||
this.remove = new EventEmitter(); | ||
} | ||
|
||
updateTitle() { | ||
this.titleChange.next(); | ||
this.todo.editing = false; | ||
} | ||
|
||
saveOriginal() { | ||
this.originalTitle = this.todo.title; | ||
} | ||
|
||
resetTodo() { | ||
this.todo.title = this.originalTitle; | ||
this.todo.editing = false; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import {Directive, Inject} from 'ng-forward'; | ||
|
||
/** | ||
* Directive that places focus on the element it is applied to when the | ||
* expression it binds to evaluates to true | ||
*/ | ||
@Directive({ selector: '[focus-on]' }) | ||
@Inject('$timeout', '$scope', '$attrs', '$element') | ||
export default class FocusOn { | ||
constructor($timeout, $scope, $attrs, $element) { | ||
$scope.$watch($attrs.focusOn, newVal => { | ||
if (newVal) { | ||
$timeout(() => $element[0].focus(), 0, false); | ||
} | ||
}); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,67 +1,7 @@ | ||
import 'babel/polyfill'; | ||
import 'angular'; | ||
import 'zone.js'; | ||
import {Component, View, bootstrap} from 'ng-forward'; | ||
import uiRouter from 'angular-ui-router'; | ||
import InnerApp from './innerApp/innerApp'; | ||
import Test from './services/Test'; | ||
import Nested from './Nested/Nested'; | ||
import {bootstrap} from 'ng-forward'; | ||
import TodoApp from './components/TodoApp'; | ||
|
||
// Our root component which we will bootstrap below. Again no module management | ||
// needed. Here we specify the non-directive injectables we want to provide for | ||
// injection in the 'bindings' array in @Component. Notice we are passing in | ||
// "ui.router" as a string; ng-forward recognizes this as a module and bundles | ||
// it with this component. All of ui-router's injectables are now available to | ||
// inject into controllers or use in your templates. | ||
@Component({ | ||
selector: 'app', | ||
bindings: [Test, uiRouter] | ||
}) | ||
@View({ | ||
// Again specifying directives to use. We really wanted to support specifying | ||
// dependencies as Objects and not strings wherever possible. So we just pass | ||
// in the InnerApp and Nested class references. | ||
directives: [InnerApp, Nested], | ||
template: ` | ||
<h1>App</h1> | ||
<nested></nested> | ||
<p>Trigger count: {{ app.triggers }}</p> | ||
<!-- You still have to use non-event ng1 directives, such as ng-model used here. --> | ||
<h4>One Way Binding to Child:</h4> | ||
<input ng-model="app.message1"/> | ||
<h4>Two Way Binding to/from Child:</h4> | ||
<input ng-model="app.message2"/> | ||
<hr/> | ||
<!-- Here we see various bindings and events in use. We are listening for | ||
the event1 and event2 events on inner-app. You have to prepend with | ||
'()' for events. With message 1, 2 and 3, we show the three ways you | ||
can bind to component properties: prop (with no prefix) will pass in | ||
a simple string, [prop] will one-way bind to an expression, and | ||
[(prop)] will two way bind to an expression. --> | ||
<inner-app (event1)="app.onIncrement()" (event2)="app.onIncrement()" | ||
[message1]="app.message1" [(message2)]="app.message2" message3="Hey, inner app... nothin'"> | ||
</inner-app> | ||
` | ||
}) | ||
class App{ | ||
constructor(){ | ||
this.triggers = 0; | ||
this.message1 = 'Hey, inner app, you can not change this'; | ||
this.message2 = 'Hey, inner app, change me'; | ||
} | ||
|
||
onIncrement(){ | ||
this.triggers++; | ||
} | ||
} | ||
|
||
// Finally go ahead and bootstrap a component. It will look for the selector | ||
// in your html and call ng1's bootstrap method on it. What's cool is if you | ||
// include zone.js in your app, we'll automatically bootstrap your app within | ||
// the context of a zone so you don't need to call $scope.$apply (Mike is | ||
// this really true??). | ||
bootstrap(App); | ||
bootstrap(TodoApp); |
Empty file.
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.