The Ext JS Grid has been a powerhouse for years, used by thousands of enterprises to deliver robust, data-rich applications to the web. The Ext JS Grid for the Modern Toolkit builds on these years of experience and leverages the full power of HTML5 and CSS3 to provide an extremely flexible and efficient grid for the modern era. In this session, we'll explore some of the key architectural advantages of the Modern Grid. Come and see how you can take advantage of these capabilities to tame mountains of data and give your users the world-class experience they demand.
2. The Grid That Was…
XTemplates + cell renderers
(simple DOM replacement)
3. Classic Grid is a DataView with a BIG template
Four nested templates with embedded code.
1. The standard Component tpl which calls…
2. renderRows which uses
3. itemTemplate which uses
4. rowTemplate which uses
5. cellTemplate which calls…
6. Cell renderers
4. Challenges
• DOM churn.
• Changes replace DOM. Plugins, event handlers might “own” that DOM content.
• Scaling to today’s big apps. Multi thousand row grids.
5. Shortcomings
• View not configurable
• View not themeable
Header1 Header2 Header3
Data1.1 Data1.2 Data1.3
Data2.1 Data2.2 Data2.3
9. Throttling/batching of DOM updates
View config:
{
throttledUpdate: true
}
Flushes batched changes every Ext.view.Table.updateDelay milliseconds
No long frames, small pulse of update activity every 200ms
10. Column updaters
• Similar to renderers, but only called for update. Passed cell’s DOM
updater: function(cellDom, value) {
cellDom.firstChild.style.color = value < 0 ? ‘red’ : ‘green’;
}
11. Minimal DOM update
<tr class=“x-grid-row”>
<td class=“x-grid-cell”>
<div class=“x-grid-inner”>
OldData
</div>
</td>
</tr>
<tr class=“x-grid-row”>
<td class=“x-grid-cell”>
<div class=“x-grid-inner”>
NewData
</div>
</td>
</tr>
New, detached DOM Existing grid row
• Only the changed columns used to render detached row DOM
• For each row:
• Recursively update changed DOM attributes or text content
12. Field dependencies
• Relying on renderers to produce dependent values is unreliable.
• The calculated data does not really exist, so cannot be used.
• Renderers which use their full argument list mean no minimal DOM rerender
How do those fields change when we just set the new price?
13. Field dependencies
// Depends on price field. Called whenever that is set
{
name: 'trend',
calculate: function(data) {
// Avoid circular dependency by hiding the read of trend value
var trend = data['trend'] || (data['trend'] = []);
trend.push(data.price); // Ext.data.Model class sees this!
if (trend.length > 10) {
trend.shift();
}
return trend;
}
29. Throttling/batching of DOM updates
View config:
viewModel: {
scheduler: {
tickDelay: 200
}
}
Flushes batched changes every 200 milliseconds
No long frames, even smaller pulse of update activity every 200ms
42. What if we’re not rapidly changing?
Scrolling data is exactly that when we are using buffered rendering.
New DOM has to be created to show rows in the direction of scroll.
43. Row
Modern Grid is managed Row Widgets
Row
Visible
Spares
Row 1
Row 2
Row 3
45. Conclusion
• Less openly HTML centred concept.
• Widgets abstract away from HTML, each can carry CSS classes for theming.
• HTML + CSS power still there for decorative purposes – tpl config.
• Analogous to UIWebView.
• If you need different appearance and behaviour, prefer a custom component.
46. Please Take the Survey in the Mobile App
• Navigate to this session in the mobile app
• Click on “Evaluate Session”
• Respondents will be entered into a drawing to win one of five $50 Amazon gift cards
Hinweis der Redaktion
Pre-4.2
DOM IS SLOW
Refresh destroyed and then created the full view DOM.
Row DOM completely replaced upon record mutation.
Templates can contain embedded code, so templates can call into their owning Component, or call other templates.
Historically, we dumped a huge load of textual HTML into a Panel’s element to create the grid’s UI
The reason it’s broken into nested template is to enable features such as grouping, row body, or anything else which needs extra DOM.
The API gave explicit access to strings in the DOM generation buffer
The structure is transient. Changes regenerate HTML because of the HTML centric API of the classic grid
Refreshing tips out old DOM, regenerates all rows.
Even simple updates used to destroy active DOM prior to V5. Cell editor inside a cell would be destroyed, or a focused cell could be destroyed by data changes.
Causes memory use and frequent garbage collection.
Thousands of rows hurts performance
Because it was very HTML-centric, content required HTML and possible CSS skills.
Internal elements were not configurable
Internal elements were not themeable.
See Phil Guerant’s talk Theming the Modern Toolkit at 3:40 today for more details
JavaScript is much FASTER than DOM
Column updaters are passed the TD and may change it as the author sees fit – see later.
Upon field change, we do create whole new rows through the whole template tree
But ONLY for changed fields.
Calculated fields can depend upon other calculated fields which can depend upon other fields. Calculations will be run in correct order – we’ll see this in action later
… instead of renderers.
Rapidly updating DOM.
50 changes per second thrown at the store!
Timeline – still long frames. If part of a larger UI, would hurt its perf.
Need to throttle changes to DOM. 5 times a second is fast enough for the human perception.
Classic has throttledUpdate. – check timeline.
Non-destructive update since 5.0. CellEditor undamaged.
Test auto sort.. Use sortable: false on rapidly changing columns! Price is OK
Free for all
Injects HTML.
Mixes concerns. Appearance should come only from theme.
Because of the historical API we generate textual using the full render pathway.
HTML. XTemplates have computational cost.
HTML parsing has cost. We only generate the cells that have changed, so not full width rows.
But because of renderer parameters, some columns MUST be generated even if not changed because renderer has such wide access.
Walking the new tree and copying across only changed attributes or data of cells is suboptimal.
No minimal DOM update because we don’t know what other data the renderer uses, so we must ALWAYS update that column.
The only field being set in the demo is the price.
All other fields are dependent, and the values in the record change when fields they are known to depend on change.
Unlike renderers, your dependent data really exists.
Scroll.
“Spare” rows are just derefenced and hopefully left to the garbage collector.
New full rows are rendered through the pathway we’ve already seen and appended.
One row update cycle. Uncompressible now.
Template overwrite = apply (string creation) + overwrite (HTML element creation). You can see the parse HTML call.
Update columns which traverses the DOM is quite fast in comparison to parsing HTML and creating DOM
It’s STILL amazingly fast! 7 - 12ms/flush. No long frames – no jank in large UI
producesHTML: false opts out of HTML parsing
(getPosition is grid navigation model
Modern components and widgets and ViewModels
Because we use widgets at each level, Row and Cell, we can
“Also is …” my word play ran out of steam
These are the things that it doesn’t do yet.
These will be in the next major release
It’s all still there.
Field dependencies are still useful
Data binding tracks dependencies
Binding is async and buffered, a ViewModel’s Scheduler can be configured with a tickDelay
Components and configs act as a kind of virtual DOM
But a Higher-level of abstraction
Not regenerated and differenced
Setters and getters autogenerated
The generated setter is a bit more detailed
You can provide an applier to “promote” the config object to its final form.
If the applier returns undefined, then the property is not changed.
This is so an applier can simply update the old property if the incoming just means reconfiguring.
Example, setScrollable({y: false}) – the applier simply reconfigures the oldScrollable.
Key is that updaters run only when config changes
Modern components and widgets
“*” – we’ll expand on List later
A List is a dataview of components.
Each component is bound to a record, and inherits a ViewModel from the List
Grid’s components are of type gridrow
Only enough gridrows are created to fill the visible area, plus a configurable buffer amount
Modern components and widgets
“*” – we’ll expand on List later
Containers carry some overhead.
A Row consists of a display:table-row element.
A Cell consists of display:table-cell elements.
Modern components and widgets
“*” – we’ll expand on List later
Modern components and widgets
Rows are not containers. No add/remove methods or add/remove events.
The grid
“*” – we’ll expand on List later
The viewModel affects the grid Panel
If you need docked components to update immediately, then contain the grid in a panel with a default ViewModel
This is instead of a recursive DOM comparison/update
Cell’s updaters react as they see fit. Text cells do nothing but update the data of a text node
Some cells have bound data to their innerCls
The point is that only changed data gets updated.
The record applier in the RowWidget will call setters which will then block non-changes
Eleven records flushing took less than 3ms
It’s just configs being set by binding.
Here’s a typical graph of two changed configurations.
One is the trend field updating the sparkline, and the other is the price which ends in a simple set of the data of a textNode
Modern components and widgets
“*” – we’ll expand on List later
Modern components and widgets
“*” – we’ll expand on List later
A little like Widget column in classic, in that a config, the cell config determines what kind of widget to create under that column header
Column configuration drives the headferts
And the cell config of each column drives the cells below it.
Cells only update what they need to update, when they need to update.
Text JUST for text through dataIndex/binding
Cell adds renderers, formatters and a tpl
By default, they’ll encode any HTML.
More secure. Data could contain “<div onmouseover=“doNefariousStuff”>data</div>
Trees just have a fancy cell to display the first column. As in classic, a tree is a grid
First two entered in a CellEditor will execute the user’s Javascript code!
Last one will Invoke a nefarious website.
In Classic, HTML was assumed, and it was up to you, the developers to defend.
In Modern, HTML is not allowed into a cell unless you opt in.
Enough Row components are created to fill the view
A buffer config determines how many spares
Limited by how many are needed to fill the visible zone, so there won’t be an excessive number.
Rows are moved and reconfigured as needed.
Row widgets are recycled. None will be created unless the view changes height.
Minimal change propagated into cells by configs.
Widgets encapsulate the HTML.
Developers concentrate on behaviour and data.
Designers concentrate on theming the widgets.
But if you NEED HTML, it’s still there with a Component’s tpl. It’s just like a UIWebView in native apps.
Preferably, create custom components to encapsulate your HTML needs which are configurable and themeable.
The value of “orderId” is the “id” value of the reference type