Architecture
- Model is persistent in server side.
- Store is cache of model thru its proxy.
- Controller connect View and data in Store.
- Controller responds users’ operations over View.
- View presents binded data and input widgets to users.
Application Files
Root | Second | Third | Forth / Fifth | Fifth |
---|---|---|---|---|
MyApp | / | |||
+ | ext-4 | /<extjs_code> | ||
+ | index.html | |||
+ | app.js | |||
+ | app | / | ||
+ | + | controller | /Main.js | |
+ | + | model | /Main.js | |
+ | + | store | /Main.js | |
+ | + | view | / | |
+ | + | + | Viewport.js | |
+ | + | + | main | / |
+ | + | + | + | Act1.js |
+ | + | + | + | Act2.js |
+ | data | / | ||
+ | + | main.json | ||
+ | + | updatemain.json |
index.html
<html>
<head>
<title>Account Manager</title>
<!-- Load ExtJS style sheet and scripts -->
<link rel="stylesheet" type="text/css" href="ext-4/resources/css/ext-all.css">
<script type="text/javascript" src="ext-4/ext-debug.js"></script>
<script type="text/javascript" src="app.js"></script>
</head>
<body></body>
</html>
/app.js
Ext.application({
requires: ['Ext.container.Viewport'],
// The following name is registered to Ext.namespace
name: 'Hello',
appFolder: 'app',
// specify controllers
controllers: ['Main'],
launch: function() {
Ext.create('Ext.container.Viewport', {
layout: 'fit',
items: [{
// Defined in view/main/Act1.js
xtype: 'mainAct1',
}]
});
console.log('app is lunched');
}
});
Controller
/app/controller/Main.js
Ext.define('Hello.controller.Main', {
extend: 'Ext.app.Controller',
stores: ['Main'],
models: ['Main'],
views: ['main.Act1', 'main.Act2'],
init: function() {
// Bind listeners by this.control
this.control({
// `select' components from view
'viewport > panel': {
// Register a listener to selected components
render: this.onPanelRendered
},
// mainAct1 is defined in view/main/Act1.js
'mainAct1' : {
itemdblclick: this.doAct2
},
// see view/main/Act2.js
'mainAct2 button[action=save]': {
click: this.updateModel
}
});
console.log('controller/Main was initialized');
},
onPanelRendered: function() {
console.log('The panel was rendered');
},
// open dialog when item is dbclicked
doAct2: function(grid, record) {
// record format is defined in view/main/Act1.js (store config)
console.log('Double clicked on ' + record.get('name'));
var view = Ext.widget('mainAct2');
// `down' is a ComponentQuery method
view.down('form').loadRecord(record);
},
updateModel: function(button) {
var win = button.up('window'), // Act2 window
form = win.down('form'), // Act2 form
record = form.getRecord(), // Ref to record
values = form.getValues(); // New values
record.set(values);
win.close();
this.getMainStore().sync();
}
});
Note the execution order:
Log | Location |
---|---|
controller/Main was initialized | Main.js?_dc=1399966476315:9 |
The panel was rendered | Main.js?_dc=1399966476315:12 |
app is lunched | app.js:15 |
Views
/app/view/main/Act1.js
Ext.define('Hello.view.main.Act1' ,{
extend: 'Ext.grid.Panel',
// Make this view of xtype mainAct1
alias: 'widget.mainAct1',
title: 'Act 1',
// Use StoreMain to replace inline store
store: 'Main',
initComponent: function() {
/* inline store config
this.store = {
// following go to /app/model/Main.js
fields: ['name', 'message'],
// following go to /data/main.json
data : [
{name: 'Ed', message: 'Hello Tommy!'},
{name: 'Tommy', message: 'Hello Ed!'}
]
};
*/
// columns rendered by Ext.grid
this.columns = [
{header: 'Name', dataIndex: 'name', flex: 1},
{header: 'Message', dataIndex: 'message', flex: 1}
];
this.callParent(arguments);
}
});
/app/view/main/Act2.js
Ext.define('Hello.view.main.Act2', {
extend: 'Ext.window.Window',
alias: 'widget.mainAct2',
title: 'Act2',
layout: 'fit',
autoShow: true,
initComponent: function() {
this.items = [{
xtype: 'form',
// form fields
items: [{
xtype: 'textfield',
name : 'name',
fieldLabel: 'Name'
}, {
xtype: 'textfield',
name : 'message',
fieldLabel: 'Message'
}]
}];
this.buttons = [{
// will be selected by controller/Main via
// 'mainAct2 button[action=save]'
text: 'Save',
action: 'save'
}, {
text: 'Cancel',
scope: this,
handler: this.close
}];
this.callParent(arguments);
}
});
Model
/app/model/Main.js
Ext.define('Hello.model.Main', {
extend: 'Ext.data.Model',
fields: ['name', 'message']
});
Note
We can add `validations’ to validate values of fields.
Store and Proxy
/app/store/Main.js
Ext.define('Hello.store.Main',{
extend: 'Ext.data.Store',
model: 'Hello.model.Main',
// enforce proxy to refresh data immediately
autoLoad: true,
proxy: {
type: 'ajax',
// bind api(URI) for read/update actions
api: {
read: 'data/main.json',
update: 'data/updatemain.json'
},
// specify response handler
reader: {
type: 'json',
root: 'main',
successProperty: 'success'
}
}
});
Data
/data/main.json
{
// successProperty defined in store proxy
"success": true,
// data
"main": [
{"id": 1, "name": 'Ed', "message": "Hello Tommy!"},
{"id": 2, "name": 'Tommy', "message": "Hello Ed!"}
]
}
Note
Happened to type “main” as “maint” and ExtJS reported nothing and left blank grid. Should I register a error handler or something to get the error?
/data/updatemain.json
{"success" : true }
Architecture & Flow
Draw flow for editing only (not include cancel, render, etc).
Some Observation
PRO
- They made GUI programming like answering fill-in-blank question.
- Rich set of ready to use component (maybe not that apparent in this note).
Confusion
- Names of constructors are inconsistent. e.g. controller uses
init()
and view usesinitComponent()
instead. - Inheritance model is intrusive. e.g. explicit invoke
this.callParent(arguments);
- Ambiguous methods.
- Example1
form.getRecord()
for retrieving original data reference, andform.getValues()
for getting new (user input) data. - Example2
controller.getMainStore().sync()
is sync form record to store. It would be celar if they named itcommit()
.
- Example1
- Potentially performance problem - MVC are all done in JS.
- Model only define schema, huge amount of work reside in Store(need more investgation).
Written with StackEdit.
留言
張貼留言