SenchaCon 2011: ExtJS 4.1 Enhancements to Performance, Grid and API

Don Griffin, @dongryphon, from Sencha presents on ExtJS 4.1 features and performance improvements which I recently watched on vimeo here.  These are my notes from that session.

Main theme is performance, especially in the Grid.  On IE8, it takes ~4.7 seconds to fully render the Grid, with render and layout taking the most time and initalize and load taking the least. In 4.1, the load time is cut 2.4 seconds in IE8.  Focus was placed in all areas to improve on this to get it closer to the way it was in 3.4, but there are many more components in 4 which exacerbates the issue.

Regarding initialization, you can use the new class system and minimize the classes you load by using custom build.  This will then only load the classes (over the network) that you use in your app.  initComponent was also improved.

Rendering changes in 4.1 including removing createElement (createElement is fairly costly in 4.0); renderTpl manages this better by creating the elements in the hierarchy and then using renderSelectors during onRender to tie it back to the base element rather than doing this incrementally.  In 4.1, you now need to move custom code from onRender to beforeRender so that it gets called prior to be created in the DOM.

In 4.1, dockedItems config is available to add titles and other things to the edges of a container.

CSS calculations are expensive: Write + Read = Reflow because when you write to the DOM, the browser won’t cache it and thus causing an expensive reflow. In 4.1, “layout context” is moved to the global context so all the components are read at once and then writes after everything is read first. This minifies reflows and thus performance.

Other areas of investigation include converting layouts to CSS where possible, optimizing class creation and optimizing DOM Event binding.  This is being looked at for future releases.

In 4.1, Grid scrolling has gone back to “native scrolling” (or having true momentum when scrolling) especially when infinite scrolling.  This is a big deal since there were so many issues with the feel of scrolling in version 4.0; it wasn’t very smooth especially in IE.

API enhancements for 4.1 include:

  • Border Layout you can now do multiple regions (i.e., 2 west’s and 2 east’s) without nesting border layouts.  You can also set preferences to how regions layout with each others.  Regions can be added and removed dynamically too all within ONE border layout.  Splitters work as you’d expect, respecting the borders of its neighbors.
  • XTemplate compiles and runs faster and is now debuggable.  Now they compile to a function and support else, elseif and switch case control statements.  You can also add code right into the template.
  • Overrides will allow you to only bring in the components that you’re using in your build.

When porting from 4.0 to 4.1 you may need to check your custom layouts since now there is the global context for rendering as well as onRender which I mentioned earlier.

Increasing performance by 2 fold should certainly be welcome, but I especially like that attention was given to grid scrolling because I found it nearly unusable in 4.0 when using IE.  That will make my customers very happy.

For more information, I would encourage you to take a look at Ed Spencer’s blog on the subject.  An even more recent article is here that speaks to how to take advantage of the 4.1 performance enhancements in detail.

Using the Ext JS 4 MVC architecture and a few gotchas

I recently worked on a POC to integrate Solr search with the Ext JS 4 infinite scrolling grid. This allows you to scroll through 234k+ records without having the user page through the data; the scrolling does the data buffering automatically.  Other features include hit highlighting, wild card searching, resizing windows and word-wrapped columns.  However, the most interesting part to me was using the new MVC approach that Sencha introduced in this release to organize your project much like you would a Grails or Java Web project.  I’ll detail the approach I took to make that happen and point out some gotchas along the way.

First, let’s start from the model.  In the code below you can see I’ve defined a model and proxy which will be the piece that will pull the data.  There’s nothing too special here with the exception of the namespace I used to define the model, ESearch.model.EPart.  These are crucial that they are spelled correctly because they will be used later in other parts of the MVC.

Ext.define('ESearch.model.EPart', {
    extend: 'Ext.data.Model',
    idProperty: 'id',
    fields: [
        {name:'id', type:'int'}, 'description', 'item_number', 'part_number'
    ],
    proxy: {
        // load using script tags for cross domain, if the data in on the same domain as
        // this page, an HttpProxy would be better
        type: 'jsonp',
        url: 'http://solrdev1/solr-eti/select/',
        callbackKey: 'json.wrf',
        limitParam: 'rows',
        extraParams: {
            q: '*',
            wt:'json',
            hl:'on',
            'hl.fl': 'description',
            'json.nl':'arrarr'
        },
        reader: {
            root: 'response.docs',
            totalProperty: 'response.numFound'
        },
        // sends single sort as multi parameter
        simpleSortMode: true
    }
});

The next thing to consider is the store.  Arguably, this is not part of MVC per se, but it is used in conjunction with the model to define what type of store we want to use.  In this case, we want to use a buffered store of with a page size of 500.  You’ll also notice that in this code I create some listeners for beforeload and load so that I can allow sorting with Solr and to be able to do hit highlighting and query times.  You’ll also notice that I link the model to the store by using the namespace for the model parameter.  Let’s take a look:

// default to wildcard search
var query = '*';

Ext.define('ESearch.store.EParts', {
    extend: 'Ext.data.Store',
    model: 'ESearch.model.EPart',
    pageSize: 200,
    remoteSort: true,
    autoLoad:false,
    // allow the grid to interact with the paging scroller by buffering
    buffered: true,
    listeners: {
        beforeload:{ fn: function(store, options) {
            if (options && options.sorters) {
            var sorters = options.sorters;
            for (var i=0; i 1) {
                var queryParsed = query.replace(/\*/g,'').replace(/"/g, '').trim();
                var queries = queryParsed.split(' ');

                for (var i=0; i < queries.length; i++) {
                    if (queries[i]) {
                        var q = escapeRegExChars(queries[i]);

                        // Check to highlight text only in grid-body
                        var node = Ext.get("grid-inf").dom.childNodes;
                        for (var j=0; j < node.length;j++ ) {
                            if (node[j].className.contains("x-grid-body",true)) {
                                node = node[j];
                                break;
                            }
                        }

                        highlightText(node,
                                q + "+", 'HL', true);
                    }
                }
            }
            // temporary fix to address issue with scrollbars not resizing           
            var grid = Ext.getCmp('grid-inf');
            grid.resetScrollers();
          }
        }
    }
});

Now that I have the data being consumed the way I want it the next step is to put it into an infinity scrolling grid.  Here’s the code to do that:

Ext.define('ESearch.view.parts.List', {
    extend: 'Ext.grid.Panel',
    alias: 'widget.partslist',
    store: 'EParts',
    initComponent: function() {

        var groupingFeature = Ext.create('Ext.grid.feature.Grouping', {
            groupHeaderTpl: 'Group: {name} ({rows.length})',
            startCollapsed: false
        });

        var selectFeature = Ext.create('qcom.grid.SelectFeature');

        var config = {
            name: 'qparts-grid',
            id: 'grid-inf',
            verticalScrollerType: 'paginggridscroller',
            loadMask: true,
            invalidateScrollerOnRefresh: false,
            disableSelection: false,
            features: [groupingFeature,selectFeature],
            viewConfig: {
                trackOver: false
            },
            // grid columns
            columns:[{xtype: 'rownumberer',width: 45, sortable: false},{
                id: 'id-col',
                header: "ID",
                dataIndex: 'id',
                width:60
            },{
                id:"descr",
                header: "Description",
                dataIndex: 'description',
                width: 300,
                renderer: columnWrap
            },{
                id:"itemnum",
                header: "Item Numbers",
                dataIndex: 'item_number',
                width: 100
            },{
                id: "partnum",
                header: "Part Numbers",
                dataIndex: 'part_number',
                flex: 1,
                renderer: columnWrap
            }]
            ,selModel:{
           selType:'rowmodel'
          ,allowDeselect:true
          ,mode:'MULTI'
         },
            tbar:
                ['Search:',{
                     xtype: 'textfield',
                     name: 'searchField',
                     hideLabel: true,
                     width: 250,
                     emptyText: "Enter search terms separated by space",
                     listeners: {
                         change: {
                            fn: function adjustQuery(field) {
                                // temporary fix to address issue with scrollbars not resizing
                                this.store.resetData();

                                // Regex query and add wildcards where appropriate
                                if (field.value.length >= 1) {
                                    var values = field.getValue().match(/[A-Za-z0-9_%\/\.\-\|]+|"[^"]+"/g),
                                        value =[];
                                    if (values && values.length > 1) {
                                        for ( var i=0; i < values.length; i++ ) {
                                            if (values[i].indexOf("\"") >= 0 ) {
                                                value.push(values[i].toLowerCase());
                                            }
                                            else {
                                                value.push("*" + values[i].toLowerCase() + "*");
                                            }
                                        }
                                        query = value.join(" ");
                                        if (Ext.isChrome) {
                                            console.log(query);
                                        }
                                    }
                                    else {
                                        if (field.getValue().indexOf("\"") >= 0 ) {
                                            value.push(field.getValue().toLowerCase());
                                            query = value.join(" ");
                                        }
                                        else {
                                            // temporary fix because regex not picking up 1 char
                                            var temp = values ? values[0] : field.getValue();
                                            query = "*" + temp.toLowerCase() + "*";
                                        }
                                        if (Ext.isChrome) {
                                            console.log(query);
                                        }
                                    }
                                    this.store.load({
                                        params: {q:query}
                                    });
                                }
                            },
                            scope: this,
                            buffer: 500
                         }
                     }
                },
                {
                     xtype: 'tbfill'
                },{
                     xtype: 'displayfield',
                     name: 'totalText',
                     id: 'totalText',
                     hideLabel: true,
                     baseCls: 'x-toolbar-text',
                     style: 'text-align:right;',
                     width:180
                }
            ]
        };
        // apply config object
     Ext.apply(this, config);

     // call parent initComponent
     this.callParent(arguments);
    }
});

So from the above code you see that I’m defining a Grid Panel and assigning an alias to it called “partslist” (more on that later), but one gotcha I found is that I could not use the full namespace for the store definition — I had to just simply call it “EParts”.  Finally, you’ll see me set-up the columns and create a top bar that will hold the search field.  I do a regex to process the search field to create a wildcard search and to preserve quotes.  I also set the buffer to 500 so they it will wait 500ms for keystrokes before firing the search again.

Now that we have the grid, we need to put it some where.  This is where I bring the window into the picture.  In the code below, I simply define my window size, where I want it in the browser, window capabilities like maximize, collapse and closable, and finally the items.  Notice for the items, I’m using the alias partslist from the previously defined grid as the xtype.  This allows me to insert a grid as I defined it before without having to instantiate it as a variable.  Let’s take a look:

Ext.define( 'ESearch.view.Portal', {
    extend: 'Ext.window.Window',
    alias: 'widget.portal',
        width: 800,
        height:600,
        x: 150,
        y: 80,
        layout:'fit',
        border: false,
        closable: true,
        maximizable: true,
        collapsible: true,
        title: 'EParts Search',
        items: [{
            xtype: 'partslist',
            itemId:'myPartList'
        }]
});

So to finish up the MVC portion, we need a controller.  In the code below you will see how we create the controller and then define the models, stores, views, and any references needed.  You’ll also see in the init function where I invoke the store for the initial load of data as well as an example of how we could listen for certain events and do something with that event.  Notice again, that the alias from the grid comes into play (partslist) so that we can capture button events from the grid.  This wasn’t completely implemented, but it gives an example how it might be implemented.

Ext.define('ESearch.controller.Search', {
    extend: 'Ext.app.Controller',
    models:[
        'EPart'
    ],
    stores:[
        'EParts'
    ],
    views:[
        'parts.List'
    ],
    refs:[{
         ref:'PartsList',
         selector:'partslist'
    }],
    init:function(app) {
            var store = this.getEPartsStore();
            store.guaranteeRange(0, 199);
            this.control({
                   'partslist button':{
                    click:this.onButtonClick
               }
          });
    },
    onButtonClick: function(btn, e) {
        if (btn.operation === 'newSearch') {
            //TODO need to find a nice way to instantiate a new window
        }
    }
});

The last little bit of code simply defines the application and sets some criteria as to what paths we should use and which pieces of Ext JS are required for this application to function.  One gotcha I noticed is that you must define the first part of your namespace as the the folder your app will fall under.  You’ll notice that I have mapped the path “app” to “ESearch” so thusly my directory structure for my application must follow something like this:

 

So for instance, ESearch.view.Portal, must live as a file called Portal.js under app/view and same for the other files you see there.  The App.js file that contains the following code will be under the “webapp” directory adjacent to “app” to maintain relative pathing.  All I do is create my viewport based on its namespace and fire .show() to kick the whole thing off.

Ext.Loader.setConfig({enabled: true,
        paths: {
            'Ext.ux':'lib/extjs4/ux/',
            'ESearch': 'app'
        }
});
Ext.require([
    'Ext.grid.*',
    'Ext.data.*',
    'Ext.util.*',
    'ESearch.view.Portal',
    'Ext.grid.PagingScroller',
    'Ext.ux.grid.FiltersFeature',
    'Ext.grid.feature.Grouping',
    'Ext.grid.plugin.CellEditing',
    'Ext.state.CookieProvider'
]);

Ext.application({
     name:'ESearch',
     appFolder:'app',
     autoCreateViewport:false,
     controllers:['Search'],
     launch:function() {
     Ext.state.Manager.setProvider(new Ext.state.CookieProvider());
     this.viewport = Ext.create('ESearch.view.Portal', {
              stateId:'esearchWindow'
     });
     window[this.name].app = this;

      this.viewport.show();

    }
});

And finally, we have an HTML file that points to all the necessary JS files for this application to work, which is pretty standard stuff to bootstrap the application. However, with this approach, I only had to define the App.js file and not all the underlying JS files in the MVC portion. This is because the pathing we used in the previous section.

I hope this is useful for folks that would like to explore MVC in Ext JS 4 a little more.  I really find it useful because it helps break up a larger component much along the lines I’m used to.  In this way you could have multiple DEVs work on the same project pretty easily without walking over each other.

SenchaCon Day3: Debugging SenchaTouch

Evan Trimboli and Tommy Maintz give this talk.

Common JS Gotchas:

  • No compiler so all errors at runtime.  Be sure you check case and check spelling. Declare variables in the appropriate scope by declaring vars at the top of a function to avoid accidental local vars.  Careful using reserved words like “class”.
  • Truthiness & Equality: in JS there’s multiple ways things can be true/false rather than explicit.  Type coercion not performed by “===”.  Note: null/undefined are different (e.g., undefined means it doesn’t exist).
  • Closure you have to check for variable binding (outside the closure) and variable shadowing (response is an example in an ajax call).
  • No block scope only function scope (e.g., a variable in an IF block is global to the function)
  • Prototypal inheritance
  • Higher Order Functions can be passed to functions and be variables.

Tools to Debug

Chrome Debugger

  • Elements screen to look at position in DOM, see styles and their precedence. You can search for DOM ids and modify styles dynamically.
  • Scripts screen allows you to debug and view local and global variables. You can look at the call stack and set conditional breakpoints.
  • Resources screen shows size and time it took to load various artifacts that come over HTTP or XHR.
  • Console screen allows interactive commands and use log(), warn() and error() to log to the console

JSLint

  • Code Quality Tool to check for undefined or inadvertently declared global vars, required blocking, unreachable code, trailing commas and much more.

RemoteJS

  • Made by Webkit staff on Sencha team to help debug Android devices (on Github and blog).

Sencha Touch Specifics

On Data

  • Is the URL correct?? Did the request complete?? Is data valid??
  • Does the model align with the Component (from JSON for example).

Event Driven Model

  • Use appropriate callbacks
  • Remember that things are typically async

Overnesting

  • Panels with no layout
  • Specify layouts instead (be sure to specify layouts for Panel and use “fit”).  Use the DOM view in the Chrome debugger to see if elements have the correct height (common mistake is that they’ll be zero).

Dynamic Layouts

  • Remember doLayout and doComponent to reset layout after changes to the component

What is “this”

  • Log console.log(this) to be sure you’re dealing with the right object especially in callbacks and handlers.  You may not be in the right context.
  • All callbacks allow you to specify scope

Event Handling

  • Use capture and observe to check events.

SenchaCon Day3: Theming SenchaTouch

David Kaneda gives this talk.

CSS3, SASS and Compass make theming wicked easy.

www.sass-lang.com and www.compass-style.org are good resources to check out for details on those topics.

config.rb is the file you use to configure locations and themes for Sencha.  SASS/Compass use Ruby to compile CSS.  You can also set the output_style as compressed or expanded for production and development, respectively.

myapp.scss is the file you define your SASS.  So define variables, import libs you’ll use and @include the components you’ll use. From there, you can define your custom rules.

Sencha has tons for variables.  $base-color and $base-gradient are really cool to change theme colors across the board.

Helpful mixins include background-gradient, color-by-background, bevel-by-background and mask-by-background.  The latter 3 will accept the background color and augment accordingly.

The “UI” attribute can be added to components that can allow you to customize.  Buttons commonly use this to change the style of buttons (e.g. back/fwd, square, round, small, etc.).  You can also customize your own buttons with a mixin as follows: @include sencha-button-ui(‘orange’,#ff8000, ‘glossy’) and to use  in your component set the UI attribute to ‘orange’. Same with @include sencha-toolbar-ui and sencha-tabbar-ui.

Icons: over 300+ icons available.  In SCSS use @include pictos-icon(‘refresh’) and then in the JS just use “xtype:’button’ iconCls: ‘refresh’ iconMask: true ui: drastic”.

Optimization tips: remove unused components (e.g., @include-top-tabs:false and/or comment components in .scss file), remove images, remove UIs and output_style to compressed.

From resources dir run compass compile scss to compile the sencha source.  You compile after each scss change.  There’s also a way to automate this Dave says.

styleHtmlContent: true controlled by x-html for custom styling of HTML attributes.

 

SenchaCon Day3: SenchaTouch Performance Optimization

Tommy Mainz gives this talk.

Problems: DOM size, events, drawing, animation and code execution all affect performance on mobile devices.

DOM size:

  • Smaller screens help to keep DOM size smaller because there’s only so much data you can show without cognitive overload.
  • However, destroy components that you’re not using anymore in the deactivate. E.g. container.on(‘deactivate’, function(oldCard), { oldCard.destroy(); }); container.setActiveItem(newCard, ‘slide’); or overlay.on(‘hide’, function() { overlay.destroy(); });

Events – Solutions:

  • Use Event Manager from SenchaTouch which has automatic removal of listeners and delegation for Touch events.
  • Use event delegation when possible e.g. Ext.get(‘list’).on{ tap: function(e, target) {target.toggleCls(;pressed’); } delegate: ‘.list-item’)

Drawing: GPU

  • Keep your DOM structures “light” (especially in Android) by limited amount of advanced CSS3 (e.g., opacity, rgba, border-radius, gradients, text-shadow and box-shadows) and limiting the amount and depth of DOM elements.
  • Don’t go too deep: prevent unnecessary layouts. e.g. you have a tab panel with a panel of two tabs (using layout of fit) but instead should just use tabpanel with items being tabs.

Animations: GPU

  • Slide to a list e.g. on beforeActivate use list.setload(true) on activate remove loading mask (set it to false).  It will be more seamless and less burden on the

Code Execution: Compiler

  • Android v8 compiler is leading in this space
  • Not the usual suspects, like CPUs and JS engines
  • The reason is JIT compilation which is related to size of the file that the method exists in; complexity of method doesn’t seem to be relevant but comments/docs in code
  • Solution is to use minified and compressed code of both SenchaTouch and your own code.
  • You can set up builds to do this with .jsb3 files (JSBuilder is bundled with SenchaTouch)  you have builds and packages that will minify and consolidate those files.

SenchaCon Day2: Ext4 Charting and Visualization

Jamie Evans gives this excellent talk and how to use SVG, VML and HTML5 Canvas for Animation, charting and drawing.

Ext.draw.Surface renders primitives, text, images and gradients.

Ext.draw.Sprite to abstract the drawing elements and normalize differences between engine API syntax as well as do transforms and animations via Ext.fx. You can also do SpriteCompositions

Ext.draw.Component which extends Ext.Component and has engine preference via feature detection to work on any browser today.  You can customize the engine pref order.  Redraw is automatically taken care of for you.  Performance is excellent!

xtype for chart and then set the type to the kind you want (pie, line, bar, etc.).  It can do label config, special animation effects, shadow and highlighting effects, gradients, legends, callouts (tooltips on steroids) and renderers. Many customizations for each of them.

Uses cubic bezier curves for easing between animations in charting.

Printing will be supported across multiple browsers.

The demos are very visual and expressive.  I highly recommend checking the slides out.

 

 

SenchaCon Day2: Serving Mobile Apps from CMS

James Pearce from Sencha gives this talk.

How is Mobile is changing the Web?  The overriding question for this talk.

A perfect storm for HTML5, CSS3, and JS++ on new platforms like iOS, Android, and webkit.  New innovation is coming from mobile browser experience itself together with very capable computing devices that are in our pockets.

Mobile should be used as an adjective instead of a noun to represent how we should treat the browser on mobile devices.

Simplest thing to do is to have a separate theme to present on mobile versus desktop.

Per the mobile evolution, things like UI and biz logic can now be run on the mobile device and syncing data sets between the device and the server. Sencha Touch does a great job at doing this, even when the user is offline.

Walkthrough of connecting WordPress with SenchaTouch to a mobile device to categorize and comment on posts:

  1. Create a simple switching plugin
  2. Use MVC to create app
  3. CMS categories and posts will be modeled
  4. Stores populated and synced with JSON

For item 1, he uses UA sniffing to see if the device iPhone and redirect to a new location that contains the Sencha Touch app. He creates a simple PHP plugin and enable in into WordPress.

For item 2, he uses a generator from cmd line to create a skeleton Sencha Touch app.  He shows how it stubs out the MVC elements and tests it in a iPhone emulator to show it works.

For item 3, he shows how to create categories on the left side and the posts on the right. He does this again with the CLI to create these elements.

For item 4, he shows how we can switch the store to “ajax” and extend the PHP plugin to iterate through the data and serve that up in a JSON response.

Things to consider:

  • Thematic consistency
  • Brand consistency
  • Smart sync for large data sets
  • Account management for security in the case of a lost phone
  • http://tinysrc.net will help with scale/resize graphics for mobile screens

Mobile devices are different…geolocation, telephony, camera, messaging, etc.  How can you incorporate this easily with your CMS?

Most importantly, Mobile users are different.  Don’t just throw up a desktop designed web app on the mobile device.

SenchaCon Day2: Designing for the Mobile Experience

Brian Fling gives talk on Designing for the Mobile Experience.

Brian starts off with a history lesson on Western civilization.  The Guttenberg press is how we came out of the dark ages but shows how knowledge is the essential component and it’s persistence and availability changes the human existence.

6 Rules for building amazing apps for the mobile and tablet:

  1. Understand the medium: phones are about making the most of the moment and its primary function is communication — sharing your experience in real time.  Tablets are about focus, consumption, simple tasks and portability over desktop computers.
  2. Getting your data dialed: 1 out 3 projects go sideways due to BAD DATA! Content is data! There is no Design without the Data. Brian actual starts with DB modeling versus UI design to understand the data.  #1 challenge with mobile experience is dealing with the “semantic web” where data can come from EVERYWHERE and is it push or pull! (See Pull).  Finally, figure how to push or pull data from its source before you begin.
  3. Be a Master of Context:  See “Context of Mobile Interaction” from David Crow. Ask, how to add value to the user’s context.  Requires insight into what the user is doing (PSYCH 101).
  4. Design for the Interaction: The thing they see is the medium, the Know is the context and the do is the action they perform. Lighting is becoming more important to give the impression of 3D to emote with the user (See Norman’s Emotional Design). In Maslow’s hierarchy of needs, what need does your experience fulfill? Fulfill down to the lower level to reach level above. Affect, behavior, and cognition are aspects to consider on the response of the user to your application.  Example is the “deck” in iTunes.
  5. Focus on the Details: The future is in the details.
  6. Allow for ample time: Forget the waterfall model and focus on a more iterative process where there’s more involvement of the designer up front but also some involvement of the developer.  They will intersect and transition involvement as the project moves into development.

Brian admit’s that it feels like it’s always the first day of school — he feels he doesn’t necessarily have the answer due to the changes in this medium.

SenchaCon Day2: Desktop User Experience

Glen Lipka from Marketo talks about how to optimize the desktop user experience in a fun presentation with lots of primates!

Get the unexpected wow when showing your app to achieve loyalty.

See Layout managers on the ExtJS docs to cover layouts.  Here are some highlights to consider.

  • Global Nav should have maybe 4-6 components max
  • Use TreeNav on the left side
  • Use tabs to show local navigation
  • Show toolbar to have actions inside that tab
  • Canvas is the main area from the treenav actions
  • Aux tools are action oriented things that go on the right side
  • The drawer goes at the bottom to use for charting to complement stuff on the canvas

Mashup desktop and Web apps to create the Webtop application.  Web = Fold.  Percent of viewing time is spent on content above the fold (avoid scrolling!).  Accordions are your friend for a crazy, busy form to keep it in the “fold”.

Owning UX is how you do the one thing you do well instead of how many options you have in an application.

You may have to take a few steps backward (i.e., invest in the technology) before things can get better.  Get to “Global Max” island requires you to get in the ocean to get off “Local Max” island.

The Halloween Principle suggests that users are constantly distracted to spend attention on your application, even if it’s completely spelled out.  He suggests that users are not being intentionally being stupid.

User research suggests that users our horrible judges of their own reality.  It is better to watch and learn what a user does to understand how to create an application for them.  You can’t validate an idea through a few people.

Users don’t read!  They skim at best.  Don’t expect them to read long instructions.  You can use some tricks by using humor or making the action “fun to use” with simple demos including 1-2 minute videos.

It’s the little things that matter in your app.  Lots of smaller features make a customer happy than 1 huge feature.

Nice additions include easter eggs, keyboard shortcuts, drag-n-drop, and details.

Referrals (see Crossing the Chasm) should come from evangelists that are big proponents for your product.

Don’t design for the advanced user but instead target the middle by saving clicks and making the app easy to use.

Remove system friction to help users reach their goals.  Users will thank you later for this.

80% rule – get them the majority of the things they want (see Paradox of Choice). Limit the cognitive load on users with too much crap on the page.

Engineers and designers are distinctly separate roles that must have a good relationship together.

SenchaCon Day1: Ext 4 Architecture

Ed Spencer gives a talk on the Ext 4 architecture and focuses on the data package integration in this release.

It used to be you had to configure a proxy, reader and writer.  Now you define a store that has a model and proxy (with reader/writer underneath).  Models define fields, associations, validations, proxy and any other biz logic you need for it to perform its functions.  Stores use the model.

Example use Ext.regModel to define the model, “fields” to define the fields, and “hasMany” (and “belongsTo” to complete the relationship) to set-up the associations. Additionally, you can define validations and proxy which is pretty slick.  Once you have the model, you can define this in a store and create a callback function to traverse the items in the model.  Further, you can use this same model in a template to render this in a dataView.

Store now includes sorters and filters that can become “MixedCollections”.  This allows you to add/remove them any time. Ext.util.Sorter and Ext.util.Filter and perform these tasks anywhere in the application.

ExtJS4 extends proxies to include RestProxy on the server-side and WebStorageProxy and WebSqlProxy on the client-side. The latter two will manage this in persistent local storage.  WebSqlProxy allows for full relational access to the data.

Ext.data.Operation mediates between a store and a proxy for CRUD operations.  Ext.data.Batch allows you to combine operations into a single request.

Paging is improved  and provided by the store directly in ExtJS4.