# Ext JS From Zero

> Learn the config-driven enterprise JavaScript framework that thousands of internal apps still run on - and that almost nobody documents well: the class system, components and the containment tree, layouts, the data package (Model/Store/proxy/reader), the Grid and forms, MVVM with ViewControllers and ViewModels and binding, and Sencha Cmd. The magic, made explainable - especially if you got thrown into a legacy Ext JS codebase with no map.


---

# Ext JS From Zero

Some frameworks you choose. Ext JS is one you usually *inherit* - you join a company, get handed a
sprawling internal app (a CRM, a trading desk, an admin console, a logistics dashboard), open the code,
and nothing looks like the JavaScript you know. No HTML to speak of. No `document.querySelector`. Just
enormous nested objects full of `xtype` and `items` that somehow become a full desktop-grade UI. There's
often no useful documentation, the original authors are long gone, and the official docs sit behind a
login. If that's ever cost you sleep - or a job - this guide is for you.

Here's the mental model that turns Ext JS from alien to obvious: **you don't build the UI, you describe
it.** You write a tree of plain config objects - "a panel containing a grid and a form" - and the
framework instantiates, lays out, renders, and manages every piece for you. Every visible thing is a
**component**; components live inside **containers**; data flows in through **stores**. Once those three
ideas click - components, containment, stores - the "magic" stops being magic and becomes a system you
can read, debug, and change with confidence.

> 📝 This assumes solid **JavaScript** - objects, functions, prototypes, `this`
> ([JavaScript From Zero](/guides/javascript-from-zero)). Ext JS predates and differs sharply from
> React/Vue/Angular ([What a Framework Even Is](/guides/what-a-framework-even-is) explains the family),
> so park those instincts at the door. Ext JS is a large proprietary framework with its own build tool,
> so code here is shown and explained rather than run on the page.

## How to read this

Read in order - it builds from a single component up to a data-bound grid in an MVVM app, using a running
example of a small **users admin** screen. The last phase is a survival kit for legacy codebases. Phases
carry difficulty badges.

## The phases

**Part 1 - The foundations (🟢 → 🟡)**
1. **[What Ext JS Even Is](01-what-extjs-is.md)** 🟢 - the config-driven, component-based model, Classic vs Modern, and why it feels so different.
2. **[The Class System](02-the-class-system.md)** 🟡 - `Ext.define`, `extend`, the `config` block, `xtype`, `Ext.create`, `requires`, and mixins.
3. **[Components & the Containment Tree](03-components-and-containers.md)** 🟡 - every UI piece is a component; `items` nest them; the lifecycle and how to find a component without `Ext.getCmp`.

**Part 2 - Real screens (🟡 → 🔴)**
4. **[Layouts: How Things Get Positioned](04-layouts.md)** 🟡 - `fit`, `hbox`/`vbox`, `border`, `card`; why "nothing shows up" is almost always a layout problem.
5. **[The Data Package](05-the-data-package.md)** 🔴 - `Ext.data.Model`, `Store`, proxies and readers: how server data reaches the screen.
6. **[The Grid & Forms](06-the-grid-and-forms.md)** 🔴 - `Ext.grid.Panel` bound to a store, columns and renderers, editing plugins, and form panels.

**Part 3 - Wiring & survival (🔴 → 🟢)**
7. **[MVVM: ViewControllers, ViewModels & Binding](07-mvvm-and-binding.md)** 🔴 - view logic, two-way `bind`, formulas, and how a modern Ext JS app is held together (plus legacy MVC).
8. **[Sencha Cmd, Theming & Surviving a Legacy Codebase](08-sencha-cmd-and-survival.md)** 🟢 - the build tool, SASS theming, debugging tactics, Ext JS vs the modern field, and where to go next.

> The whole framework is one sentence: **describe a tree of components, point them at stores, and let the
> framework run it.** Hold that, and even a ten-year-old Ext JS app becomes something you can read.


---

# What Ext JS Even Is

You open a legacy internal app - a CRM, an admin console, a trading desk - expecting JavaScript and
find a wall of nested objects: `xtype` this, `items` that, `panel` inside `tabpanel` inside `viewport`.
No HTML to grep for, no `document.querySelector`, no JSX. And somehow this soup renders a polished,
desktop-grade interface that real money depends on.

Nothing here is magic. It's one consistent idea wearing unfamiliar clothes.

Stated once, plainly: **in Ext JS you don't build the UI, you describe it.** You write a tree of plain
JavaScript config objects - "a panel containing a grid and a form" - and the framework reads that
description and does the rest: creates the objects, lays them out, renders the HTML, wires up events,
manages their whole life. You write the *what*; the framework owns the *how*.

> 📝 **Ext JS** - Sencha's comprehensive JavaScript framework for **data-intensive enterprise web
> apps**: admin consoles, CRMs, trading desks, dashboards. It started around 2006 as an extension of
> the YUI library (hence "Ext"), grew into a full framework, and is now owned by IDERA - predating
> React, Vue, and Angular, so it solves the same problems with conventions invented years earlier.

## The one big idea: config in, UI out

A minimal Ext JS application that puts a panel on the screen:

```javascript
Ext.application({
    name: 'Admin',

    launch: function () {
        Ext.create('Ext.panel.Panel', {
            renderTo: Ext.getBody(),
            title: 'Users',
            width: 400,
            height: 200,
            html: 'A panel, built from a config object.'
        });
    }
});
```

*What just happened:* `Ext.application({...})` declares the app and hands Ext a `launch` function to
run once the framework finishes booting. Inside it, `Ext.create('Ext.panel.Panel', {...})` asks the
framework to build one component from the config object - pure data: `title`, `width`, `height`,
`html`, and `renderTo: Ext.getBody()` telling Ext where to drop it in the page. You never wrote a
`<div>`, never touched the DOM. You described a panel and Ext turned that description into real,
rendered HTML.

The part that makes Ext *feel* like Ext: components nest.

```javascript
Ext.create('Ext.panel.Panel', {
    renderTo: Ext.getBody(),
    title: 'Users',
    width: 500,
    height: 300,
    layout: 'vbox',
    items: [
        { xtype: 'textfield', fieldLabel: 'Search' },
        { xtype: 'button',    text: 'Add user' }
    ]
});
```

*What just happened:* the outer Panel now has an `items` array - its children. Each child is *also* a
config object, but instead of a full class name we used `xtype`: `'textfield'` and `'button'` are
short nicknames for component classes. Ext reads `items`, sees each `xtype`, creates the matching
component, and stacks them inside the panel (`layout: 'vbox'` lays children out vertically). Scale
that up - panels inside tab panels inside a viewport - and you have the entire UI of a legacy app.

> 💡 The mental shortcut for reading *any* Ext JS file: find the outermost config object, then follow
> `items` downward like branches of a tree. The shape of the nested objects **is** the shape of the
> screen. You're reading a blueprint, not instructions that run top to bottom.

## Components, containers, and `xtype`

Three words the rest of this guide leans on (Phases 2 and 3 go deep - this is the teaser).

📝 **Component** - every visible thing in Ext JS is a component: a button, a text field, a data grid,
a panel, a popup window. All descend from a common base class, which is why they all take config the
same way and share the same lifecycle. **Container** - a component that can hold *other* components
via its `items` array (a Panel is the classic container). **xtype** - the short string name of a
component class (`'grid'`, `'form'`, `'button'`), used so Ext can create children lazily, only when
needed. Components nest inside containers via `items`; `xtype` names which one to make.

## Classic vs Modern: which toolkit is this codebase?

Ext JS ships in two flavors, called **toolkits**, and knowing which one you're looking at saves real
confusion.

📝 **Classic toolkit** - the older, mature, desktop-focused widget set. This is where the famous,
deeply-featured data **Grid** lives, along with the dense forms and windows enterprise apps are built
from. If you inherited a big internal back-office app, it's very likely Classic. **Modern toolkit** - 
built for touch and mobile. Same core concepts - components, `items`, `xtype`, stores - but the widget
*packages* differ, so a few class names and options won't match between them.

A codebase picks one (or builds both via Sencha Cmd, the build tool we'll meet later):

```javascript
// app.json (an Ext JS app's config file)
{
    "name": "Admin",
    "toolkit": "classic",
    "theme": "theme-triton"
}
```

*What just happened:* the `app.json` at the root of an Ext JS project declares the `toolkit` outright
 - your fastest answer. No `app.json` handy? Class names mentioning `Ext.grid.Panel` and themes like
`triton` or `neptune` point to **Classic**; paths under `modern` or touch-flavored components point to
**Modern**.

> ⚠️ Don't mix advice across toolkits. A snippet for the Modern Grid may use config options that don't
> exist in the Classic Grid, and vice versa. Include the toolkit name in searches - "Ext JS
> **classic** grid column renderer" - or you'll burn an afternoon on options that never applied.

## Why it feels so different from React

If your instincts come from React, Vue, or Angular, Ext JS will feel alien - knowing *why* is the
fastest way to stop fighting it.

The first difference is **config vs. markup**. React uses JSX, a template that looks like HTML. Ext
JS has no templates and no JSX - you write **plain JavaScript objects** that *describe* components.
`{ xtype: 'panel', title: 'Users', items: [...] }` is an ordinary object literal. The structure of
your data is the structure of your UI.

The second is **how much the framework owns**. Modern front-end work is "pick your libraries": React
for views, plus a router, a state library, a data-fetching library, a bundler - all your choice. Ext
JS is the opposite: **batteries-included to the extreme**. Widgets, layout engine, class system, data
layer, charts, theming, even the build tool - all Sencha's, all shipped together. That's why it feels
like its own universe rather than a library dropped into a project: it *is* the project.

> 💡 New to a framework calling *your* code instead of you calling *its*? That inversion of control is
> shared by every framework - see [What a Framework Even Is](/guides/what-a-framework-even-is). Ext JS
> just takes it further: it owns more of the stack than almost anything else you'll meet.

So when newcomers call Ext JS "weird," they mean: no JSX, config instead of markup, a custom class
system instead of plain ES classes, its own data layer instead of `fetch` plus a state library, slow
Java-based builds, and docs that were historically thin and often paywalled. Every one is real
friction - but none is chaos. It's a consistent system with consistent rules, and that's learnable.

📝 **Where you'll meet it:** greenfield Ext JS is rare today - new projects reach for React/Vue/
Angular. What's *not* rare is maintaining the enormous body of revenue-critical enterprise software
already built on Ext JS. That's the job this guide prepares you for.

## Our running example: a users admin screen

To keep every phase grounded in something real, we'll build one small feature across the guide: a
**users admin screen** - a table of users on the left, an edit form on the right that fills in when
you click a row.

```javascript
{
    xtype: 'panel',
    title: 'User Administration',
    layout: 'hbox',
    items: [
        { xtype: 'grid', title: 'Users', flex: 1 },   // the list, on the left
        { xtype: 'form', title: 'Edit',  flex: 1 }     // the editor, on the right
    ]
}
```

*What just happened:* a top-level Panel lays its children side by side (`layout: 'hbox'`) and holds
two components - a Grid and a Form, each taking an equal share of the width (`flex: 1`). Both are
empty placeholders right now. Coming phases fill the Grid with columns, feed it real users through a
**Store**, wire the Form to edit the selected row, and hold it together with Ext's MVVM tools. You can
already *read the shape of the whole feature*, because the config tree is the feature.

## Recap

- **The one idea:** in Ext JS you describe the UI as a tree of plain config objects, and the framework
  instantiates, lays out, renders, and manages it. You write *what*, not *how*.
- **Everything visible is a component**; **containers** hold other components via their `items`
  array; **`xtype`** is the short string name Ext uses to create a component (often lazily).
- **Two toolkits:** **Classic** (desktop, the mature widget set with the famous Grid) and **Modern**
  (touch/mobile). Check `app.json`'s `toolkit` to know which one a codebase uses - don't mix advice.
- **Why it feels alien vs. React:** config objects instead of JSX/templates, and a batteries-included
  framework that owns the whole stack instead of a library you assemble from npm pieces.
- **Where you meet it:** maintaining big, revenue-critical enterprise apps - greenfield is rare, but
  existing codebases are everywhere.
- **Running example:** a users admin screen (a Grid of users + an edit Form) we'll build across the guide.

## Quick check

Three questions on the config-driven model, the toolkits, and how Ext differs from React:

```quiz
[
  {
    "q": "What is the core idea behind how you build a UI in Ext JS?",
    "choices": [
      "You write a tree of plain config objects describing the components, and the framework instantiates, lays out, and renders them",
      "You write HTML templates with placeholders that Ext fills in",
      "You imperatively create and append DOM nodes with document.createElement",
      "You write JSX that compiles to component calls"
    ],
    "answer": 0,
    "explain": "Ext JS is config-driven: you describe the UI as nested config objects (parents with an `items` array of children) and the framework builds, lays out, renders, and manages everything. No HTML, no JSX, no manual DOM."
  },
  {
    "q": "What does `xtype` mean in an Ext JS config object?",
    "choices": [
      "The short string name of a component class, used so Ext can create the component (often lazily)",
      "An HTML element tag like 'div' or 'span'",
      "A CSS class applied to the rendered element",
      "The unique id of an existing component instance"
    ],
    "answer": 0,
    "explain": "`xtype` is the short nickname for a component class - `'grid'`, `'form'`, `'button'`. Ext reads it inside `items` to know which component to create, and can defer creation until the component is actually needed."
  },
  {
    "q": "What's the main difference between the Classic and Modern toolkits, and how do you tell which a codebase uses?",
    "choices": [
      "Classic targets desktop (the mature widget set incl. the Grid), Modern targets touch/mobile; check the `toolkit` field in app.json",
      "Classic is the free version and Modern is the paid one; check your Sencha license",
      "Classic is written in JavaScript and Modern in TypeScript; check the file extensions",
      "There is no difference - they are two names for the same widgets"
    ],
    "answer": 0,
    "explain": "Both share the same core concepts (components, items, xtype, stores), but Classic is the desktop-focused mature widget set and Modern is built for touch/mobile, so some widget packages differ. The `toolkit` field in app.json tells you which one outright."
  }
]
```


---

# The Class System

The one idea to carry through this phase: **in Ext JS, almost nothing is a plain object - everything
is a *class*.** Every view, data model, controller, and reusable widget is declared with `Ext.define`.
Once you can read an `Ext.define` block - what it extends, what config it exposes, what xtype it
answers to - you can read the *shape* of an entire Ext JS codebase, even one nobody has touched in years.

Why a custom class system when JavaScript already has classes? Ext JS predates ES2015 classes by the
better part of a decade - the language gave you raw prototypes and not much else, so Sencha built a
full object system on top: single inheritance, mixins, static members, and the part that surprises
everyone, **declarative config properties that generate their own getters and setters**. The class
system also powers **lazy loading** and the dependency graph the build tool relies on. This is the
spine of the framework, not legacy cruft to ignore.

> 📝 This builds on [What Ext JS Even Is](01-what-extjs-is.md) - read that first if "config-driven"
> and "component" don't ring a bell yet.

## `Ext.define`: declaring a class

Every class starts with `Ext.define`, taking a **string name** and a **config object**.

```javascript
Ext.define('MyApp.view.UserGrid', {
    extend: 'Ext.grid.Panel',

    title: 'Users',

    initComponent: function () {
        this.columns = [
            { text: 'Name',  dataIndex: 'name', flex: 1 },
            { text: 'Email', dataIndex: 'email', flex: 1 }
        ];
        this.callParent(arguments);
    }
});
```

*What just happened:* we declared a class named `'MyApp.view.UserGrid'`. That dotted string is a
**namespace path** mapped to a folder/file convention (`app/view/UserGrid.js` under `MyApp`), which is
how the loader and build tool find it. `extend: 'Ext.grid.Panel'` means our class **inherits** from
the built-in grid panel. Inside `initComponent` (a lifecycle hook run as the component sets itself up)
we call `this.callParent(arguments)` to run the parent's version too. **Forgetting `callParent` is one
of the most common Ext JS bugs** - the component half-initializes into a blank or broken widget with
no obvious error.

A few things about `extend`:

- It's **single inheritance** - one parent, like Java or C# classes (mixins cover multiple, below).
- `this.callParent(arguments)` passes the original arguments straight to the parent method; you'll
  see `this.callParent([newArg])` when a method deliberately changes what it passes up.
- The dotted name tells you the file's home - `MyApp.controller.Users` lives at
  `app/controller/Users.js`. This convention is how you navigate an unfamiliar Ext JS repo.

## The `config` block: getters and setters you never wrote

This trips up everyone arriving from plain JavaScript. Declare properties inside a `config` block, and
Ext JS **automatically generates a getter and setter for each one** - plus optional hooks that fire
when the value changes.

```javascript
Ext.define('MyApp.view.UserPanel', {
    extend: 'Ext.panel.Panel',

    config: {
        userName: 'Anonymous',
        unreadCount: 0
    },

    // optional hook: runs whenever userName is set
    updateUserName: function (newName, oldName) {
        this.setTitle('Profile: ' + newName);
    }
});

var p = Ext.create('MyApp.view.UserPanel');
p.getUserName();          // 'Anonymous' - getter you never wrote
p.setUserName('Nika');    // setter you never wrote; fires updateUserName
```

*What just happened:* we declared two config properties. Ext JS generated `getUserName()`/
`setUserName()` and `getUnreadCount()`/`setUnreadCount()` for us - nowhere in the file are those
methods written, yet they exist. Calling `setUserName('Nika')` also triggers the `updateUserName`
hook, which we used to re-title the panel. **This is why an Ext JS codebase is full of `getX()`/`setX()`
calls for properties that seem to have no definition** - they're config accessors.

Two hook flavors, and the difference matters:

- **`applyXxx(newValue, oldValue)`** runs *before* the value is stored and can *transform or veto*
  it - whatever you `return` becomes the stored value (`undefined` skips the set). Use it to coerce
  or validate.
- **`updateXxx(newValue, oldValue)`** runs *after* the value is stored. Use it for side effects.

> 💡 When you find a setter call and want to know what *actually* happens, search the class (and its
> parents) for `applyThatProp` and `updateThatProp` - that's where the real logic hides.

## `xtype` vs `Ext.create`: why config objects are everywhere

Phase 1 showed nested config objects with `xtype` instead of `new`. Here's the machinery behind that.

An **`xtype`** is a short string alias assigned to a component class. Once assigned, you can describe
that component as a plain object - `{ xtype: 'usergrid' }` - and the framework instantiates it
**lazily**, only when actually needed.

```javascript
Ext.define('MyApp.view.UserGrid', {
    extend: 'Ext.grid.Panel',
    xtype: 'usergrid',            // short alias
    // alias: 'widget.usergrid',  // the long form xtype is sugar for
    title: 'Users'
});

// Eager: instantiated right now
var grid = Ext.create('MyApp.view.UserGrid', { title: 'All Users' });

// Lazy: just a config object; the parent builds it when it renders
Ext.create('Ext.panel.Panel', {
    items: [
        { xtype: 'usergrid' },
        { xtype: 'textfield', fieldLabel: 'Search' }
    ]
});
```

*What just happened:* `Ext.create('MyApp.view.UserGrid', {...})` builds an instance **immediately**.
The second block never instantiates anything by hand - it hands the parent panel an `items` array of
plain config objects, and the parent turns each into a real component **lazily**, as it lays itself
out. That lazy-by-xtype pattern is *the* reason an Ext JS codebase reads as giant nested config trees
rather than a pile of `new` calls.

- `xtype: 'usergrid'` is sugar for `alias: 'widget.usergrid'` - you'll see both forms in the wild.
- `Ext.create(...)` is the loader-aware replacement for `new`; prefer it - it cooperates with the
  dependency system, `new` does not.

## `requires` and Ext.Loader: the "works in dev, breaks in build" trap

Ext JS can load classes **dynamically** - the **Ext.Loader** fetches a class file the first time it's
referenced, reading the **`requires`** array on each class to know what to load and in what order.

```javascript
Ext.define('MyApp.view.UserPanel', {
    extend: 'Ext.panel.Panel',

    requires: [
        'MyApp.view.UserGrid',
        'Ext.form.field.Text'
    ],

    items: [
        { xtype: 'usergrid' },
        { xtype: 'textfield', fieldLabel: 'Search' }
    ]
});
```

*What just happened:* we declared that `UserPanel` **depends on** `UserGrid` and the text field
class. The Loader guarantees both load before `UserPanel` is used, and - crucially - **Sencha Cmd
reads these same `requires` arrays to build the dependency graph** for the production bundle. Leave
one out and you've planted a time bomb.

> ⚠️ The classic Ext JS bug: you reference a class by xtype but forget to add it to `requires`. In
> **development** it often still works, because the Loader lazily fetches everything on demand and the
> class happens to already be loaded by something else. A **production build** with Sencha Cmd - which
> only bundles what's declared - leaves it out, and the app throws `xtype not found` or shows a blank
> screen. When a built app breaks but dev is fine, **suspect a missing `requires` first.**

## Mixins: behavior without inheritance

`extend` gives you exactly one parent. To compose *reusable behavior* from several sources, use
**mixins** - additional classes whose methods get folded into yours.

```javascript
Ext.define('MyApp.util.Logger', {
    extend: 'Ext.Base',
    mixins: ['Ext.mixin.Observable'],   // can now fire/listen to events

    statics: {                          // shared across all instances
        VERSION: '1.0'
    },

    log: function (msg) {
        this.fireEvent('logged', msg);  // method from the Observable mixin
    }
});
```

*What just happened:* `Logger` extends `Ext.Base` (the root of every Ext class) but also **mixes in**
`Ext.mixin.Observable`, gaining event methods like `fireEvent` and `on` without inheriting from an
event class. You can list **multiple** mixins - composition alongside single inheritance. The
`statics` block means `MyApp.util.Logger.VERSION` is shared by the class itself, not copied per
instance.

> 💡 Two entry points you'll see atop a legacy app: **`Ext.application({...})`** boots a full
> MVC/MVVM app, and the older **`Ext.onReady(function () {...})`** runs code once the framework and
> DOM are ready. Both just mean "where execution begins."

## Recap

- **Everything is a class** declared with `Ext.define('Namespace.Path.Name', {...})`; the dotted name
  is also the file's location in the project.
- **`extend`** gives single inheritance; call up the chain with **`this.callParent(arguments)`** - 
  forgetting it is a top-tier Ext JS bug.
- The **`config` block** auto-generates `getX()`/`setX()`; setters fire **`applyX`** (transform/veto,
  before store) and **`updateX`** (side effects, after store) hooks - that's where the real logic lives.
- **`xtype`** (sugar for `alias: 'widget.xxx'`) lets the framework instantiate components **lazily**
  from plain config objects; **`Ext.create`** instantiates eagerly and is the loader-aware `new`.
- **`requires`** feeds Ext.Loader and Sencha Cmd's build graph; a forgotten `requires` works in dev but
  breaks the production build.
- **Mixins** compose reusable behavior from multiple sources alongside single inheritance; `statics`
  holds class-level members.

## Quick check

```quiz
[
  {
    "q": "You set a config property with setUserName('Nika') and want to run a side effect (re-render a label) afterward. Which hook fires after the value is stored?",
    "choices": ["applyUserName", "updateUserName", "initUserName", "onUserName"],
    "answer": 1,
    "explain": "updateXxx runs after the value is stored - use it for side effects. applyXxx runs before and can transform or veto the value."
  },
  {
    "q": "An app works perfectly in dev but throws 'xtype not found' after a Sencha Cmd production build. What's the most likely cause?",
    "choices": ["A missing requires declaration", "A typo in callParent", "Using Ext.create instead of new", "A mixin conflict"],
    "answer": 0,
    "explain": "The build only bundles classes listed in requires. Dev loads lazily and often works by accident; the production build doesn't, so the undeclared class is missing."
  },
  {
    "q": "What is xtype: 'usergrid' shorthand for?",
    "choices": ["extend: 'usergrid'", "alias: 'widget.usergrid'", "requires: ['usergrid']", "statics: { usergrid: true }"],
    "answer": 1,
    "explain": "xtype is sugar for alias: 'widget.usergrid'. The widget. prefix is what registers the class so it can be created lazily from a config object."
  }
]
```


---

# Components & the Containment Tree

[The last phase](02-the-class-system.md) covered how Ext JS classes are declared - 
`Ext.define`, `extend`, the `config` block, `xtype`. Now we use those classes to build
something you can actually see, via the one idea that makes a sprawling legacy Ext JS
app readable instead of terrifying.

💡 **The mental model: your UI is a tree, and you only ever write the tree - never the
DOM.** No appending `<div>`s, no `appendChild`. You write a nested config object that
says "a viewport containing a panel, and that panel contains a grid and a form," and
the framework walks that tree, instantiates each node, lays it out, and renders the
real HTML for you. The structure you write *is* the structure of the screen. When you
inherit an Ext JS codebase, you are reading someone's tree.

Here's that tree as a config, for our running **users admin** screen - a grid of users on
the left, an edit form on the right:

```javascript
Ext.create('Ext.container.Viewport', {
    layout: 'border',
    items: [
        {
            xtype: 'panel',
            region: 'center',
            title: 'Users',
            items: [
                { xtype: 'grid', /* ...the users grid... */ },
                { xtype: 'form', /* ...the edit form...  */ }
            ]
        }
    ]
});
```

*What just happened:* we described a screen, we didn't build one. The `Viewport` is the
root node (it fills the browser window). Its `items` array holds one panel; that panel's
`items` array holds a grid and a form. `xtype` names the class for each node so the
framework knows what to instantiate. `items` nesting *is* the layout wiring - no HTML
touched anywhere.

The same tree, drawn out:

```mermaid
flowchart TD
    V[Viewport] --> P[Panel: Users]
    P --> G[Grid: users]
    P --> F[Form: edit user]
```

📝 Keep that picture in your head for the whole framework. A grid is a node. A form field
is a node. A button inside a toolbar inside a panel - all nodes. Phase 5's stores feed
data *into* these nodes, but the skeleton is always this containment tree.

## Component vs Container vs Panel

Three classes form a ladder, and almost every widget you'll meet sits on a rung.

- **`Ext.Component`** is the base of *every visible thing*. It owns a lifecycle, a position
  in the tree, an element it renders to, show/hide, and so on. A plain component is a leaf - 
  it does not hold children.
- **`Ext.container.Container`** *is* a component that can hold child components, in its
  **`items`** array. It adds the machinery to manage children and arrange them with a
  **layout** (Phase 4). Anything with `items` is a container.
- **`Ext.panel.Panel`** is the container you'll see most often: a container plus a
  **header/title**, a **border**, and **tools** (the little buttons in the header), docked
  toolbars, collapsibility, and so on. Grids, forms, windows, and tab panels all **extend
  Panel** - which is why they all have titles and borders "for free."

```javascript
// Leaf component - no items, just renders something:
{ xtype: 'component', html: 'Just some markup, no children.' }

// Container - holds children, arranges them, but no chrome:
{ xtype: 'container', items: [ { xtype: 'button', text: 'A' },
                               { xtype: 'button', text: 'B' } ] }

// Panel - a container WITH a title, border, and header tools:
{ xtype: 'panel', title: 'Users', items: [ /* ...children... */ ] }
```

*What just happened:* same `items` mechanism each step up the ladder; the difference is
purely what each class adds. The bare `component` has no `items` because it can't hold any.
The `container` holds children but draws no header - useful for invisible grouping. The
`panel` is the same thing dressed up with the chrome users expect. When you see a class in
legacy code, ask "is it a Component (leaf), a Container (holds items), or a Panel
(container with chrome)?" - that one question tells you most of what it does.

## The lifecycle, and why "hidden" is not "destroyed"

A component isn't just a config blob - it goes through a fixed sequence of life stages, and
knowing them is how you debug "my setup code ran too early" and "why is the app leaking memory."

The Classic toolkit lifecycle, in order:

`constructor` → **`initComponent`** → `render` → `afterRender` → ... (lives, reacts to
events) ... → `destroy`

The one you override constantly is **`initComponent`** - the canonical place to set up
config and build `items` *before* the component renders.

```javascript
Ext.define('App.view.UsersGrid', {
    extend: 'Ext.grid.Panel',
    xtype: 'usersgrid',

    initComponent: function () {
        this.title = 'Users';
        this.columns = [
            { text: 'Name',  dataIndex: 'name',  flex: 1 },
            { text: 'Email', dataIndex: 'email', flex: 2 }
        ];
        this.callParent(arguments); // ⚠️ never skip this
    }
});
```

*What just happened:* we subclassed a grid and used `initComponent` to assemble its config
just before render - handy when a value has to be computed (a default title, columns built
from a variable) rather than written as a static literal. The critical line is
`this.callParent(arguments)`: it runs the parent's `initComponent`, which is what actually
wires up the component. Forget it and your component half-initializes and fails in
confusing ways. (The Modern toolkit leans on the `config` block and an `initialize` method
instead, but the override-and-`callParent` discipline is the same idea.)

⚠️ **The part that bites people: hiding a component does not destroy it.** `cmp.hide()`
just sets it invisible - the instance, its DOM, event listeners, and any store bindings
all still exist, consuming memory. If your code creates components over and over (open a
window, close it, open it again...) and only *hides* them, you have a **memory leak** and
stale listeners firing on ghosts. To truly free a component you must **`destroy`** it.

```javascript
win.hide();      // invisible, but fully alive - listeners still attached, DOM still there
win.destroy();   // tears down DOM + listeners + child components; the instance is gone
```

*What just happened:* `hide()` is for "I'll show this again in a second." `destroy()` is for
"I'm done with this forever." A container's `destroy()` cascades - destroying a panel
destroys its grid and form too. Rule of thumb for inherited code: things created repeatedly
but never destroyed mean a leak.

## Adding and removing children at runtime

The tree isn't frozen at startup - containers grow and shrink while the app runs. This is
how a "+ Add User" button makes a new form appear, or how closing a tab removes a panel.

```javascript
var panel = Ext.getCmp('mainPanel'); // (shown for illustration - see the warning below)

panel.add({ xtype: 'form', title: 'Edit User' }); // append a child to items
panel.remove(someChildComponent);                 // remove one child (destroys it by default)
panel.removeAll();                                 // clear every child
```

*What just happened:* `add` takes a config object (or a real component) and slots it into
the container's `items`, then re-runs the layout so it appears. `remove` pulls one child out
 - and by default **destroys** it, which is usually what you want (no leak). `removeAll`
empties the container, and each call re-lays-out the container so children rearrange
automatically. Notice we had to *find* `panel` first - the next, and most important, skill.

## Finding components - done right

The single most useful skill for navigating an inherited Ext JS codebase: grabbing a
component to read its value, refresh its store, or react to a click. There's a wrong way
that's all over old code, and a right way.

### ⚠️ The trap: `Ext.getCmp` and global ids

```javascript
// DON'T build code around this:
var grid = Ext.getCmp('usersGrid'); // requires a hand-assigned id: 'usersGrid'
```

*What just happened:* `Ext.getCmp(id)` looks up a component by a manual `id:` you set in its
config. It *works* - but `id` must be **globally unique across the entire app**, forever. The
moment two instances of the same view exist (two tabs, a reused window), their ids collide
and the lookup returns the wrong one or breaks outright. It's a well-known anti-pattern - 
recognize it in legacy code, and don't add more of it.

### The safe local id: `itemId`

If you need a stable handle inside one container, use **`itemId`** - it's scoped to that
container, so it can't collide globally - and reach it with `down('#...')`:

```javascript
{ xtype: 'panel', items: [ { xtype: 'textfield', itemId: 'emailField' } ] }
// later, from the panel:
var field = panel.down('#emailField'); // '#' targets an itemId, scoped to this panel
```

*What just happened:* `itemId` is the leak-free cousin of `id` - two copies of this panel can
each have their own `emailField` without conflict, because the lookup is relative to the
container, not the whole page.

### The right way: `reference` + `lookupReference`

In a modern MVVM app (Phase 7), you tag a child with **`reference`** and look it up from the
view's **ViewController** with **`lookupReference`** (or `view.lookup` in newer versions):

```javascript
// In the view:
{ xtype: 'textfield', reference: 'emailField' }

// In the ViewController:
onSaveClick: function () {
    var field = this.lookupReference('emailField'); // newer: this.lookup('emailField')
    console.log(field.getValue());
}
```

*What just happened:* `reference` names a child *within its view*, and the ViewController
resolves it locally. No global namespace, no collisions, and the wiring lives right next to
the logic that uses it. When you see `reference` in a view and `lookup`/`lookupReference` in
a controller, they're two ends of the same string.

### Walking the tree: `up()` and `down()`

Often you already have *one* component (the button that was clicked) and need its
neighbor - walk the tree relative to where you are:

- **`cmp.up('selector')`** - the nearest **ancestor** that matches.
- **`cmp.down('selector')`** - the first **descendant** that matches.

```javascript
onDeleteClick: function (button) {
    var grid = button.up('grid');                    // nearest grid ancestor of the button
    var emailField = grid.up('panel')               // hop up to the surrounding panel...
                         .down('textfield[name=email]'); // ...then down to a field in it
}
```

*What just happened:* `up('grid')` climbs from the button toward the root until it hits a
grid - no id needed, just "the grid I live inside." `down('textfield[name=email]')`
descends to the first text field whose `name` is `email`. This is how event handlers find
their context: start from what you were handed, navigate by relationship.

### Querying anywhere: `Ext.ComponentQuery`

The selectors above (`'grid'`, `'#emailField'`, `'textfield[name=email]'`) are **component
queries** - CSS-like selectors matching over **xtypes and component attributes** instead of
HTML tags and classes. `up`/`down` run them relative to a component; `Ext.ComponentQuery.query`
runs one globally:

```javascript
Ext.ComponentQuery.query('grid');                  // every grid in the app
Ext.ComponentQuery.query('panel > grid');          // grids that are direct children of a panel
Ext.ComponentQuery.query('textfield[name=email]'); // all email fields anywhere
```

*What just happened:* same selector grammar as `up`/`down`, returning an **array** of every
match across the whole component tree. `xtype` is the "tag", `[attr=value]` filters by config,
`>` means direct child - read it like CSS for components. In a strange codebase this is your
flashlight: query for the component you can see on screen, inspect what comes back, and
you've found where it lives in the tree.

💡 The practical hierarchy of "how do I get a component," best first: **`reference` +
`lookupReference`** (or a relative `up`/`down`) for everyday view logic; **`itemId` +
`down('#...')`** when you need a stable local handle; **`Ext.ComponentQuery`** for ad-hoc
spelunking and debugging; and **`Ext.getCmp` only** when you're reading old code that already
uses it - never as the way you write new code.

## Recap

- **Your UI is a tree of components**, and `items` is the only nesting you write - the
  framework instantiates the tree and renders the real DOM. Reading an Ext JS app means
  reading its containment tree.
- **Component → Container → Panel** is a ladder: every visible thing is a `Component`; a
  `Container` adds child `items` and a layout; a `Panel` adds a header/title/border. Grids,
  forms, and windows extend Panel.
- The Classic **lifecycle** is `constructor` → `initComponent` → `render` → `afterRender` →
  `destroy`; override `initComponent` to set up config and always call `callParent`.
- **`hide()` ≠ `destroy()`** - hidden components keep their DOM, listeners, and bindings
  alive. Destroy what you're done with, or leak memory.
- Containers change at runtime with **`add`**, **`remove`**, and **`removeAll`**.
- **Find components the right way:** prefer `reference` + `lookupReference` and relative
  `up()`/`down()`; use `itemId` for safe local handles and `Ext.ComponentQuery` for
  exploration. Treat `Ext.getCmp` and global `id` as a legacy anti-pattern.

## Quick check

Lock in how the tree fits together and how to navigate it:

```quiz
[
  {
    "q": "What is the difference between Ext.container.Container and Ext.panel.Panel?",
    "choices": [
      "Container renders HTML; Panel does not",
      "A Container holds child components via items; a Panel is a Container that also adds a header/title and border",
      "Panel is the base class that Container extends",
      "They are aliases for the same class"
    ],
    "answer": 1,
    "explain": "Container adds the items machinery to Component; Panel is a Container with chrome (header, title, border). Grids and forms extend Panel."
  },
  {
    "q": "You call cmp.hide() on a window and reopen a fresh one each time the user clicks a button, never destroying the old ones. What happens?",
    "choices": [
      "Nothing - hide() fully frees the component",
      "Ext JS automatically garbage-collects hidden components",
      "The old instances stay alive with their DOM and listeners, leaking memory",
      "The new window reuses the hidden one automatically"
    ],
    "answer": 2,
    "explain": "hide() only makes a component invisible. Its DOM, listeners, and bindings stay alive until you destroy() it - so repeatedly hiding instead of destroying leaks."
  },
  {
    "q": "Which is the recommended way to get a child component in modern Ext JS, and which is the anti-pattern to avoid?",
    "choices": [
      "Recommended: Ext.getCmp('id'); avoid: reference + lookupReference",
      "Recommended: reference + lookupReference (or up()/down()); avoid: Ext.getCmp with a global id",
      "Recommended: document.querySelector; avoid: Ext.ComponentQuery",
      "Both Ext.getCmp and reference are equally fine"
    ],
    "answer": 1,
    "explain": "Global ids must be unique forever and collide on reuse. Prefer reference + lookupReference (or relative up()/down()); Ext.getCmp is a legacy anti-pattern."
  }
]
```


---

# Layouts: How Things Get Positioned

Here's the one idea that, once it lands, makes Ext JS layouts stop fighting you: **you do not size things with CSS. The parent's `layout` sizes its children.**

Coming from the web, that's backwards. Normally you slap `width: 50%` on a div and you're done. In Ext JS, you can set `width` and `height` on a component and still watch it render at zero pixels - or vanish entirely - because a *container* doesn't ask its children how big they want to be. It runs its **layout**, and the layout decides each child's box. The child is a passenger; the parent's `layout` drives.

> 💡 Every layout below is just a different *strategy a parent uses to size and place its `items`.* `fit` means "one child, fill me." `hbox` means "lay children left to right." `border` means "children claim edges." Same job, different rule.

Every container has a `layout` config. Skip it and you get the default - `auto` - which is exactly where beginners get burned.

## The default `auto` layout (and why it disappoints)

A container with no `layout` set uses `layout: 'auto'`, which does almost nothing: it stacks children as plain block-level elements, top to bottom, each taking its natural content height with **no managed width or height at all**.

```javascript
Ext.create('Ext.panel.Panel', {
    renderTo: Ext.getBody(),
    title: 'Users',
    height: 400,
    // no layout specified -> 'auto'
    items: [
        { xtype: 'grid', /* ...columns, store... */ },
        { xtype: 'form', /* ...fields... */ }
    ]
});
```

*What just happened:* the panel is 400px tall, but `auto` doesn't divide that 400px between the grid and the form - it drops them in as unsized blocks. The grid, having no managed height, often collapses to almost nothing, leaving an empty panel with no obvious cause. Nothing is broken; the parent never told the grid how tall to be. This single misunderstanding is behind most "my Ext JS screen is blank" panic.

The fix is always the same shape: **pick a layout that actually sizes the children.**

## `fit` - one child, fill the box

`fit` is the simplest useful layout. The container has **exactly one child**, and that child is stretched to fill the container completely - full width, full height.

```javascript
Ext.create('Ext.panel.Panel', {
    renderTo: Ext.getBody(),
    title: 'Users',
    height: 400,
    width: 600,
    layout: 'fit',
    items: [
        { xtype: 'grid', /* columns, store */ }
    ]
});
```

*What just happened:* the grid now fills the entire 600×400 panel because `fit` told it to. This is the canonical pattern for "a panel that wraps a single grid," and you'll see it constantly in legacy code as a window whose only job is to frame one component.

> ⚠️ `fit` is built for **one** child. If you give it multiple `items`, only the **first** one shows - the rest are still created but laid out on top / hidden behind it. If you put two things in a `fit` container and the second vanished, that's not a bug, that's `fit` doing exactly what it says. You wanted `hbox`/`vbox` or `card`.

## `hbox` / `vbox` - rows and columns with `flex`

The box layouts are your workhorses. `hbox` lays children out in a **horizontal row**; `vbox` lays them in a **vertical column**. The magic ingredient is **`flex`**: a number on each child that says how to divide the *available space along the main axis* proportionally.

```javascript
Ext.create('Ext.panel.Panel', {
    renderTo: Ext.getBody(),
    title: 'Users',
    height: 400,
    width: 800,
    layout: {
        type: 'hbox',
        align: 'stretch'   // stretch children on the cross-axis (full height)
    },
    items: [
        { xtype: 'grid', flex: 2 /* ...columns, store... */ },
        { xtype: 'form', flex: 1 /* ...fields... */ }
    ]
});
```

*What just happened:* `hbox` puts the grid and form side by side. `flex: 2` and `flex: 1` split the horizontal space two-to-one, so the grid gets twice the width of the form. `align: 'stretch'` is the part beginners forget - it stretches both children to the **full height** of the panel on the cross-axis. Without it, each child is only as tall as its own content, and the grid could collapse again. For `vbox`, swap the roles: `flex` divides the *height*, and `align: 'stretch'` gives children full *width*.

Two more knobs worth knowing:

- **`flex`** vs fixed size: mix them freely. A child with a fixed `width` (in `hbox`) keeps that width, and the `flex` children share whatever's left over.
- **`pack` and `align`**: `pack` controls distribution along the main axis (`'start'`, `'center'`, `'end'`) - handy for right-aligning toolbar buttons. `align` controls the cross-axis (`'stretch'`, `'top'`/`'left'`, `'middle'`/`'center'`, `'bottom'`).

> 💡 Mental shortcut: in `hbox`, `flex` is about *width* and `align` is about *height*. In `vbox`, flip them. The "main axis" is the direction the box lays children out; the "cross axis" is the other one.

## `border` - the classic app shell

`border` is the layout that screams "this is an Ext JS app." Children declare a **`region`** - one of `'north'`, `'south'`, `'east'`, `'west'`, or `'center'` - and the layout pins them to the edges of the container. `center` soaks up whatever space is left.

```mermaid
flowchart TB
  subgraph BORDER[border layout]
    N[north - toolbar / header]
    subgraph MID[ ]
      direction LR
      W[west - nav] --- C[center - takes the rest]
    end
    S[south - status bar]
  end
  N --> MID --> S
```

Here's the users admin shell - a west nav and a center work area:

```javascript
Ext.create('Ext.container.Viewport', {
    layout: 'border',
    items: [
        {
            xtype: 'panel',
            region: 'west',
            title: 'Navigation',
            width: 220,
            collapsible: true,
            split: true,         // draggable splitter between west and center
            html: 'Users · Roles · Settings'
        },
        {
            xtype: 'panel',
            region: 'center',    // REQUIRED - takes all remaining space
            title: 'Users',
            layout: 'fit',
            items: [ { xtype: 'grid' /* columns, store */ } ]
        }
    ]
});
```

*What just happened:* the `Viewport` fills the whole browser window, and `border` carves it up. The west nav gets a fixed `width: 220`, `collapsible: true` gives it a collapse tool, and `split: true` adds a draggable splitter so the user can resize it. The center region claims everything that's left and, via its own `layout: 'fit'`, hands all that space to the grid. North/south regions take a fixed `height`; east/west take a fixed `width`; center never gets a size from you - it just absorbs the remainder.

> ⚠️ **A `border` layout must have exactly one `center` region.** This is non-negotiable - leave out `center` and Ext JS throws an error (older versions) or renders a broken, empty shell. If a `border` screen is blank, check for a `center` first. You can have multiple north/south/east/west regions in some setups, but **one and only one** center, always.

## `card` - show one child at a time

`card` stacks all its children in the same space but shows **only one at a time** - think wizards, step-by-step flows, or the body of a tab panel. Switch which child is visible with `setActiveItem`.

```javascript
var wizard = Ext.create('Ext.panel.Panel', {
    renderTo: Ext.getBody(),
    width: 500,
    height: 300,
    layout: 'card',
    activeItem: 0,   // start on the first card
    items: [
        { xtype: 'form', title: 'Step 1: Account' },
        { xtype: 'form', title: 'Step 2: Profile' },
        { xtype: 'form', title: 'Step 3: Confirm' }
    ]
});

// later, on a "Next" button:
wizard.getLayout().setActiveItem(1);
```

*What just happened:* all three forms exist, but only card 0 is visible at first. `setActiveItem(1)` swaps the display to the second form - no re-creation, just a visibility switch. You've likely used this without knowing it: **`Ext.tab.Panel` uses a `card` layout under the hood**, and clicking a tab is just `setActiveItem` for that tab's body.

## `anchor` - sizing relative to the container

`anchor` sizes children as a percentage (or offset) of the container. You'll meet it in older code:

```javascript
{
    xtype: 'panel',
    layout: 'anchor',
    items: [
        { xtype: 'textfield', anchor: '100%' },     // full width
        { xtype: 'grid',      anchor: '100% 50%' }  // full width, half height
    ]
}
```

*What just happened:* `anchor: '100%'` makes the textfield span the container's full width; `anchor: '100% 50%'` gives the grid full width and half the container's height. It works, but it's the older approach - **box layouts (`hbox`/`vbox`) are usually preferred** for new work because `flex` handles proportional sizing more cleanly. Recognize `anchor` in legacy screens; reach for box layouts when writing fresh ones.

## Layouts nest - and that's the whole trick

No single layout builds a real screen - real screens are layouts inside layouts. The users admin is the textbook case: a `border` viewport whose **center region is itself a `vbox`** holding the grid above the form.

```javascript
Ext.create('Ext.container.Viewport', {
    layout: 'border',
    items: [
        {
            xtype: 'panel',
            region: 'west',
            title: 'Navigation',
            width: 220,
            split: true,
            collapsible: true
        },
        {
            xtype: 'panel',
            region: 'center',
            title: 'Users',
            layout: { type: 'vbox', align: 'stretch' },  // nested layout
            items: [
                { xtype: 'grid', flex: 1 /* the users list */ },
                { xtype: 'form', height: 180 /* edit selected user */ }
            ]
        }
    ]
});
```

*What just happened:* the outer `border` handles the app shell (nav on the left, work area filling the rest). The center region then runs *its own* `vbox`: the grid gets `flex: 1` so it grows to fill the leftover vertical space, while the form keeps a fixed `height: 180` at the bottom. `align: 'stretch'` makes both span the full width of the center region. Each container only worries about its own children - nest them and arbitrarily complex screens fall out of simple rules.

## ⚠️ "Nothing shows up" - the troubleshooting section

> 💡 **If a component is invisible or zero-size, suspect the PARENT'S layout first - not the component.** Nine times out of ten the child is fine; the parent never gave it a box.

When something won't render, walk this checklist in order:

1. **Is the parent on `auto` layout?** No `layout` config means `auto`, which doesn't size children - a grid or panel with no managed height collapses to nothing. Give the parent a real layout (`fit`, `vbox`, `border`...).
2. **Is there a `border` layout with no `center`?** Missing `center` breaks the whole container. Add exactly one `center` region.
3. **Is a child missing its `region`?** In a `border` layout, every child needs a `region`, or it won't lay out.
4. **Box layout with no size on the cross-axis?** In `hbox`/`vbox`, children with no `flex` and no fixed size on the main axis get zero, and without `align: 'stretch'` they get zero on the cross-axis too. Add `flex`, a fixed `width`/`height`, or `align: 'stretch'`.

> ⚠️ A telltale sign: the component shows up in the DOM and in the component tree (you can find it), but it's 0px tall or 0px wide. That is *always* a layout problem, never a "the component is broken" problem. Stop inspecting the child's config - go look at how its parent lays things out.

## Recap

- **The parent's `layout` sizes its children - not CSS.** This is the core mental model; setting `width`/`height` on a child often does nothing because the layout overrides it.
- The default **`auto`** layout barely sizes anything, which is why unconfigured panels look empty. Pick a real layout.
- **`fit`** = one child fills the box; **`hbox`/`vbox`** = rows/columns sized by **`flex`** (and `align: 'stretch'` on the cross-axis); **`border`** = edge `region`s with exactly one required **`center`**; **`card`** = one child visible at a time via `setActiveItem`.
- Layouts **nest** - a `border` center region can hold a `vbox`, and that's how real screens (like the users admin) get built.
- When **"nothing shows up,"** check the **parent's layout** first: `auto` layout, a missing `center`, a missing `region`, or a box child with no size are the usual culprits.

## Quick check

Test what sizes a child, and how to read a blank screen:

```quiz
[
  {
    "q": "You put a grid in a panel, give the grid height: 300, and it still renders at zero height. What's the most likely cause?",
    "choices": ["The grid's store is empty", "The parent panel's layout (probably 'auto') isn't sizing the grid", "You forgot renderTo", "Grids can't have a fixed height"],
    "answer": 1,
    "explain": "In Ext JS the parent's layout sizes children. An 'auto' layout doesn't manage height, so the child collapses no matter what height you set on it."
  },
  {
    "q": "Which statement about the border layout is correct?",
    "choices": ["You must have exactly one 'center' region", "The 'center' region needs a fixed width", "You can have at most one 'west' region and no 'center'", "Regions are optional and default to 'north'"],
    "answer": 0,
    "explain": "A border layout requires exactly one center region; it absorbs the space left after the edge regions take their fixed sizes."
  },
  {
    "q": "In an hbox layout, what does flex: 2 on one child and flex: 1 on another do?",
    "choices": ["Stretches both to full height", "Splits the available WIDTH two-to-one between them", "Splits the available HEIGHT two-to-one", "Shows only the first child"],
    "answer": 1,
    "explain": "In hbox, flex divides the available space along the main (horizontal) axis, so flex 2 vs 1 gives the first child twice the width. Cross-axis height is controlled by align: 'stretch'."
  }
]
```


---

# The Data Package

[The last phase](04-layouts.md) got components to *show up* in the right places. But an empty
grid with three columns and no rows isn't an app - it's a picture of one. Every Ext JS screen
you'll ever inherit is, underneath the chrome, a pipe that pulls rows off a server and pushes
edits back. That pipe is the **data package**, and it's the single biggest source of "why is my
grid empty?" and "why didn't my save go through?" tickets in legacy code.

💡 **The mental model - four pieces, one sentence: a *Model* is the shape of one record, a *Store*
is the collection of records, a *proxy* is where they come from and go back to, and a *reader*
is how the server's response gets parsed into records.** That quartet is the heart of every Ext JS
app. Internalize it and the rest of this phase is just syntax.

Here's the pipe, drawn out - server data flows left to right until it lands in a grid you can see:

```mermaid
flowchart LR
    S[(Server)] --> P[proxy: makes the request]
    P --> R[reader: parses the response]
    R --> ST[Store: holds the records]
    ST --> G[Grid: renders the rows]
```

*What just happened:* the proxy talks to the server. The reader takes whatever comes back (usually
JSON) and turns it into **Model instances**. The Store collects those instances. The grid binds to
the Store and paints a row per record. When a grid is empty, the bug is somewhere on this line.

📝 The proxy is the only piece here that knows about HTTP. If you're shaky on URLs, GET vs POST, or
what a JSON response body looks like, skim
[HTTP & JSON API basics](/guides/http-and-json-api-basics) first - the data package sits directly
on top of those ideas.

## The Model: the shape of one record

A **`Ext.data.Model`** describes what one record looks like - its fields and their types. It's the
schema for a single row. Here's the `User` model we'll use for the rest of this phase:

```javascript
Ext.define('MyApp.model.User', {
    extend: 'Ext.data.Model',
    fields: [
        { name: 'id',     type: 'int' },
        { name: 'name' },                       // type defaults to 'auto' (string-ish)
        { name: 'email' },
        { name: 'active', type: 'boolean' }
    ]
});
```

*What just happened:* we declared a class with `Ext.define` (the same class system from
[Phase 2](02-the-class-system.md)) that `extend`s `Ext.data.Model`. The `fields` array names each
property a `User` record carries and, optionally, its `type` - `int`, `boolean`, `string`, `date`,
`float`. The type matters: a field declared `type: 'int'` coerces the server's `"42"` string into
a real number `42` on the way in, so your renderers and comparisons behave. A `User` instance
isn't a plain object - it's a record with methods (`get`, `set`, dirty tracking) we'll use shortly.

💡 Models can do more than fields: they can declare **validators**, and **associations**
(`hasMany` / `belongsTo`) so a `User` can reach its `Orders`, and a model can even carry its own
`proxy` so it knows how to load and save itself. You'll see all of these in older codebases; for
now, fields are enough to get rows on screen.

## The Store: the collection of records

A **`Ext.data.Store`** is an in-memory collection of Model instances - *the* data source you bind
to a grid, a combo box, a tree, or a list. Create one, point it at a model, give it a proxy that
knows where the data lives, and tell it to load:

```javascript
var usersStore = Ext.create('Ext.data.Store', {
    model: 'MyApp.model.User',
    proxy: {
        type: 'ajax',                  // make an HTTP request to one url
        url: '/api/users',
        reader: {
            type: 'json',
            rootProperty: 'data',      // ⚠️ where the array of rows lives in the response
            totalProperty: 'total'     // total count, for paging
        }
    },
    autoLoad: true                     // fire the load() automatically on creation
});
```

*What just happened:* we built a store of `User` records. The **`proxy`** with `type: 'ajax'`
says "go GET `/api/users`." The **`reader`** with `type: 'json'` says "the body is JSON." The line
that bites everyone is **`rootProperty: 'data'`** - it tells the reader *where in the response the
array of rows is*. With `autoLoad: true`, the store fires its load the moment it's created; leave
it off and nothing happens until something calls `store.load()`.

For that reader config to work, the server has to return JSON shaped like this:

```javascript
{
    "total": 2,
    "data": [
        { "id": 1, "name": "Ada Lovelace",  "email": "ada@example.com",  "active": true  },
        { "id": 2, "name": "Alan Turing",   "email": "alan@example.com", "active": false }
    ]
}
```

*What just happened:* the reader looks at `rootProperty: 'data'`, finds that array, and builds one
`User` record per element - coercing `id` to int and `active` to boolean per the model. `total`
feeds paging. ⚠️ This is the #1 empty-grid bug: if the server actually returns `{ "users": [...] }`
or a bare top-level array, your `rootProperty: 'data'` finds nothing and the grid stays empty with
no error. When a grid is blank, open the network tab, check the real response body, and confirm
`rootProperty` matches the key the array actually sits under.

### Picking a proxy

The proxy is *where* the store reads and writes. Common ones:

- **`ajax`** - one `url`, fires HTTP requests; the everyday read-from-a-server proxy.
- **`rest`** - like `ajax`, but maps CRUD onto HTTP verbs on a REST url: GET to read, POST to
  create, PUT to update, DELETE to destroy. This is what you want when the store also *saves*.
- **`memory`** - data already on the page (a JS array), no server. Great for static lookups.
- **`localstorage`** - persists records in the browser's local storage.

Every proxy has a **`reader`** (parse responses coming in) and, for the writing proxies, a
**`writer`** (serialize records going out).

## Loading is asynchronous - the bug everyone hits once

The trap that has cost more Ext JS developers an afternoon than anything else on this page:
`store.load()` kicks off a network request and **returns immediately** - the records are *not*
there on the next line. The data shows up later, when the response arrives.

```javascript
usersStore.load();
console.log(usersStore.getCount()); // ⚠️ logs 0 - the request hasn't come back yet!
```

*What just happened:* `load()` started the request and moved on. `getCount()` ran microseconds
later, long before the server replied, so it sees an empty store - not a bug, just asynchrony. Any
code that needs the loaded records must wait for the load to finish. Use the **`callback`**:

```javascript
usersStore.load({
    callback: function (records, operation, success) {
        if (success) {
            console.log('Loaded', records.length, 'users'); // now they're really here
        } else {
            console.warn('Load failed:', operation.getError());
        }
    }
});
```

*What just happened:* the `callback` runs *after* the response is parsed into records. `records` is
the array that just arrived, `operation` carries status and errors, and `success` is the boolean
you should always check before trusting the data. Any logic that depends on loaded rows belongs
inside this callback (or in a `load` event listener) - never on the line right after `load()`.

💡 Stores are **observable**: besides the load callback, they fire events you can listen to - 
`load` (data arrived), `datachanged` (the collection changed), `update` (a record was edited),
`add`, and `remove`. In MVVM code ([Phase 7](07-mvvm-and-binding.md)) you mostly let *binding*
react to these for you, but in older MVC code you'll see explicit `store.on('load', ...)` handlers
everywhere.

## Working with records once they're loaded

A loaded store isn't read-only - you read and write records through it, and those edits get
tracked so you can push them back to the server later. The everyday store methods:

```javascript
usersStore.getCount();                       // how many records
usersStore.getAt(0);                         // the record at index 0
usersStore.each(function (rec) { /* ... */ });   // iterate all records
var ada = usersStore.findRecord('email', 'ada@example.com'); // find by field value
```

*What just happened:* these are the read operations you'll reach for constantly when navigating an
inherited screen - count the rows, grab one by position, loop them, or look one up by a field. None
touch the server; they work on records already in memory.

Now the writing side. A record exposes `get` and `set`, and **`set` marks the record dirty** - 
Ext JS remembers it was changed but hasn't persisted yet:

```javascript
var rec = usersStore.getAt(0);
console.log(rec.get('name'));    // 'Ada Lovelace' - read a field
rec.set('name', 'Ada King');     // edit it - the record is now DIRTY
console.log(rec.dirty);          // true - pending, not yet saved to the server
```

*What just happened:* `get('name')` reads a field; `set('name', ...)` changes it and flips the
record's **dirty** flag to `true`. Dirty means "edited in memory, not yet on the server." This
tracking is the whole point - it lets Ext JS know exactly which records need saving, so it can
send only the changes instead of re-uploading everything.

Adding and removing records works at the store level, and those also count as pending changes:

```javascript
usersStore.add({ name: 'Grace Hopper', email: 'grace@example.com', active: true }); // pending create
usersStore.remove(rec);                                                              // pending destroy
```

*What just happened:* `add` appends a new record (a pending *create*) and `remove` pulls one out (a
pending *destroy*). Like `set`, these stage changes in memory - nothing has hit the server yet. The
grid bound to this store updates instantly, reacting to `add`/`remove` events, but the backend
doesn't know a thing until you sync.

## Pushing changes back: `record.save()` and `store.sync()`

Staged edits become persisted edits through the proxy's **writer**. Two ways to fire it:

```javascript
rec.save();          // save THIS one record through its proxy

usersStore.sync();   // push ALL pending creates/updates/destroys in one batch
```

*What just happened:* `record.save()` persists a single record. `store.sync()` is the workhorse: it
walks every dirty/added/removed record in the store and sends them through the proxy and writer - 
creates as POSTs, updates as PUTs, deletes as DELETEs (with a `rest` proxy). After a successful
sync, the records are no longer dirty - the same proxy that *read* the data is what *writes* it
back.

For sync to actually save, the store needs a writing proxy. Swap the `ajax` proxy for a `rest` one
so CRUD maps cleanly onto HTTP verbs:

```javascript
var usersStore = Ext.create('Ext.data.Store', {
    model: 'MyApp.model.User',
    proxy: {
        type: 'rest',                  // CRUD -> GET / POST / PUT / DELETE
        url: '/api/users',
        reader: { type: 'json', rootProperty: 'data', totalProperty: 'total' }
    },
    autoLoad: true
});

// ...user edits a row in the grid, then clicks Save:
usersStore.sync({
    success: function () { console.log('All pending changes saved.'); },
    failure: function () { console.warn('Sync failed - records stay dirty.'); }
});
```

*What just happened:* with a `rest` proxy, `sync()` translates each pending change into the right
verb against `/api/users`: a new record POSTs, an edited record PUTs to `/api/users/{id}`, a removed
record DELETEs. The `success`/`failure` handlers let you confirm the round trip - and on failure
the records *stay dirty*, so nothing is silently lost and a retry will resend them. If REST
conventions (verbs, status codes, resource URLs) are fuzzy, [REST APIs explained](/guides/rest-apis-explained)
is the companion read; the `rest` proxy is essentially a REST client wired straight into your grid.

⚠️ One more legacy gotcha: dirty changes live only in the browser. If the user edits five rows and
the page reloads before a `sync()` (or the sync fails and nobody retries), those edits are gone.
When you inherit a "my changes didn't save" screen, check that *something* actually calls `sync()`
or `save()` - a surprising amount of legacy code stages edits and never sends them.

## Recap

- The **data package** is the pipe from server to screen, and it's four pieces: **Model** (shape of
  one record), **Store** (collection of records), **proxy** (where data comes from / goes to), and
  **reader** (how the response is parsed). Memorize the quartet - it explains every store you'll meet.
- A **Model** (`Ext.define` + `extend: 'Ext.data.Model'`) declares `fields` with types that coerce
  incoming values; it can also carry validators, associations, and its own proxy.
- A **Store** binds a model to a proxy + reader. **`rootProperty`** tells the JSON reader where the
  array of rows lives - a mismatch here is the classic empty-grid bug.
- **`store.load()` is asynchronous**: records aren't available on the next line. Use the `callback`
  (or the `load` event) for anything that depends on loaded data, and check `success`.
- **`record.set()` marks a record dirty**; `store.add`/`remove` stage pending changes. Nothing
  reaches the server until **`record.save()`** or **`store.sync()`** pushes it through the proxy's
  writer - use a `rest` proxy to map CRUD onto HTTP verbs.

## Quick check

Lock in where rows come from, and why edits do or don't save:

```quiz
[
  {
    "q": "In the data package, what is the job of each piece?",
    "choices": [
      "Model loads data, Store renders it, proxy validates it, reader caches it",
      "Model is the shape of one record, Store is the collection of records, proxy is where data comes from / goes to, reader parses the response",
      "Model and Store are the same thing; proxy and reader are optional",
      "Store defines fields, Model holds the rows, proxy renders the grid"
    ],
    "answer": 1,
    "explain": "Model = shape of one record, Store = collection of records, proxy = transport (where it comes from / goes to), reader = how the response is parsed into records."
  },
  {
    "q": "You call usersStore.load() and on the very next line read usersStore.getCount(). It returns 0 even though the server has rows. Why?",
    "choices": [
      "The store model is misconfigured",
      "load() is asynchronous - the request hasn't returned yet, so the records aren't there on the next line",
      "getCount() only counts dirty records",
      "autoLoad must be true for getCount() to work"
    ],
    "answer": 1,
    "explain": "load() fires the request and returns immediately. The records arrive later, so code that needs them must run in the load callback or a load event handler."
  },
  {
    "q": "A user edits two grid rows (rec.set(...)) but the changes never reach the server. What is most likely missing?",
    "choices": [
      "A call to store.sync() (or record.save()) to push the pending dirty changes through a writing proxy",
      "A second reader on the store",
      "rootProperty set to 'data'",
      "autoLoad set to false"
    ],
    "answer": 0,
    "explain": "set() only marks records dirty in memory. Nothing persists until store.sync() or record.save() sends the staged creates/updates/destroys through the proxy's writer (e.g. a rest proxy)."
  }
]
```


---

# The Grid & Forms

The mental model that dissolves most of the apparent complexity: **the Grid is just a store rendered as rows, and the form is just fields over one record.** Nothing more exotic than that.

The Grid (`Ext.grid.Panel`) is Ext JS's flagship - for a lot of enterprises it's *the reason they chose Ext JS in the first place*. Sortable, editable, paged, filterable data tables that would take weeks to hand-roll, declared in a config object. But under that power it's doing something simple: it takes the `store` you built in [the data package](05-the-data-package.md), walks its records, and paints one row per record. Change the store, the grid updates - the grid is a *view* of the store.

The form is the same idea aimed at a single record instead of a collection. A grid shows you *all* the users; a form shows you *one* user's fields to edit. Both are windows onto the data package - the grid is the list, the form is the detail.

> 💡 If you remember one thing: a grid is bound to a **store** (many records), a form is bound to a **record** (one). Selecting a row in the grid and loading it into the form is just handing one record from the collection over to the detail view. That single move is the spine of nearly every admin screen ever built in Ext JS.

We'll build the users admin from [phase 4](04-layouts.md)'s shell for real: a grid of users on top, a form to edit the selected one below.

## The grid: a store with columns

A grid needs two things: a `store` (where the data lives) and `columns` (how to show it). You already have the store from phase 5. The columns are new - each one maps to a field on your model via `dataIndex`.

```javascript
Ext.create('Ext.grid.Panel', {
    renderTo: Ext.getBody(),
    title: 'Users',
    height: 400,
    width: 700,
    store: usersStore,          // the Ext.data.Store from phase 5
    columns: [
        { text: 'Name',  dataIndex: 'name',  flex: 1 },
        { text: 'Email', dataIndex: 'email', width: 240 },
        { text: 'Active', dataIndex: 'active', width: 80 }
    ]
});
```

*What just happened:* the grid asked `usersStore` for its records and drew one row per record. Each column's `text` is the header you see; its `dataIndex` is the **model field** it reads from each record - `dataIndex: 'name'` pulls the `name` field out of every user and stacks those values down the Name column. `flex: 1` lets the Name column grow to fill leftover width (same `flex` from `hbox` in phase 4 - columns size the same way), while `width: 240` pins Email to a fixed size. Click a header and the grid sorts the store by that field for free.

So far the `active` column shows raw `true`/`false`. That's what renderers are for.

## Renderers: formatting a cell

A `renderer` is a function on a column that turns the raw field value into what the user actually sees: booleans become "Yes/No", numbers become currency, statuses become badges - all without touching the underlying data.

```javascript
columns: [
    { text: 'Name',  dataIndex: 'name',  flex: 1 },
    { text: 'Email', dataIndex: 'email', width: 240 },
    {
        text: 'Active',
        dataIndex: 'active',
        width: 80,
        renderer: function (value, meta, record) {
            return value ? 'Yes' : 'No';
        }
    }
]
```

*What just happened:* for every cell in the Active column, the grid called your `renderer` with three things: `value` (the raw `active` field - `true` or `false`), `meta` (cell metadata you can tweak, e.g. `meta.tdCls` to add a CSS class), and `record` (the whole user model instance, in case you need *another* field to decide). You returned a string, and that string is what got painted. The stored value is still a real boolean; only the *display* changed.

> ⚠️ Renderers run a **lot** - once per visible cell, and again on every scroll, sort, filter, and refresh. Keep them cheap: no DOM lookups, no network calls, no heavy formatting inside a renderer. Real work in there will hurt on a grid with thousands of rows - precompute on the model or cache outside the function.

## Selection: knowing which row the user picked

The grid tracks selection for you. The `selModel` (or its shorthand `selType`) decides *how* the user selects - `'rowmodel'` (the default: click a row to select it) or `'checkboxmodel'` (a checkbox column for multi-select).

```javascript
Ext.create('Ext.grid.Panel', {
    title: 'Users',
    store: usersStore,
    selType: 'rowmodel',          // default; 'checkboxmodel' adds checkboxes
    columns: [ /* ... */ ],
    listeners: {
        selectionchange: function (selModel, selected) {
            var record = selected[0];       // the chosen user, or undefined
            if (record) {
                console.log('Picked:', record.get('name'));
            }
        }
    }
});
```

*What just happened:* `selType: 'rowmodel'` means a click selects a whole row. The `selectionchange` event fires whenever the selection changes, handing you the array of selected records. We grabbed the first one - for single selection that's your picked user. You can also ask the grid directly any time with `grid.getSelection()` (returns an array). This event is the trigger we'll use later to load the selected user into the edit form.

## Editing in the grid: plugins + editor columns

Grids can be editable in place, but editing isn't built into the base grid - it comes from a **plugin**. Two options:

- **`Ext.grid.plugin.CellEditing`** (`ptype: 'cellediting'`) - double-click a single cell to edit just that cell.
- **`Ext.grid.plugin.RowEditing`** (`ptype: 'rowediting'`) - double-click a row to edit the whole row at once, with Update/Cancel buttons.

You add the plugin to the grid, then give each editable column an `editor` (the field component used while editing).

```javascript
Ext.create('Ext.grid.Panel', {
    title: 'Users',
    store: usersStore,
    plugins: [{ ptype: 'cellediting', clicksToEdit: 2 }],
    columns: [
        {
            text: 'Name', dataIndex: 'name', flex: 1,
            editor: { xtype: 'textfield', allowBlank: false }
        },
        {
            text: 'Email', dataIndex: 'email', width: 240,
            editor: { xtype: 'textfield', vtype: 'email' }
        },
        { text: 'Active', dataIndex: 'active', width: 80 }   // no editor = read-only
    ]
});
```

*What just happened:* the `cellediting` plugin made cells editable on double-click (`clicksToEdit: 2`). Each column with an `editor` swaps in that field when you edit - Name becomes a `textfield` that refuses to be blank (`allowBlank: false`), Email a `textfield` that validates as an email (`vtype: 'email'`). The Active column has no `editor`, so it stays read-only. When you commit an edit, the plugin writes the new value back into the record and the store marks that record **dirty**.

This connects straight back to phase 5: editing only changes the record *in memory*. To push those changes to the server, call `sync` on the store.

```javascript
usersStore.sync({
    success: function () { console.log('Saved.'); },
    failure: function () { console.log('Save failed.'); }
});
```

*What just happened:* `store.sync()` looked at every dirty (modified), newly created, and removed record, and sent them to the server through the store's proxy - the same proxy/writer machinery from phase 5. Edit a cell, then `sync()`, and the change persists. No sync, and your edit lives only in the browser until a refresh wipes it. This is the most common "why didn't my change save?" gotcha: editing and persisting are two separate steps.

## Paging: don't load 50,000 rows at once

When a store has more records than you want on screen, a **paging toolbar** (`pagingtoolbar`) docks at the bottom and walks through pages, binding to the same store and driving the proxy's paging params.

```javascript
Ext.create('Ext.grid.Panel', {
    title: 'Users',
    store: usersStore,         // proxy reader has totalProperty set (phase 5)
    columns: [ /* ... */ ],
    bbar: {
        xtype: 'pagingtoolbar',
        store: usersStore,
        displayInfo: true      // shows "Displaying 1 - 25 of 1,043"
    }
});
```

*What just happened:* the `bbar` (bottom toolbar) holds a `pagingtoolbar` bound to the store. When you click Next, the toolbar asks the store to load the next page; the store's proxy sends `page`, `start`, and `limit` params to the server, and the reader uses `totalProperty` from the response to know how many records exist in total (so it can compute the page count and enable/disable the arrows). The grid only ever holds one page of rows in memory - that's the whole point.

> 💡 For genuinely huge datasets (hundreds of thousands of rows) where you want a single scrollbar instead of page buttons, Ext JS offers **buffered / infinite grids** - the store loads pages on demand as you scroll and discards rows that scroll out of view. Same store/proxy foundation, different rendering strategy, and more finicky to set up - reach for it only when paging genuinely isn't enough.

A few more column features you'll bump into in legacy grids, worth recognizing:

- **Filtering** - the `Ext.grid.filters.Filters` plugin adds per-column filter menus in the headers.
- **Grouping** - the `Ext.grid.feature.Grouping` feature collapses rows into labeled groups by a field.
- **Locked columns** - `locked: true` on a column freezes it on the left while the rest scroll horizontally.

## The form: fields over one record

Now the detail half. `Ext.form.Panel` (xtype `'form'`) is a container whose children are **field** components - `textfield`, `numberfield`, `combobox`, `datefield`, `checkbox`, and friends. Each field has a `fieldLabel` (the label beside it) and a `name` (the key it reads/writes under).

```javascript
Ext.create('Ext.form.Panel', {
    renderTo: Ext.getBody(),
    title: 'Edit User',
    width: 400,
    bodyPadding: 10,
    defaults: { anchor: '100%' },   // make every field full width
    items: [
        { xtype: 'textfield',   fieldLabel: 'Name',  name: 'name',  allowBlank: false },
        { xtype: 'textfield',   fieldLabel: 'Email', name: 'email', vtype: 'email' },
        { xtype: 'checkbox',    fieldLabel: 'Active', name: 'active' },
        {
            xtype: 'combobox',  fieldLabel: 'Role',  name: 'roleId',
            store: rolesStore,  displayField: 'name', valueField: 'id',
            queryMode: 'local'
        }
    ]
});
```

*What just happened:* the form rendered a labeled field per item. `fieldLabel` is what the user reads; `name` is the data key - `name: 'email'` means this field maps to the `email` value when the form reads or writes data. The `combobox` is itself bound to a **store** (`rolesStore`) - combos are mini-grids really: `displayField` is what the user sees in the dropdown, `valueField` is what actually gets stored. So the form, too, leans on the data package. `defaults: { anchor: '100%' }` applies that config to every child so you don't repeat it.

The form panel exposes its underlying engine as `basicForm` (reachable via `form.getForm()`) - that's the object that holds field values, validation state, and the load/submit machinery.

## Wiring it together: select → load → edit → save

The payoff: connecting the grid and the form into a working editor. The bridge is two methods: **`form.loadRecord(record)`** copies a record's values *into* the form's fields, and **`form.updateRecord(record)`** copies the edited field values *back into* the record.

```javascript
// when the grid selection changes, load that user into the form
usersGrid.on('selectionchange', function (selModel, selected) {
    var record = selected[0];
    if (record) {
        editForm.loadRecord(record);   // fields now show this user's values
    }
});

// when the user clicks Save on the form
function onSave() {
    if (!editForm.isValid()) {
        return;                        // a field failed validation; stop
    }
    var record = editForm.getRecord(); // the record we loaded earlier
    editForm.updateRecord(record);     // write edited values back into it
    usersStore.sync();                 // persist via the proxy (phase 5)
}
```

*What just happened:* selecting a row fires `selectionchange`; we take the chosen user and call `loadRecord`, which matches each field's `name` to a model field and fills the inputs - the form now mirrors that user. The user edits. On Save we first call `isValid()` to check every field's validation rules (`allowBlank`, `vtype`, custom validators) and bail if anything's wrong. Then `updateRecord` does the reverse of `loadRecord` - it copies the field values back onto the record, which marks it **dirty**. Finally `store.sync()` ships the change to the server, closing the same loop the grid editing did. Grid and form, two views of the same record, kept in step.

> 📝 Older code sometimes skips the record entirely and uses the form's own proxy: `form.submit()` POSTs the field values directly, `form.load()` GETs them. That classic load/submit path still works and shows up in legacy screens. But the **record-based path** (`loadRecord`/`updateRecord` + `store.sync()`) is the common modern approach because it keeps the store as the single source of truth - the grid and form never disagree about what a user's data is. When you see both styles in one codebase, that's usually history, not intent.

A couple of validation helpers worth keeping in your pocket:

- `form.getValues()` - returns a plain object of all field values (handy for logging or a custom save).
- `allowBlank: false` makes a field required; `vtype: 'email'` (and `'url'`, `'alpha'`, etc.) validate format; a `validator` function lets you write arbitrary checks. All of these feed `isValid()`.

## Recap

- **The Grid is a store rendered as rows; the form is fields over one record.** Both are views onto the phase-5 data package - the grid shows the collection, the form shows one member of it.
- A grid needs a **`store`** and **`columns`**; each column's **`dataIndex`** maps it to a model field, and a **`renderer`** formats the displayed value (keep renderers cheap - they run constantly).
- Editing comes from a **plugin** (`cellediting` or `rowediting`) plus an **`editor`** on each editable column. Edits mark records **dirty**; **`store.sync()`** is the separate step that actually persists them.
- A **`pagingtoolbar`** bound to the store walks pages using the proxy's paging params and the reader's `totalProperty`; buffered grids handle truly huge datasets.
- Wire grid to form with **`loadRecord`** (record → fields) and **`updateRecord`** (fields → record), guard with **`isValid()`**, then **`sync()`** - so the grid and form stay two consistent windows onto the same record.

## Quick check

Lock in what binds to what, and how an edit actually reaches the server:

```quiz
[
  {
    "q": "What does a column's dataIndex do?",
    "choices": ["Sets the column's pixel width", "Maps the column to a field on the store's model", "Defines the sort order", "Names the CSS class for the cell"],
    "answer": 1,
    "explain": "dataIndex tells the column which model field to read from each record, so dataIndex: 'name' fills the column with every record's name field."
  },
  {
    "q": "You edit a cell with the cellediting plugin and see the new value in the grid, but after a refresh it's gone. Why?",
    "choices": ["The renderer overwrote it", "Editing only changed the record in memory; you never called store.sync()", "The column had no dataIndex", "The grid wasn't bound to a store"],
    "answer": 1,
    "explain": "Editing marks the record dirty in memory. Persisting is a separate step: store.sync() sends dirty/new/removed records to the server via the proxy."
  },
  {
    "q": "Which pair of methods moves data between a grid's selected record and a form?",
    "choices": ["form.load() and form.submit()", "form.loadRecord(record) to fill the form, form.updateRecord(record) to write edits back", "grid.getSelection() and grid.setSelection()", "store.add() and store.remove()"],
    "answer": 1,
    "explain": "loadRecord copies record values into the form's fields; updateRecord copies edited field values back into the record (which then gets persisted with store.sync())."
  }
]
```


---

# MVVM: ViewControllers, ViewModels & Binding

Up to now you've been building *views* - panels, grids, forms, all described as config trees. But a real screen has to *do* things: handle a button click, know which row is selected, keep a form in sync with that row. The question every Ext JS app eventually has to answer is **where does that logic and that state live?** In Ext JS 5 and up, the answer has a name - **MVVM** - and it comes down to one clean split.

> 💡 **The whole mental model in one line: a ViewController is the view's *behavior*, a ViewModel is the view's *data*, and `bind` is the *wire* that connects the data to the widgets.** Three boxes. Behavior, data, wire. Hold those three and every config in this phase has an obvious home.

Why does this matter for maintaining a legacy app? The *old* way (Ext JS 4) put all logic in big global controllers that lived nowhere in particular, with hand-written glue for all the syncing. MVVM was Sencha's answer to the spaghetti that resulted. You'll meet both - the modern way first, since it's how you *should* think, then the legacy version so you can read old screens.

```mermaid
flowchart LR
  V[View<br/>the component tree] -->|controller| VC[ViewController<br/>behavior: handlers]
  V -->|viewModel| VM[ViewModel<br/>data + stores + formulas]
  VM -->|bind| V
```

## ViewController - the view's behavior

A **`ViewController`** is a class that holds the *logic* for one view: its event handlers and helper methods. Define it with `Ext.define`, extend `Ext.app.ViewController`, and give it an `alias` of the form `controller.<name>`. The view then points at it with `controller: '<name>'`.

```javascript
Ext.define('MyApp.view.users.UsersController', {
    extend: 'Ext.app.ViewController',
    alias: 'controller.users',          // referenced as controller: 'users'

    onDeleteUser: function () {
        var grid = this.lookup('userGrid'),       // find child by its reference
            record = grid.getSelection()[0];      // the selected record

        if (record) {
            grid.getStore().remove(record);       // remove it from the store
        }
    }
});
```

*What just happened:* we defined a controller class scoped to the users view. `this.lookup('userGrid')` reaches a child component tagged with `reference: 'userGrid'` in the view - the *sanctioned* way to find components, the clean replacement for the `Ext.getCmp` trap from phase 3. `this` inside any handler is the ViewController instance, so `this.getView()` gives you the view it belongs to, and `this.lookup(...)` (or its longer alias `this.lookupReference(...)`) gives you any referenced child. No global state, no `Ext.getCmp('someId')` - everything resolves relative to *this* view instance.

Now wire that handler up. Two ways, and you'll see both.

**Option A - `listeners` declared in the view**, naming the controller method as a string:

```javascript
Ext.define('MyApp.view.users.UsersPanel', {
    extend: 'Ext.panel.Panel',
    xtype: 'userspanel',
    controller: 'users',                // attach the ViewController

    layout: { type: 'vbox', align: 'stretch' },
    items: [
        { xtype: 'grid', reference: 'userGrid', flex: 1 /* columns, store */ },
        {
            xtype: 'button',
            text: 'Delete',
            listeners: { click: 'onDeleteUser' }   // string => method on the ViewController
        }
    ]
});
```

*What just happened:* `controller: 'users'` attaches our ViewController to this panel. The button's `listeners: { click: 'onDeleteUser' }` uses a **string**, not a function - Ext JS resolves that string against the view's ViewController and calls `onDeleteUser` there. That string-instead-of-function detail is the giveaway you're looking at MVVM wiring, not an inline callback.

**Option B - a `control` block inside the ViewController**, which wires events by *selector* instead of touching the view:

```javascript
Ext.define('MyApp.view.users.UsersController', {
    extend: 'Ext.app.ViewController',
    alias: 'controller.users',

    control: {
        'button[action=delete]': {      // component query selector
            click: 'onDeleteUser'
        },
        'grid': {
            selectionchange: 'onSelectionChange'
        }
    },

    onDeleteUser: function () { /* ... */ },
    onSelectionChange: function (sel, records) { /* ... */ }
});
```

*What just happened:* the `control` block maps **component query selectors** (the same selector syntax `ComponentQuery` uses - `'button[action=delete]'` matches a button whose `action` config is `'delete'`) to event-to-handler pairs. It's the inverse of putting `listeners` in the view: the controller declares "any matching component, when it fires this event, call this method." A `control` block is scoped to the controller's own view, so selectors only match components *inside this view* - you won't accidentally grab a button from some other screen. Use `listeners` for one-off wiring on a specific component, `control` to keep all event wiring in one place in the controller.

## ViewModel - the view's data

If the ViewController is behavior, the **`ViewModel`** is *state*. It holds three things: a **`data`** object (the view's local variables), inline **`stores`**, and **`formulas`** (derived values that recompute themselves). Attach it with `viewModel: { type: 'users' }` (referencing a defined class) or inline with `viewModel: { data: {...} }`.

```javascript
Ext.define('MyApp.view.users.UsersModel', {
    extend: 'Ext.app.ViewModel',
    alias: 'viewmodel.users',           // referenced as viewModel: { type: 'users' }

    data: {
        isAdmin: false,
        currentUser: null               // will hold the selected record
    },

    stores: {
        users: {
            model: 'MyApp.model.User',
            autoLoad: true
        }
    },

    formulas: {
        fullName: function (get) {
            var u = get('currentUser');
            return u ? get('currentUser.first') + ' ' + get('currentUser.last') : '';
        }
    }
});
```

*What just happened:* the ViewModel declares the view's data in one place. `data` holds plain values (`isAdmin`, `currentUser`). `stores` declares a `users` store *inline* - the ViewModel owns it, so it's automatically available for binding (no more hand-creating the store and wiring it to the grid yourself). `formulas` defines derived data: `fullName` is a function that receives a `get` helper, reads `currentUser.first` and `currentUser.last`, and returns the combined name. The payoff is that **a formula recomputes automatically** whenever any value it reads changes - set a new `currentUser` and `fullName` updates itself, no manual recalculation. Think of `formulas` as spreadsheet cells: a formula over other cells that refreshes on its own.

## Two-way `bind` - the wire, and the killer demo

The **`bind`** config connects a component to ViewModel data using `{path}` template syntax. Bind a single property as a shorthand string, or bind several properties with an object:

```javascript
// shorthand: bind the field's primary value
{ xtype: 'textfield', bind: '{currentUser.name}' }

// object form: bind several configs at once
{
    xtype: 'textfield',
    bind: {
        value: '{currentUser.name}',
        hidden: '{!isAdmin}'           // hide unless isAdmin is true
    }
}
```

*What just happened:* the ViewModel *publishes* its data; bound components subscribe. `bind: '{currentUser.name}'` ties the field's value to that path - and because a textfield is an input, the binding is **two-way**: type in the field and it publishes the new value *back* to `currentUser.name` in the ViewModel. The object form binds multiple configs; `hidden: '{!isAdmin}'` shows the negation operator working right inside the binding template, so the field hides itself whenever `isAdmin` is falsy. No event handlers, no manual `setValue` - the wire keeps both ends in sync.

Now the demo that makes people fall in love with MVVM. In phase 6 you'd select a grid row and then *manually* call `form.loadRecord(record)` in a `selectionchange` handler to push the record into the form - glue code, the exact kind binding deletes. Watch:

```javascript
Ext.define('MyApp.view.users.UsersPanel', {
    extend: 'Ext.panel.Panel',
    xtype: 'userspanel',
    controller: 'users',
    viewModel: { type: 'users' },

    layout: { type: 'vbox', align: 'stretch' },
    items: [
        {
            xtype: 'grid',
            reference: 'userGrid',
            flex: 1,
            bind: {
                store: '{users}',                // grid reads the ViewModel's store
                selection: '{currentUser}'       // selected row publishes INTO currentUser
            }
            /* columns... */
        },
        {
            xtype: 'form',
            height: 200,
            defaultType: 'textfield',
            items: [
                { fieldLabel: 'First', bind: '{currentUser.first}' },
                { fieldLabel: 'Last',  bind: '{currentUser.last}' },
                { fieldLabel: 'Email', bind: '{currentUser.email}' }
            ]
        }
    ]
});
```

*What just happened:* two bindings do all the work. `bind: { selection: '{currentUser}' }` on the grid says "whatever row is selected, publish it into the ViewModel as `currentUser`." The form's fields each bind to `{currentUser.first}`, `{currentUser.last}`, `{currentUser.email}`. So when you click a row, the grid publishes that record to `currentUser`, the ViewModel notifies everyone bound to it, and the three fields fill in automatically. **There is no `selectionchange` handler and no `loadRecord` call** - the data flowed through the ViewModel and the wires did the rest. And because the field bindings are two-way, edits in the form publish back into the record - the modern replacement for the manual wiring in phase 6.

> 💡 Read a bound screen by tracing the `{paths}`. Find which component publishes a path (the grid's `selection: '{currentUser}'`) and which components read it (the fields' `'{currentUser.*}'`). The ViewModel is the switchboard in the middle - you never have to find the wires by hand because the path *is* the wire.

> ⚠️ Bindings are **deferred**, not instant. The ViewModel batches changes and flushes them on a short timer (a scheduler tick), so the bound widget updates a beat after you set the data - not synchronously on the same line. If you set a value and immediately read the widget expecting the new state, you'll get the *old* one. This trips people debugging in the console: the data is right, the DOM just hasn't caught up.

## Legacy MVC controllers (Ext JS 4) - what you'll meet in old code

Plenty of apps you'll inherit predate MVVM. Ext JS 4 used **MVC** with a single, *global* `Ext.app.Controller` per concern, registered in the application's `controllers` array. Instead of a per-view ViewController, one controller reached across the whole app using **`refs`** (named component-query references) and **`control`** (event wiring):

```javascript
Ext.define('MyApp.controller.Users', {
    extend: 'Ext.app.Controller',

    refs: [
        { ref: 'userGrid', selector: 'grid' },        // creates this.getUserGrid()
        { ref: 'userForm', selector: 'form' }
    ],

    init: function () {
        this.control({
            'grid': {
                selectionchange: this.onSelectionChange
            },
            'button[action=delete]': {
                click: this.onDeleteUser
            }
        });
    },

    onSelectionChange: function (model, records) {
        this.getUserForm().loadRecord(records[0]);     // MANUAL glue - no binding
    },

    onDeleteUser: function () {
        var record = this.getUserGrid().getSelection()[0];
        this.getUserGrid().getStore().remove(record);
    }
});
```

*What just happened:* the controller declares `refs` - each entry generates a getter like `this.getUserGrid()` that runs the `selector` as a component query and returns the match. `init` calls `this.control({...})` to wire events by selector, just like the modern `control` block. The handlers do the work *by hand*: `onSelectionChange` calls `loadRecord` manually - exactly the glue MVVM binding eliminates. Functionally it works, and you'll see thousands of lines shaped like this.

> ⚠️ **The classic legacy gotcha: a global Ext JS 4 controller does not scope to a view instance.** Its `refs` and `control` selectors match across the *entire application*. If the same view appears twice on screen (two user panels, two tabs of the same type), the controller's `getUserGrid()` returns whichever component the query finds *first* - and an event from *either* instance fires the *same* handler with no clean way to tell which one. So a button in panel B can end up mutating panel A's grid - a frequent, maddening source of "why did the wrong panel update?" bugs. The modern ViewController fixes it precisely because it's bound to *one* view instance: `this.lookup('userGrid')` and `control` selectors only ever match *that* instance's children. If you're untangling a duplicated-view bug in an old app, this scoping difference is very often the root cause.

## Putting the three boxes together

When you open an MVVM view, sort every config into one of the three boxes and the file stops being a wall of code:

- **`controller: '...'`, `listeners: 'methodName'`, `reference: '...'`** → behavior. The logic lives in the ViewController; `this.lookup` reaches children; `control` or `listeners` wires events.
- **`viewModel: { ... }`, `data`, `stores`, `formulas`** → data. State lives in the ViewModel and recomputes itself.
- **`bind: '{...}'`** → the wire. Trace the paths to see what's connected to what.

That's the architecture modern Ext JS apps are built on, and the lens that turns even a sprawling legacy screen into something you can read line by line.

## Recap

- **ViewController = behavior, ViewModel = data, `bind` = the wire.** Sort every config into one of those three boxes and an MVVM view becomes readable.
- A **ViewController** (`Ext.app.ViewController`, `alias: 'controller.x'`) holds handlers; wire events via the view's `listeners: 'methodName'` or a `control` block; reach children with `this.lookup('ref')` / `this.getView()` - the clean replacement for `Ext.getCmp`.
- A **ViewModel** (`Ext.app.ViewModel`) holds `data`, inline `stores`, and `formulas` (derived values that recompute automatically when their inputs change).
- **Two-way `bind`** connects components to ViewModel paths. Binding a grid's `selection` into `{currentUser}` and binding the form to `{currentUser}` makes selecting a row fill the form **with no `loadRecord` glue** - the modern replacement for phase 6's manual wiring. (Bindings are deferred - they flush on a scheduler tick, not instantly.)
- **Legacy Ext JS 4 MVC** uses global `Ext.app.Controller`s with `refs` and `control`. The big trap: a global controller **doesn't scope to a view instance**, so with duplicated views its selectors match the wrong one - a common source of "wrong panel updated" bugs that ViewControllers fix.

## Quick check

Confirm the three boxes and the scoping gotcha stuck:

```quiz
[
  {
    "q": "In modern Ext JS MVVM, where does a view's data (local values, stores, derived fields) belong?",
    "choices": ["The ViewController", "The ViewModel", "The global Ext.app.Controller", "Directly on the component via Ext.getCmp"],
    "answer": 1,
    "explain": "The ViewModel holds the view's data: its data object, inline stores, and formulas. The ViewController holds behavior (handlers); bind is the wire between data and widgets."
  },
  {
    "q": "You bind a grid with selection: '{currentUser}' and bind a form's fields to '{currentUser.*}'. What happens when the user clicks a grid row?",
    "choices": ["Nothing until you call form.loadRecord(record)", "The selected record publishes to currentUser and the form fills in automatically", "The grid throws because selection can't be bound", "Only the first field updates"],
    "answer": 1,
    "explain": "The grid publishes the selected record into currentUser, the ViewModel notifies the bound form fields, and they populate themselves - no selectionchange handler or loadRecord glue needed."
  },
  {
    "q": "Why can a global Ext JS 4 Controller misbehave when the same view appears twice on screen?",
    "choices": ["Global controllers can't define refs", "Its refs/control selectors match across the whole app, so they can hit the wrong instance", "ViewModels override the controller", "Duplicated views aren't allowed in Ext JS 4"],
    "answer": 1,
    "explain": "A global controller doesn't scope to a view instance - its component-query refs and control selectors match application-wide, so an event from one instance can run a handler that mutates another. ViewControllers fix this by scoping to one view."
  }
]
```


---

# Sencha Cmd, Theming & Surviving a Legacy Codebase

Look back at where you started: a legacy Ext JS app, a wall of nested objects full of `xtype` and `items`, nothing looking like the JavaScript you knew.

Now look at what you can do. You can read a tree of components and know that containers hold items. You can follow the data: a **Model** describes a record, a **Store** holds the rows, a **proxy** fetches them from the server. You can find any component live with `Ext.ComponentQuery`, you know "nothing renders" is almost always a layout problem, and you know a ViewController holds the logic while a ViewModel holds the state two-way `bind` watches. The framework that once felt like a curse is now legible - it was never magic, it's a system, and you can read it.

This last phase is the survival kit: the build tool that trips up newcomers, theming in brief, a practical field guide for orienting in someone else's codebase, a clear word on where Ext JS lives in 2026, and what to do next.

## Sencha Cmd and `app.json`: the build tool nobody explained

A thing that surprises people: an Ext JS app is not a pile of `<script>` tags you load in order. The class system you met in [the class system phase](02-the-class-system.md) - `Ext.define`, `requires`, `Ext.Loader` - builds a **dependency graph**. Something has to walk that graph, pull in exactly the classes you use, and stitch them into a production bundle. That something is **Sencha Cmd**.

Sencha Cmd is the official command-line build tool. It scaffolds new apps (`sencha generate app`), resolves the dependency graph from your `requires` declarations, compiles your code, compiles the theme, minifies everything, and produces an optimized production build. The two commands you'll live in:

```bash
# Dev: starts a local server and live-rebuilds as you save
sencha app watch

# Production: the optimized, minified build for deploy
sencha app build
```

> ⚠️ Two real frictions to brace for. First, **Sencha Cmd is a Java application** - it needs a JDK installed, which catches people off guard ("why does my JavaScript build want Java?"). Second, **the builds are slow** compared to the npm tooling you may know from React or Vue, and a full build can take a while. That's the tool, not you.

The project's control panel is **`app.json`** - the manifest that declares what the app *is*:

```json
{
  "name": "UsersAdmin",
  "toolkit": "classic",
  "theme": "theme-triton",
  "requires": ["font-awesome"],
  "js":  [{ "path": "app.js", "bundle": true }],
  "css": [{ "path": "${build.out.css.path}" }]
}
```

The parts that matter: **`toolkit`** picks `classic` or `modern` (back to that [Classic vs Modern](01-what-extjs-is.md) split - this is where the choice gets *compiled in*, deciding which widget package builds); **`theme`** names the theme package; and the `js`/`css`/`requires` entries declare resources and packages. For shops running several apps side by side, a **`workspace.json`** ties them together. 📝 If you ever wonder "where does this app's config actually live?" - `app.json` is the first file to open.

Here's the whole pipeline in one picture:

```mermaid
flowchart LR
  A[Your classes
+ requires] --> B[Sencha Cmd]
  T[Theme SASS] --> B
  J[app.json] --> B
  B --> C[Resolve graph
compile + minify]
  C --> D[Optimized
app + CSS]
```

💡 Newer Ext JS also ships **`@sencha/ext` with open tooling via npm**, so you *can* build with familiar npm workflows on recent versions. But the overwhelming majority of existing codebases - the ones you're likely to inherit - use Sencha Cmd. Learn to recognize both; expect Cmd.

## Theming, in brief

Ext JS theming is **SASS-based**, and it's more pleasant than you'd fear. Themes ship as **packages** - names like **Triton, Crisp, Neptune, Graphite,** and **Material**. To restyle an app, override SASS variables and let Sencha Cmd compile the CSS:

```javascript
// in your theme's SASS, e.g. _vars.scss
$base-color: #2b5797;
$font-family: 'Inter', sans-serif;
```

Beyond global variables, most components accept a **`ui`** config that selects a visual variant - the same button drawn several ways:

```javascript
{ xtype: 'button', text: 'Save',   ui: 'action' }
{ xtype: 'button', text: 'Delete', ui: 'decline' }
```

That's the whole mental model: variables for the broad strokes, `ui` for per-component variants, Sencha Cmd to compile it all. You don't need to master SASS to read a theme - just know *which file* the colors come from when someone asks you to change the blue.

## Surviving a legacy codebase

You've been handed an unfamiliar Ext JS app. Here's how to get your bearings instead of drowning.

**Orient yourself first - find the map.** Five moves, in order:

1. **Find the entry point.** Look for `Ext.application({...})` (usually in `app.js`) - `main()` for an Ext JS app, naming the app and its `mainView`.
2. **Follow the convention folders.** MVC/MVVM apps lay out as `app/view`, `app/model`, `app/store`, `app/controller` (and ViewControllers/ViewModels alongside views). Folder names tell you what each file *is*.
3. **Follow the `xtype`s.** When a view references `xtype: 'usergrid'`, search the codebase for `usergrid` - the `alias`/`xtype` declaration leads you straight to that component's definition.
4. **Identify the toolkit.** Check `app.json`'s `toolkit` so you know whether you're in Classic or Modern widgets - it changes which APIs exist.
5. **Trace one screen end to end** before touching anything: view → its ViewController → the ViewModel/`bind` → the Store → the proxy → the server. One full trace teaches you the app's shape faster than reading ten files at random.

**Then debug live in the console.** An Ext JS app is fully inspectable at runtime from your browser devtools - you don't have to guess:

```javascript
// Find components live, by xtype or selector (phase 3)
Ext.ComponentQuery.query('grid')          // every grid on the page
Ext.ComponentQuery.query('usergrid')[0]   // your specific one

// Walk the containment tree from a component you grabbed
cmp.up('panel')      // nearest ancestor panel
cmp.down('button')   // first matching descendant

// Inspect the data (phase 5)
var store = cmp.getStore();
store.getData();         // what rows are actually loaded?
store.getCount();        // how many?

// Inspect a record's unsaved/dirty state
record.getChanges();     // fields changed since last sync
record.dirty;            // is it modified?
```

A few reflexes worth burning in, each tied to something you already learned:

- 💡 **Nothing renders?** Suspect a **layout problem** before a data problem ([layouts phase](04-layouts.md)) - a missing or wrong `layout` config silently produces a zero-size component far more often than missing data does.
- ⚠️ **"It's defined but Ext can't find it"?** That's usually a **missing `requires`** ([class system phase](02-the-class-system.md)) - a build-time/loader breakage, not a logic bug. The class never got pulled into the graph.
- 📝 **Lost on the page?** `Ext.ComponentQuery.query(...)` in the console answers "what's actually here right now?" without reading a line of source. Grab the component, then `.up()`/`.down()` your way around. Framework logging can surface load and lifecycle warnings too.

The pattern across all of it: stop theorizing and *ask the running app*. Ext JS will tell you what it has - you only have to know the questions.

## Where Ext JS actually sits - and what to do next

Ext JS is **mature and genuinely powerful** for what it's best at: data-dense internal applications - grids with thousands of rows, complex forms, dashboards, trading desks, admin consoles. For that job, few things match it out of the box.

But be clear-eyed. As **React, Angular, and Vue** ([what a framework even is](/guides/what-a-framework-even-is) sets the family in context) won developer mindshare, the Ext JS ecosystem shrank. Licensing is **commercial**, the community is smaller, hiring is harder, and **greenfield projects on it are rare** in 2026 - nobody's reaching for it to start a new side project.

And yet - it still runs an enormous amount of enterprise software, much of it business-critical, and that software needs people who can read and maintain it. That makes this skill **valuable and durable** for exactly the situation you're probably in: maintenance, contract, and "the person who got handed the legacy app" work. It's a quiet, well-paid, under-supplied skill, not a dead one.

So where to next? Three concrete moves:

- **Open your app's `app.js` today** and find the `Ext.application` entry. Name the `mainView`. You now have the thread to pull.
- **Trace one real screen end to end** - view → ViewController → ViewModel/`bind` → Store → proxy. Do it with the console open, querying as you go. That single exercise turns "I'm lost" into "I know how this works."
- **Bookmark the official Sencha docs and the Kitchen Sink examples.** The Kitchen Sink is a live gallery of every component with source - when you need to know how a thing is configured, it's faster than guessing.

You opened that codebase once and saw nothing but noise. Now you can name the entry point, follow the `xtype`s, read the data flow, and interrogate the live app when it misbehaves. It's a system - describe a tree of components, point them at stores, let the framework run it - and you can read it now. Go open the file.

## Recap

1. **Sencha Cmd** is the official CLI that resolves the class dependency graph, compiles, minifies, and builds. Live in `sencha app watch` for dev and `sencha app build` for production. ⚠️ It's a Java app (needs a JDK) and the builds are slow - that's the tool, not you.
2. **`app.json`** is the project manifest: it declares the `toolkit` (classic/modern), `theme`, and resources; `workspace.json` ties multi-app workspaces together. Newer Ext JS also offers `@sencha/ext` npm tooling, but most legacy code uses Cmd.
3. **Theming is SASS-based** - override variables like `$base-color`, pick a theme package (Triton, Crisp, Neptune, Graphite, Material), and use each component's `ui` config for variants. Sencha Cmd compiles the CSS.
4. **To survive a legacy app:** find `Ext.application` in `app.js`, follow the `view`/`model`/`store` convention folders, chase `xtype`s to definitions, check the toolkit, and trace one screen end to end.
5. **Debug live in the console:** `Ext.ComponentQuery.query(...)` to find components, `.up()`/`.down()` to walk the tree, `store.getData()` to inspect data, `record.getChanges()`/`record.dirty` for unsaved state. "Nothing renders" = layout; "can't find the class" = missing `requires`.
6. **Real place in the world:** Ext JS is powerful for data-dense internal apps but past its mindshare peak, commercially licensed, and rare for greenfield - yet it still runs huge amounts of enterprise software, so the maintenance skill is durable and valuable.

## Quick check

Three things to remember:

```quiz
[
  {
    "q": "Your teammate is surprised the Ext JS build wants Java installed. What's the accurate explanation?",
    "choices": [
      "The app is secretly written in Java, not JavaScript",
      "Sencha Cmd, the official build tool, is itself a Java application and needs a JDK",
      "Java is required to run the Ext JS code in the browser",
      "It's a bug; Ext JS never needs Java"
    ],
    "answer": 1,
    "explain": "Sencha Cmd is a Java app, so it needs a JDK installed even though your code is JavaScript. That surprise, plus slow builds, is normal friction with the tool."
  },
  {
    "q": "You've inherited an Ext JS app and a grid isn't showing up at all on the page. What should you suspect first?",
    "choices": [
      "The server returned no data",
      "A layout problem - a missing or wrong layout config often produces a zero-size component",
      "The theme SASS failed to compile",
      "Java is out of date"
    ],
    "answer": 1,
    "explain": "\"Nothing renders\" in Ext JS is almost always a layout problem before it's a data problem. A missing/incorrect layout config silently gives a component no size."
  },
  {
    "q": "From the browser console, how do you find a live grid component and inspect the rows its store currently holds?",
    "choices": [
      "document.querySelector('grid').rows",
      "Ext.ComponentQuery.query('grid')[0].getStore().getData()",
      "Ext.build('grid').data",
      "console.log(grid) - Ext exposes every component as a global"
    ],
    "answer": 1,
    "explain": "Ext.ComponentQuery.query('grid') finds components live; grab one, call getStore() for its Store, and getData() to see the loaded records. This is the core legacy-debugging move."
  }
]
```
