Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

Updating the extjs implementation #106

Closed
wants to merge 17 commits into from

4 participants

@boushley

Fixes #64

@boushley

This isn't ready to be merged, but to aide in commenting on the work and getting it ready we've created the pull request now.

...itecture-examples/extjs/js/view/TaskCompleteButton.js
@@ -0,0 +1,15 @@
+Ext.define('Todo.view.TaskCompleteButton' , {
+ extend: 'Ext.Component',
+ alias : 'widget.completeButton',
+ tpl: new Ext.XTemplate('<a id="clear-completed">{text}</a>', {compiled: true}),
+ setText: function (text) {
+ this.html = this.tpl.apply({text:text});
+ },
+ html: '<a id="clear-completed">Clear Completed</a>',
@sindresorhus Owner

Is this really needed, since it's set in the function above anyway?

Alright, I got the button working and removed this extra code.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
architecture-examples/extjs/js/view/TaskList.js
((6 lines not shown))
extend: 'Ext.view.View',
alias : 'widget.taskList',
tpl: Ext.create('Ext.XTemplate',
- '<tpl for=".">',
- '<div class="row">',
- '<input type="checkbox" {[values.checked ? "checked" : ""]} />',
- '<span class="{[values.checked ? "checked" : ""]}">{label}</span>',
- '</div>',
- '</tpl>',
+ '<ul id="todo-list"><tpl for=".">',
+ '<li>',
+ '<div class="view">',
+ '<input type="checkbox" {[values.checked ? "checked" : ""]} />',
+ '<span class="{[values.checked ? "checked" : ""]}">{label}</span>',
@sindresorhus Owner

The label shouldn't get a class. There should instead be a class on the <li> called done, when it's checked.

Good catch, I made that change, and changed it to a label instead of a span.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
architecture-examples/extjs/js/view/TaskList.js
((6 lines not shown))
extend: 'Ext.view.View',
alias : 'widget.taskList',
tpl: Ext.create('Ext.XTemplate',
- '<tpl for=".">',
- '<div class="row">',
- '<input type="checkbox" {[values.checked ? "checked" : ""]} />',
- '<span class="{[values.checked ? "checked" : ""]}">{label}</span>',
- '</div>',
- '</tpl>',
+ '<ul id="todo-list"><tpl for=".">',
+ '<li>',
+ '<div class="view">',
+ '<input type="checkbox" {[values.checked ? "checked" : ""]} />',
@sindresorhus Owner

Would be nicer to use the if template tag than executing arbitrary code in the template.
<tpl if="checked">checked</tpl>

Noted, I also switched the class assignment to be the same way.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@sindresorhus

Some notes:

  • The footer is inside the main container, it should be below it.

  • You probably know, it's missing the mark all as complete checkbox

  • You should trim the input for whitespace before adding it to make sure it's not possible to add todos with only whitespace.

  • Double clicking the todo doesn't activate the edit mode. And the delete button doesn't do anything.

  • In TaskCompleteButton new Ext.XTemplate( is used, but in Ext.create('Ext.XTemplate' if there isn't any difference, it should be consistent.

  • In the function onStoreDataChanged in Tasks.js; can you use a template for the count and checkedCount?

  • Remove the focus outlines

  • There need to be a space between the checkbox and label in a task

  • The clearCompleted button only shows if one or more of the todos are checked when the app is loaded

@boushley

@sindresorhus In your notes you mention "The clearCompleted button only shows if one or more of the todos are checked when the app is loaded" that is the desired behavior isn't it?

@sindresorhus

Is there a reason you have to refire the click event?

We only want this to occur when users click on the toggle-all checkbox right? That's why I am refining this here.

And I don't think that Component's have a click event by default.

I've Ext'ed up this component a bit. Annoyingly it seems ExtJS uses <input type="button"> for checkboxes, so I had to override that.

// Force ExtJS to use a checkbox instead of a button
Ext.define('ss.Checkbox', {
    extend: 'Ext.form.field.Checkbox',
    afterRender: function() {
        this.inputEl.dom.type = 'checkbox';
        this.callParent(arguments);
    }
});

Ext.define('Todo.view.CheckAllBox' , {
    extend: 'ss.Checkbox',
    alias: 'widget.checkAllBox',
    boxLabel: 'Mark all as complete',
    handler: function() {
        console.log('checked', this.getValue() );
    }
});

You then have access to all the Checkbox methods, like getting the value with .getValue() or handle a check with the .handler method.

@sindresorhus

If I loaded the app, started adding todos, and then checked a todo, the clearButton did not appear. However if I refreshed the app with a todo checked, it loaded the clearButton.

@boushley boushley Updated the TaskList to handle input clicks better.
Now once you double click on the task you can begin editing, without the list redrawing.
cfb40ac
@boushley

That scenario (clean app, add todos, check todo) around the clearButton seems to be working for me now. Can you try it after my recent commits, and see if it is still happening.

@sindresorhus

This could be simplified, by extending Ext.Button instead:

Ext.define('Todo.view.TaskCompleteButton' , {
    extend: 'Ext.Button',
    alias: 'widget.completeButton',
    tpl: new Ext.XTemplate('<a id="clear-completed">{text}</a>', {compiled: true}),
    setText: function (text) {
        this.update({text:text});
    }
});

You are clearly a better ext.js developer than I :) I thought Ext.Button's always used a tag. I'll include this, thanks.

boushley added some commits
@boushley boushley Updated TaskCompleteButton to extend Ext.Button 111533b
@boushley boushley Added editing on double click.
The editing works as long as you hit enter once you're done. I was having some trouble getting the blur event to take hold. I think the TaskList might need to be broken up to have a component rendering each item.

Also added the remove functionality, however there is a js error that occurs when doing this :/ not sure what causes it, but it works...
f3deb4e
@sindresorhus

That scenario (clean app, add todos, check todo) around the clearButton seems to be working for me now. Can you try it after my recent commits, and see if it is still happening.

Fixed :)

@addyosmani
Owner

Would you guys still like someone from ExtJS to review this? If so, I'll reach out and see what we can do.

@sindresorhus
Owner

It's @boushley' work, but in my opinion, yes, I'd think it would be a good idea to do this on all the apps. If we're going help devs choose between frameworks, we should at least show the best from and how to use each framework.

@boushley

So I've eliminated most of the problems, but there are still a few remaining:

  • The focus outlines are still in place
  • Edit does not end on blur (I'm having some trouble getting this one working, it wasn't supported previously, so I have to do a bit more reading, feel free to help out if you can come up with something @sindresorhus )
  • onStoreDataChanged still does not use a template for the strings it creates.
  • Still no consistency on new Ext.XTemplate vs Ext.create('Ext.XTemplate' (not sure which standard to choose)
@sindresorhus
Owner

1 - Outline fix:

:focus {
    outline: none;
}

2 - Just remember that the blur event does not bubble, so even delegation won't work...

4 - Looks like Ext.create is the way to go. Sencha should really update their docs to reflect this. I had to dig pretty deep to figure this out.

@addyosmani
Owner

@sindresorhus I've just reviewed @boushley's changes and they look okay to merge. Do you feel 2. above has been addressed appropriately?

It would be great if we could get @edspencer or someone else from Sencha involved in a review of whether this is the best way to advocate using ExtJS for MVC-like apps.

@boushley

2 has not been addressed. I think we need to redesign the list further, not to use the normal html templates that it has been using. Unfortunately this is over my head as far as extjs goes... and I'm working on ramping up for my new job. So if someone else wants to take a stab at that, please feel free.

@ettavolt

From my opinion, the way app is designed - not the best way to utilize what ExtJS has, because it doesn't use existing widgets. If app, that uses bundled widgets, is accepted, I could try to build one.

@sindresorhus
Owner

@ettavolt We would love some help on this one ;)

We just need to make sure we can implement our template using the builtin widgets. Is that possible?

@ettavolt

While page contents (tags/attributes) will differ significantly, it is possible to make it look like template.

@addyosmani
Owner

@ettavolt I think we would be open to reviewing an implementation that does make the best use of ExtJS. If you would be willing to put together something which follows our specs as closely as possible (i understand there may be differences) we could review and consider whether to include it over what's been done so far.

@sindresorhus related: I've tried reaching out to the ExtJS camp a few times with little success. I'll see if I can get at least someone well skilled in it in the community to come in as a reviewer for this (just so we're not doing anything too crazy) :)

@sindresorhus
Owner

@addyosmani Sounds like a plan.

I've also tried reaching out a couple of times on their IRC a while ago, got no response...

@ettavolt

I've built app with bundled widgets, but it doesn't yet support routing and has completely different look (the default of ext components). Maybe in next week I'll style it up.

@sindresorhus
Owner

@ettavolt Awesome! Thanks for doing this :)

Also, make sure you read the app spec thoroughly ;)

@ettavolt

Added routes. However, I can't figure out how to push to different branch - git says 403.

@sindresorhus

@ettavolt 403 means you don't have permission. Make sure your trying to push to a published branch on your own fork.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Feb 1, 2012
  1. @boushley
Commits on Feb 2, 2012
  1. @boushley
Commits on Feb 17, 2012
  1. @boushley
  2. @boushley
Commits on Feb 24, 2012
  1. @boushley
  2. @boushley
  3. @boushley

    Added Check All box.

    boushley authored
    Also performed some other tweaks to get things rendering cleaner.
  4. @boushley
  5. @boushley

    Updated the TaskList to handle input clicks better.

    boushley authored
    Now once you double click on the task you can begin editing, without the list redrawing.
Commits on Feb 25, 2012
  1. @boushley
  2. @boushley

    Added editing on double click.

    boushley authored
    The editing works as long as you hit enter once you're done. I was having some trouble getting the blur event to take hold. I think the TaskList might need to be broken up to have a component rendering each item.
    
    Also added the remove functionality, however there is a js error that occurs when doing this :/ not sure what causes it, but it works...
Commits on Mar 2, 2012
  1. @boushley

    Making checkall box disappear with no tasks.

    boushley authored
    Also updated the edited files to use tabs instead of spaces.
  2. @boushley
Commits on Mar 6, 2012
  1. @boushley
  2. @boushley
Commits on Mar 17, 2012
  1. @boushley
  2. @boushley
This page is out of date. Refresh to see the latest.
View
206 architecture-examples/extjs/css/app.css
@@ -0,0 +1,206 @@
+html,
+body {
+ margin: 0;
+ padding: 0;
+}
+
+:focus {
+ outline: none;
+}
+
+body {
+ font: 14px "Helvetica Neue", Helvetica, Arial, sans-serif;
+ line-height: 1.4em;
+ background: #eeeeee;
+ color: #333333;
+ width: 520px;
+ margin: 0 auto;
+ -webkit-font-smoothing: antialiased;
+}
+
+#todoapp {
+ background: #fff;
+ padding: 20px;
+ margin-bottom: 40px;
+ -webkit-box-shadow: rgba(0, 0, 0, 0.2) 0 2px 6px 0;
+ -moz-box-shadow: rgba(0, 0, 0, 0.2) 0 2px 6px 0;
+ -ms-box-shadow: rgba(0, 0, 0, 0.2) 0 2px 6px 0;
+ -o-box-shadow: rgba(0, 0, 0, 0.2) 0 2px 6px 0;
+ box-shadow: rgba(0, 0, 0, 0.2) 0 2px 6px 0;
+ -webkit-border-radius: 0 0 5px 5px;
+ -moz-border-radius: 0 0 5px 5px;
+ -ms-border-radius: 0 0 5px 5px;
+ -o-border-radius: 0 0 5px 5px;
+ border-radius: 0 0 5px 5px;
+}
+
+#todoapp h1 {
+ font-size: 36px;
+ font-weight: bold;
+ text-align: center;
+ padding: 0 0 10px 0;
+}
+
+#todoapp input[type="text"] {
+ width: 466px;
+ font-size: 24px;
+ font-family: inherit;
+ line-height: 1.4em;
+ border: 0;
+ outline: none;
+ padding: 6px;
+ border: 1px solid #999999;
+ -webkit-box-shadow: rgba(0, 0, 0, 0.2) 0 1px 2px 0 inset;
+ -moz-box-shadow: rgba(0, 0, 0, 0.2) 0 1px 2px 0 inset;
+ -ms-box-shadow: rgba(0, 0, 0, 0.2) 0 1px 2px 0 inset;
+ -o-box-shadow: rgba(0, 0, 0, 0.2) 0 1px 2px 0 inset;
+ box-shadow: rgba(0, 0, 0, 0.2) 0 1px 2px 0 inset;
+}
+
+#todoapp input::-webkit-input-placeholder {
+ font-style: italic;
+}
+
+#todo-list {
+ margin: 10px 0;
+ padding: 0;
+ list-style: none;
+}
+
+#todo-list li {
+ padding: 18px 20px 18px 0;
+ position: relative;
+ font-size: 24px;
+ border-bottom: 1px solid #cccccc;
+}
+
+#todo-list li:last-child {
+ border-bottom: none;
+}
+
+#todo-list li.done label {
+ color: #777777;
+ text-decoration: line-through;
+}
+
+#todo-list li .destroy {
+ display: none;
+ position: absolute;
+ top: 20px;
+ right: 10px;
+ cursor: pointer;
+ width: 20px;
+ height: 20px;
+ background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUBAMAAAB/pwA+AAAABGdBTUEAALGPC/xhBQAAACdQTFRFzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMAAAA////zMzMhnu0WAAAAAt0Uk5T5u3pqtV3jFQEKAC0bVelAAAAfUlEQVQI12NYtWpFsc8R865VqxhWrZpyBgg8QcylZ8AgCsjMgTCPrWJYfgYKqhjWwJgaDDVnzpw+c2bPmTPHGWzOnNm95/TuM2cOM/AARXfvBooeZAAp270bRCIz4QoOIGtDMqwJZoUEQzvCYrhzuhhWtUKYEahOX7UK6iEA3A6NUGwCTZIAAAAASUVORK5CYII=') no-repeat center center;
+}
+
+#todo-list li:hover .destroy {
+ display: block;
+}
+
+#todo-list li.editing {
+ border-bottom: none;
+ margin-top: -1px;
+ padding: 0;
+}
+
+#todo-list li.editing:last-child {
+ margin-bottom: -1px;
+}
+
+#todo-list li.editing .edit {
+ display: block;
+ width: 444px;
+ padding: 13px 15px 14px 20px;
+ margin: 0;
+}
+
+#todo-list li.editing .view {
+ display: none;
+}
+
+#todo-list li .view label {
+ word-break: break-word;
+}
+
+#todo-list li .edit {
+ display: none;
+}
+
+#todoapp footer {
+ margin: 0 -20px -20px -20px;
+ overflow: hidden;
+ color: #555555;
+ background: #f4fce8;
+ border-top: 1px solid #ededed;
+ padding: 0 20px;
+ line-height: 37px;
+ -webkit-border-radius: 0 0 5px 5px;
+ -moz-border-radius: 0 0 5px 5px;
+ -ms-border-radius: 0 0 5px 5px;
+ -o-border-radius: 0 0 5px 5px;
+ border-radius: 0 0 5px 5px;
+}
+
+#clear-completed {
+ float: right;
+ line-height: 20px;
+ text-decoration: none;
+ background: rgba(0, 0, 0, 0.1);
+ color: #555555;
+ font-size: 11px;
+ margin-top: 8px;
+ margin-bottom: 8px;
+ padding: 0 10px 1px;
+ cursor: pointer;
+ -webkit-border-radius: 12px;
+ -moz-border-radius: 12px;
+ -ms-border-radius: 12px;
+ -o-border-radius: 12px;
+ border-radius: 12px;
+ -webkit-box-shadow: rgba(0, 0, 0, 0.2) 0 -1px 0 0;
+ -moz-box-shadow: rgba(0, 0, 0, 0.2) 0 -1px 0 0;
+ -ms-box-shadow: rgba(0, 0, 0, 0.2) 0 -1px 0 0;
+ -o-box-shadow: rgba(0, 0, 0, 0.2) 0 -1px 0 0;
+ box-shadow: rgba(0, 0, 0, 0.2) 0 -1px 0 0;
+}
+
+#clear-completed:hover {
+ background: rgba(0, 0, 0, 0.15);
+ -webkit-box-shadow: rgba(0, 0, 0, 0.3) 0 -1px 0 0;
+ -moz-box-shadow: rgba(0, 0, 0, 0.3) 0 -1px 0 0;
+ -ms-box-shadow: rgba(0, 0, 0, 0.3) 0 -1px 0 0;
+ -o-box-shadow: rgba(0, 0, 0, 0.3) 0 -1px 0 0;
+ box-shadow: rgba(0, 0, 0, 0.3) 0 -1px 0 0;
+}
+
+#clear-completed:active {
+ position: relative;
+ top: 1px;
+}
+
+#todo-count span {
+ font-weight: bold;
+}
+
+#instructions {
+ margin: 10px auto;
+ color: #777777;
+ text-shadow: rgba(255, 255, 255, 0.8) 0 1px 0;
+ text-align: center;
+}
+
+#instructions a {
+ color: #336699;
+}
+
+#credits {
+ margin: 30px auto;
+ color: #999;
+ text-shadow: rgba(255, 255, 255, 0.8) 0 1px 0;
+ text-align: center;
+}
+
+#credits a {
+ color: #888;
+}
View
46 architecture-examples/extjs/index.html
@@ -1,25 +1,29 @@
<!doctype html>
-<html>
- <head>
- <meta charset="UTF-8">
- <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+ <title>ExtJS - TodoMVC</title>
+ <link rel="stylesheet" href="css/app.css">
+</head>
+<body>
+ <div id="todoapp">
+ <header>
+ <h1>Todos</h1>
+ <input id="new-todo" type="text" placeholder="What needs to be done?">
+ </header>
+ <section id="main">
+ </section>
+ </div>
- <title>ExtJS</title>
+ <div id="instructions">
+ Double-click to edit a todo.
+ </div>
+ <div id="credits">
+ Created by <a href="http://revolunet.com/">Revolunet</a>.<br />
+ Updates and Edits by <a href="http://github.com/boushley">Aaron Boushley</a>
+ </div>
- <link rel="stylesheet" href="css/style.css">
- </head>
-
- <body>
- <div id="todo">
- <h1>Todos</h1>
- <input type="text" id="taskfield" placeholder="What needs to be done?" />
- </div>
- <div class="credits">
- Credits:<br /><a href="http://revolunet.com/">Revolunet</a>
- </div>
-
- <script type="text/javascript" src="http://extjs.cachefly.net/ext-4.0.2a/bootstrap.js"></script>
- <script src="js/app.js"></script>
- </body>
+ <script type="text/javascript" src="http://extjs.cachefly.net/ext-4.0.2a/bootstrap.js"></script>
+ <script src="js/app.js"></script>
+</body>
</html>
View
37 architecture-examples/extjs/js/app.js
@@ -1,22 +1,27 @@
Ext.Loader.setConfig({enabled:true});
Ext.application({
- name: 'Todo',
- appFolder: 'js',
- controllers: ['Tasks'],
- launch: function() {
+ name: 'Todo',
+ appFolder: 'js',
+ controllers: ['Tasks'],
+ launch: function() {
- Ext.create('Ext.container.Container', {
- renderTo: 'todo',
- items: [{
- xtype: 'taskField',
- contentEl: 'taskfield'
- }, {
- xtype: 'taskList'
- }, {
- xtype: 'taskToolbar'
- }]
- });
+ Ext.create('Todo.view.TaskField', {
+ renderTo: Ext.select('header').first(),
+ contentEl: 'new-todo'
+ });
- }
+ Ext.create('Todo.view.CheckAllBox', {
+ renderTo: 'main'
+ });
+
+ Ext.create('Todo.view.TaskList', {
+ renderTo: 'main'
+ });
+
+ Ext.create('Todo.view.TaskToolbar', {
+ renderTo: 'todoapp'
+ });
+
+ }
});
View
227 architecture-examples/extjs/js/controller/Tasks.js
@@ -1,92 +1,143 @@
Ext.define('Todo.controller.Tasks', {
- models: ['Task'],
-
- stores: ['Tasks'],
-
- extend: 'Ext.app.Controller',
-
- views: ['TaskField', 'TaskList', 'TaskToolbar'],
-
- refs: [
- {ref: 'taskList', selector: 'taskList'},
- {ref: 'taskToolbar', selector: 'taskToolbar'}
- ],
-
- init: function() {
- this.control({
- 'taskField': {
- keyup: this.onTaskFieldKeyup
- },
- 'taskList': {
- itemclick: this.onListItemClick
- },
- 'taskToolbar > button': {
- click: this.onClearButtonClick
- }
- });
-
- this.getTasksStore().on({
- scope: this,
- update: this.onStoreDataChanged,
- datachanged: this.onStoreDataChanged
- });
- },
-
- onTaskFieldKeyup: function(field, event) {
- var value = field.getValue();
- if (event.keyCode === 13 && value !== '') {
- var store = this.getTasksStore();
- store.add({label: value, checked: false});
- field.reset();
- store.sync();
- }
- },
-
- onListItemClick: function(list, record, el) {
- record.set('checked', !record.get('checked'));
- record.store.sync();
- record.commit();
- },
-
- onClearButtonClick: function() {
- var records = [],
- store = this.getTasksStore();
-
- store.each(function(record) {
- if (record.get('checked')) {
- records.push(record);
- }
- });
- store.remove(records);
- store.sync();
- },
-
- onStoreDataChanged: function() {
- var info = '', text = '',
- store = this.getTasksStore(),
- totalCount = store.getCount(),
- toolbar = this.getTaskToolbar(),
- button = toolbar.items.first(),
- container = toolbar.items.last(),
- records = store.queryBy(function(record) {
- return !record.get('checked');
- }),
- count = records.getCount(),
- checkedCount = totalCount - count;
-
- if (count) {
- info = '<b>' + count + '</b> item' + (count > 1 ? 's' : '') + ' left.';
- }
-
- if (checkedCount) {
- text = 'Clear '+ checkedCount +' completed item' + (checkedCount > 1 ? 's' : '');
- }
-
- container.update(info);
- button.setText(text);
- button.setVisible(checkedCount);
- toolbar.setVisible(totalCount);
- }
+ models: ['Task'],
+
+ stores: ['Tasks'],
+
+ extend: 'Ext.app.Controller',
+
+ views: ['TaskField', 'TaskList', 'TaskToolbar'],
+
+ refs: [
+ {ref: 'taskList', selector: 'taskList'},
+ {ref: 'taskToolbar', selector: 'taskToolbar'},
+ {ref: 'checkAllBox', selector: 'checkAllBox'}
+ ],
+
+ init: function() {
+ this.control({
+ 'taskField': {
+ keyup: this.onTaskFieldKeyup
+ },
+ 'taskList': {
+ todoChecked: this.onTodoChecked,
+ itemdblclick: this.onTodoDblClicked,
+ onTaskEditKeyup: this.onTaskEditKeyup,
+ todoRemoveSelected: this.onTodoRemoveSelected
+ },
+ 'completeButton': {
+ click: this.onClearButtonClick
+ },
+ 'checkAllBox': {
+ click: this.onCheckAllClick
+ }
+ });
+
+ this.getTasksStore().on({
+ scope: this,
+ update: this.onStoreDataChanged,
+ datachanged: this.onStoreDataChanged
+ });
+ },
+
+ onTaskFieldKeyup: function(field, event) {
+ var ENTER_KEY_CODE = 13;
+ var value = field.getValue().trim();
+ if (event.keyCode === ENTER_KEY_CODE && value !== '') {
+ var store = this.getTasksStore();
+ store.add({label: value, checked: false});
+ field.reset();
+ store.sync();
+ }
+ },
+
+ onTodoChecked: function(record) {
+ record.set('checked', !record.get('checked'));
+ record.store.sync();
+ record.commit();
+ },
+
+ onTodoDblClicked: function (list, record, el) {
+ record.set('editing', true);
+ record.store.sync();
+ record.commit();
+ },
+
+ onTodoRemoveSelected: function (record) {
+ var store = this.getTasksStore();
+ store.remove(record);
+ store.sync();
+ },
+
+ onTaskEditKeyup: function (keyEvent, record, extEl) {
+ var ENTER_KEY_CODE = 13;
+ if (event.keyCode === ENTER_KEY_CODE) {
+ this.finalizeTaskEdit(extEl, record);
+ }
+ },
+
+ finalizeTaskEdit: function (extEl, record) {
+ value = extEl.getValue().trim();
+
+ if (!value) {
+ var store = this.getTasksStore();
+ store.remove(record);
+ store.sync();
+ } else {
+ record.set('label', value);
+ record.set('editing', false);
+ record.store.sync();
+ record.commit();
+ }
+ },
+
+ onClearButtonClick: function() {
+ var records = [],
+ store = this.getTasksStore();
+
+ store.each(function(record) {
+ if (record.get('checked')) {
+ records.push(record);
+ }
+ });
+ store.remove(records);
+ store.sync();
+ },
+
+ onCheckAllClick: function(checked) {
+ var store = this.getTasksStore();
+ store.each(function(record) {
+ record.set('checked', checked);
+ });
+ store.sync();
+ },
+
+ onStoreDataChanged: function() {
+ var info = '', text = '',
+ store = this.getTasksStore(),
+ totalCount = store.getCount(),
+ toolbar = this.getTaskToolbar(),
+ button = toolbar.items.first(),
+ container = toolbar.items.last(),
+ records = store.queryBy(function(record) {
+ return !record.get('checked');
+ }),
+ count = records.getCount(),
+ checkedCount = totalCount - count;
+
+ if (count) {
+ info = '<b>' + count + '</b> item' + (count > 1 ? 's' : '') + ' left.';
+ }
+
+ if (checkedCount) {
+ text = 'Clear '+ checkedCount +' completed item' + (checkedCount > 1 ? 's' : '');
+ }
+
+ this.getCheckAllBox().updateCheckedState(totalCount, checkedCount);
+ container.update(info);
+ button.setText(text);
+ button.setVisible(checkedCount);
+ toolbar.setVisible(totalCount);
+ }
});
View
12 architecture-examples/extjs/js/model/Task.js
@@ -1,8 +1,8 @@
Ext.define('Todo.model.Task', {
- extend: 'Ext.data.Model',
- fields: ['id', 'label', {name: 'checked', type: 'boolean'}],
- proxy: {
- type: 'localstorage',
- id: 'todos-extjs'
- }
+ extend: 'Ext.data.Model',
+ fields: ['id', 'label', {name: 'checked', type: 'boolean'}],
+ proxy: {
+ type: 'localstorage',
+ id: 'todos-extjs'
+ }
});
View
6 architecture-examples/extjs/js/store/Tasks.js
@@ -1,5 +1,5 @@
Ext.define('Todo.store.Tasks', {
- autoLoad: true,
- model: 'Todo.model.Task',
- extend: 'Ext.data.Store'
+ autoLoad: true,
+ model: 'Todo.model.Task',
+ extend: 'Ext.data.Store'
});
View
21 architecture-examples/extjs/js/view/CheckAllBox.js
@@ -0,0 +1,21 @@
+Ext.define('Todo.view.CheckAllBox' , {
+ extend: 'Ext.Component',
+ alias: 'widget.checkAllBox',
+ tpl: '<tpl if="hasContent"><input id="toggle-all" type="checkbox" <tpl if="allComplete">checked</tpl>> <label for="toggle-all">Mark all as complete</label></tpl>',
+ updateCheckedState: function (totalTasks, checkedTasks) {
+ this.update({
+ hasContent: !!totalTasks,
+ allComplete: checkedTasks == totalTasks
+ });
+ },
+ listeners: {
+ render: function (component) {
+ component.getEl().on('click', function (event, el) {
+ var checked = !!Ext.get(el).getAttribute('checked');
+ this.fireEvent('click', checked);
+ }, this, {
+ delegate: 'input'
+ });
+ }
+ }
+});
View
8 architecture-examples/extjs/js/view/TaskCompleteButton.js
@@ -0,0 +1,8 @@
+Ext.define('Todo.view.TaskCompleteButton' , {
+ extend: 'Ext.Button',
+ alias: 'widget.completeButton',
+ tpl: Ext.create('Ext.XTemplate', '<a id="clear-completed">{text}</a>', {compiled: true}),
+ setText: function (text) {
+ this.update({text:text});
+ }
+});
View
44 architecture-examples/extjs/js/view/TaskField.js
@@ -1,33 +1,33 @@
Ext.define('Todo.view.TaskField' , {
- enableKeyEvents: true,
+ enableKeyEvents: true,
- alias : 'widget.taskField',
+ alias : 'widget.taskField',
- extend: 'Ext.Component',
+ extend: 'Ext.Component',
- emptyText: 'What needs to be done?',
+ emptyText: 'What needs to be done?',
- afterRender: function() {
- this.callParent(arguments);
- this.field = this.el.first();
- this.field.on('keyup', this.onKeyup, this);
- },
+ afterRender: function() {
+ this.callParent(arguments);
+ this.field = this.el.first();
+ this.field.on('keyup', this.onKeyup, this);
+ },
- onKeyup: function(event) {
- this.fireEvent('keyup', this, event);
- },
+ onKeyup: function(event) {
+ this.fireEvent('keyup', this, event);
+ },
- getValue: function() {
- return this.field.dom.value;
- },
-
- setValue: function(value) {
- this.field.dom.value = value;
- },
+ getValue: function() {
+ return this.field.dom.value;
+ },
- reset: function() {
- this.setValue('');
- }
+ setValue: function(value) {
+ this.field.dom.value = value;
+ },
+
+ reset: function() {
+ this.setValue('');
+ }
});
View
73 architecture-examples/extjs/js/view/TaskList.js
@@ -1,16 +1,61 @@
Ext.define('Todo.view.TaskList' , {
- store: 'Tasks',
- loadMask: false,
- itemSelector: 'div.row',
- extend: 'Ext.view.View',
- alias : 'widget.taskList',
- tpl: Ext.create('Ext.XTemplate',
- '<tpl for=".">',
- '<div class="row">',
- '<input type="checkbox" {[values.checked ? "checked" : ""]} />',
- '<span class="{[values.checked ? "checked" : ""]}">{label}</span>',
- '</div>',
- '</tpl>',
- {compiled: true}
- )
+ store: 'Tasks',
+ loadMask: false,
+ itemSelector: 'li',
+ extend: 'Ext.view.View',
+ alias : 'widget.taskList',
+ autoEl: '<ul id="todo-list" />',
+ tpl: Ext.create('Ext.XTemplate',
+ '<tpl for=".">',
+ '<li class="<tpl if="checked">done</tpl> <tpl if="editing">editing</tpl>">',
+ '<div class="view">',
+ '<input type="checkbox" <tpl if="checked">checked</tpl> /> ',
+ '<label>{label}</label>',
+ '<a class="destroy"></a>',
+ '</div>',
+ '<input class="edit" type="text" value="{label}">',
+ '</li>',
+ '</tpl>',
+ {compiled: true}
+ ),
+ listeners: {
+ render: function () {
+ this.el.on('click', function (clickEvent, el) {
+ var extEl = Ext.get(el)
+ , parent;
+ if(extEl.getAttribute('type') === 'checkbox') {
+ parent = extEl.parent('li');
+ this.fireEvent('todoChecked', this.getRecord(parent));
+ }
+ }, this, {
+ // TODO I can't get this to delegate using something like div.view input or input[type="checkbox"]
+ // So this will have a bug with teh input.edit field... I need to figure that out so I don't have to
+ // do the if logic above.
+ delegate: 'input'
+ });
+
+ this.el.on('keyup', function (keyEvent, el) {
+ var extEl = Ext.get(el)
+ , parent;
+ if(extEl.getAttribute('type') === 'text') {
+ parent = extEl.parent('li');
+ this.fireEvent('onTaskEditKeyup', keyEvent, this.getRecord(parent), extEl);
+ }
+ }, this, {
+ delegate: 'input'
+ });
+
+ this.el.on('click', function (clickEvent, el) {
+ var extEl = Ext.get(el)
+ , record = this.getRecord(extEl.parent('li'))
+ , self = this;
+
+ // Todo this is clearly not the best way to do this, but without this we get an error when
+ // the item that was clicked is removed.
+ setTimeout(function () { self.fireEvent('todoRemoveSelected', record); }, 1);
+ }, this, {
+ delegate: 'a'
+ });
+ }
+ }
});
View
19 architecture-examples/extjs/js/view/TaskToolbar.js
@@ -1,11 +1,12 @@
Ext.define('Todo.view.TaskToolbar' , {
- hidden: true,
- extend: 'Ext.Toolbar',
- alias : 'widget.taskToolbar',
- items: [{
- xtype: 'button',
- hidden: true
- }, {
- xtype: 'container'
- }]
+ hidden: true,
+ extend: 'Ext.Toolbar',
+ alias : 'widget.taskToolbar',
+ autoEl: {tag: 'footer'},
+ requires: ['Todo.view.TaskCompleteButton'],
+ items: [{
+ xtype: 'completeButton'
+ }, {
+ xtype: 'container'
+ }]
});
Something went wrong with that request. Please try again.