Since I want to learn how Ext applications should be organized, and Jack is offering the Feed Viewer 3 prototype as "a good reference implementation" with "code broken up into logical classes", I began to write some documentation for this app. This helps me understand how all the pieces fit together, and probably others will be interested too.
This is a full quote of Jack's announcement (06-11-2007):
The FeedViewer example app has been rewritten from scratch for Ext 2.0. It is intended to be a good reference implementation of 2.0 and unlike the other examples which were written with speed of development in mind, FeedViewer 3 features its code broken up into logical classes. This makes the code much more organized and easier to maintain.
Some new things in FeedViewer 3 are reading pane placement, post summaries, context menus (tabs, grid and tree context menus), combobox (in "Add feed" window) and some significant performance improvements. It's starting to look pretty decent, so I have thrown a dev copy up. It is checked into the examples folder of the Ext 2.0 branch in SVN.
The application code consists of 4 classes (FeedGrid, FeedPanel, FeedWindow, and MainPanel), a singleton object (FeedViewer), an anonymous function invoked by Ext.onReady(), and a bit of auxiliary markup in the HTML file. There is also a CSS file, feed-viewer.css. On the server side, there is a PHP script, feed-proxy.php, acting as an HTTP proxy for getting the feeds.
Contents |
Class MainPanelThe main panel of the application, containing a FeedGrid and a preview (or reading) pane. This preview pane can be positioned at the bottom or the right, or it can be hidden.
MainPanel extends Ext.TabPanel with the following methods:
The MainPanel constructor creates the toolbar for the FeedGrid, which has 3 buttons:
Button text Button handler ------------------------------------------- OpenAll MainPanel.openAll Reading Pane MainPanel.movePreview Summary FeedGrid.togglePreview
Class FeedGridA grid whose rows represent feed items. FeedGrid extends Ext.grid.GridPanel with the following methods:
This is an overview of the code:
// constructor FeedGrid = function(viewer, config) { this.viewer = viewer; /* the MainPanel instance */ Ext.apply(this, config); this.store = /* HttpProxy & XmlReader */; this.columns = /* title, author, date */; FeedGrid.superclass.constructor.call(this, /* options object: region, id, loadMask, sm, viewConfig */); } // class extension Ext.extend(FeedGrid, Ext.grid.GridPanel, { // methods listed above }
Class FeedPanelThe panel used to store and manage the list of available feeds. This list is, in fact, a tree. You can add or remove nodes from it, using either the panel's toolbar or the context menu.
FeedPanel extends Ext.TreePanel with the following methods:
| Menu item | Handler |
|---|---|
| Load Feed | FeedPanel.ctxNode.select |
| Remove | FeedPanel.removeFeed |
| Add Feed | FeedPanel.showWindow |
This panel has a toolbar with 2 buttons:
Button text Handler ------------------------------------------- Add Feed FeedPanel.showWindow Remove FeedPanel.removeFeed
Overview of the code:
// constructor FeedPanel = function() { FeedPanel.superclass.constructor.call(this, /* options: id, region, title, split, width, minSize, maxSize, collapsible, margins, cmargins, rootVisible, lines, autoScroll, root, collapseFirst, tbar */); this.feeds = /* new Ext.tree.TreeNode() */; this.getSelectionModel().on(/* event listeners */); }; // class extension Ext.extend(FeedPanel, Ext.tree.TreePanel, { // methods };
Class FeedWindowThe modal dialog window for adding feeds. Includes an editable combobox with feed URLs.
FeedWindow extends Ext.Window with the following methods:
Object FeedViewerA singleton object with only one method and one property:
// *** TEST IF THIS WORKS INSTEAD OF ORIGINAL CODE *** FeedViewer = (function(){ return { // This is a custom event handler passed to preview panels so link open in a new windw LinkInterceptor : { render: function(p){ p.body.on({ 'mousedown': function(e, t){ // try to intercept the easy way t.target = '_blank'; }, 'click': function(e, t){ // if they tab + enter a link, need to do it old fashioned way if(String(t.target).toLowerCase() != '_blank'){ e.stopEvent(); window.open(t.href); } }, delegate:'a' }); } }, // Initialization init : function() { Ext.QuickTips.init(); var tpl = Ext.Template.from('preview-tpl', { compiled:true, getBody : function(v, all){ return v || all.description; } }); FeedViewer.getTemplate = function(){ return tpl; } var feeds = new FeedPanel(); var mainPanel = new MainPanel(); feeds.on('feedselect', function(feed){ mainPanel.loadFeed(feed); }); var viewport = new Ext.Viewport({ layout:'border', items:[ new Ext.BoxComponent({ // raw element region:'north', el: 'header', height:32 }), feeds, mainPanel ] }); // add some default feeds feeds.addFeed({ url:'http://extjs.com/news/archive/feed', text: 'ExtJS.com News' }, false, true); feeds.addFeed({ url:'http://extjs.com/forum/external.php?type=RSS2', text: 'ExtJS.com Forums' }, true); feeds.addFeed({ url:'http://feeds.feedburner.com/ajaxian', text: 'Ajaxian' }, true); } } })(); Ext.onReady(FeedViewer.init, FeedViewer);
Application initializationThe anonymous function invoked by Ext.onReady() does the following:
IssuesThis is a developer's demo based on an alpha version of Ext 2.0, which has not even been released to the general public, so it's probably too early to report bugs or issues. But just in case, let's build a list of the problems we find while playing with the demo.