refs: [
{
ref : 'contentPanel',
selector: 'ContentPanel'
}
]
var contentPanel = this.getContentPanel();
contentPanel .removeAll(); //removing existing views.
var usersGrid = Ext.create('MyApp.view.Content.UsersGrid');
contentPanel.add(usersGrid);
contentPanel.doLayout();
2013년 10월 31일 목요일
Form Field에 Store 연결하기
var store = Ext.create('Ext.data.Store', {
model: 'data',
proxy: {
type: 'ajax',
url: 'process.ashx',
reader: {
type: 'json',
root: 'data'
}
},
autoLoad: true,
listeners: {
load: function() {
var form = Ext.getCmp('formJobSummary');
form.loadRecord(store.data.first());
}
}
});
2013년 10월 17일 목요일
Creating a real-life application with the MVC pattern
1. Our first step is to create our app.js file, which will provide our application's
launch point. We create this file in the root of our application alongside our
index.html. At this stage we will give our application the name BugTracker
and an empty launch function:
Ext.application({
name: 'BugTracker',
launch: function(){
console.log('App Launch');
}
});
2. Next we will define our bug model and store. These will be placed in two files named
Bug.js in the model folder and BugStore.js in the store folder respectively. The
contents of each can be seen as follows:
// model/Bug.js
Ext.define('BugTracker.model.Bug', {
extend: 'Ext.data.Model',
fields: [
'title',
'status',
'description',
'severity'
]
});
// store/BugStore.js
Ext.define('BugTracker.store.BugStore', {
extend: 'Ext.data.Store',
model: 'BugTracker.model.Bug',
data: [ ... ]
});
3. We have created our main model and store, now we want to make the application
aware of them so the framework loads them when required. We do this by adding
stores and models configs to the Ext.application call of app.js. These
configs accept an array of strings that are then fully qualified with the relevant
namespace (for example, MyModel becomes BugTracker.model.MyModel).
Ext.application({
name: 'BugTracker',
models: [
'Bug'
],
stores: [
'BugStore'
],
launch: function(){
console.log('App Launch');
}
});
4. Our next step is to create our views. In our application we have a DataView that
displays our set of bugs, a panel to wrap the DataView, a Form panel to allow us to
edit a bug, a window to house the Form panel when it is displayed, and a Viewport
container. We are going to create each of these views as their own class, extending
the relevant framework class, starting with our BugDataView:
// view/BugDataView.js
Ext.define('BugTracker.view.BugDataView', {
extend: 'Ext.view.View',
alias: 'widget.BugDataView',
config: {
store: Ext.create('BugTracker.store.BugStore'),
tpl: '
'
' +
'{title}' +
'{severity}</
span>' +
'{description}' +
'{status}' +
'
' +'
',itemSelector: 'div.bug-wrapper',
emptyText: 'Woo hoo! No Bugs Found!',
deferEmptyText: false
}
});
5. Next we create the BugPanel that will have an instance of the BugDataView within
it. Notice the new action config we have given to each of the toolbar buttons; we will
use these later on to hook up their click events:
// view/BugPanel.js
Ext.define('BugTracker.view.BugPanel', {
extend: 'Ext.panel.Panel',
alias: 'widget.BugPanel',
config: {
title: 'Current Bugs,
height: 500,
width: 580,
layout: 'fit',
style: 'margin: 50;',
tbar: [{
xtype: 'combo',
name: 'status',
width: 200,
labelWidth: 100,
fieldLabel: 'Severity Filter',
store: ['1', '2', '3', '4', '5'],
queryMode: 'local'
}, '-', {
text: 'Sort by Severity',
action: 'sortBySeverity'
}, {
text: 'Open all Bugs',
action: 'openAllBugs'
}, '->', {
text: 'Clear Filter',
action: 'clearFilter'
}],
items: [{
xtype: 'BugDataView'
}]
}
});
6. The Bug form panel is next and we follow the same pattern as the other views and
create it in its own file whose name matches the class name that it contains:
// view/BugForm.js
Ext.define('BugTracker.view.BugForm', {
extend: 'Ext.form.Panel',
alias: 'widget.BugForm',
config: {
border: 0,
items: [{
xtype: 'textfield',
name: 'title',
width: 300,
fieldLabel: 'Title'
}, {
xtype: 'textarea',
name: 'description',
width: 300,
height: 100,
fieldLabel: 'Description'
}, {
xtype: 'numberfield',
name: 'severity',
width: 300,
fieldLabel: 'Severity',
value: 1,
maxValue: 5,
minValue: 1
}, {
xtype: 'combo',
name: 'status',
width: 300,
fieldLabel: 'Status',
store: ['Open', 'In Progress', 'Complete'],
queryMode: 'local'
}]
}
});
7. Now we create a window that contains an instance of the BugForm as well as a
single Save button:
Ext.define('BugTracker.view.BugFormWindow', {
extend: 'Ext.window.Window',
alias: 'widget.BugFormWindow',
config: {
height: 250,
width: 500,
title: 'Edit Bug',
modal: true,
items: [{
xtype: 'BugForm'
}],
closeAction: 'hide',
buttons: [{
text: 'Save',
action: 'saveBug'
}]
}
});
8. Our Viewport container is our final view and is a simple component that extends the
Ext.container.Viewport class and creates an instance of the BugPanel class
within its items collection. To have this class instantiated automatically we also add
the autoCreateViewport: true configuration to our application definition in
app.js:
// view/Viewport.js
Ext.define('BugTracker.view.Viewport', {
extend: 'Ext.container.Viewport',
initComponent: function(){
Ext.apply(this, {
layout: 'fit',
items: [{
xtype: 'BugPanel'
}]
});
this.callParent(arguments);
}
});
9. At this point if we open our index.html page we will see the application displaying
our bugs, but clicking on the buttons or bugs doesn't do anything! To bring it all to
life we need to create a controller, which will tie everything together. Start by creating
a new file called Bugs.js in the controller folder and give it the following skeleton
code. At this point we must also add a controllers config to the application
definition of app.js with a value of ['Bugs'] so the controller is automatically
loaded and initialized:
// controller/Bugs.js
Ext.define('BugTracker.controller.Bugs', {
extend: 'Ext.app.Controller',
views: [
'BugDataView',
'BugPanel',
'BugForm',
'BugFormWindow'
],
init: function(){
console.log('Bugs Controller Init');
}
});
10. Next we use the refs config option to adds some accessor methods for each of the
main components so we can access references to them in our action methods:
refs: [{
ref: 'bugDataView',
selector: 'BugPanel BugDataView'
}, {
ref: 'bugFormPanel',
selector: 'BugFormWindow BugForm'
}, {
ref: 'bugFormWindow',
selector: 'BugFormWindow'
}]
11. We now use the control method (which we introduced in the previous recipe) to
wire up our button clicks to a controller action. We use a simple component query to
target the correct button using the action property that we gave each button and
then hook its click event to a method within the controller:
init: function(){
console.log('Bugs Controller Init');
this.control({
'BugPanel button[action="sortBySeverity"]': {
click: this.onSortBySeverityButtonClick,
scope: this
}
});
},
onSortBySeverityButtonClick: function(btn){
this.getBugDataView().getStore().sort('severity', 'DESC');
}
onSortBySeverityButtonClick: function(btn){
this.getBugDataView().getStore().sort('severity', 'DESC');
}
12. We do this for the remaining buttons, the DataView's itemclick event and the
comboboxes' change event:
init: function(){
console.log('Bugs Controller Init');
this.control({
'BugPanel BugDataView': {
itemclick: this.onBugDataViewItemClick,
scope: this
},
'BugPanel button[action="sortBySeverity"]': {
click: this.onSortBySeverityButtonClick,
scope: this
},
'BugPanel button[action="openAllBugs"]': {
click: this.onOpenAllBugsButtonClick,
scope: this
},
'BugPanel button[action="clearFilter"]': {
click: this.onClearFilterButtonClick,
scope: this
},
'BugPanel combo[name="status"]': {
change: this.onBugStatusComboboxChange,
scope: this
},
'BugFormWindow button[action="saveBug"]': {
click: this.onSaveBugButtonClick,
scope: this
}
});
},
onSaveBugButtonClick: function(btn){
var form = this.getBugFormPanel();
// get the record loaded into the form
var selectedRecord = form.getRecord();
selectedRecord.set(form.getValues());
// refilter
this.getBugDataView().getStore().filter();
this.getBugFormWindow().close();
},
onBugDataViewItemClick: function(view, record, item, index,e){
var win = this.getBugFormWindow();
if(!win){
win = Ext.create('BugTracker.view.BugFormWindow');
}
win.show();
// populate the form with the clicked record
this.getBugFormPanel().loadRecord(record);
},
onSortBySeverityButtonClick: function(btn){
this.getBugDataView().getStore().sort('severity', 'DESC');
},
onOpenAllBugsButtonClick: function(btn){
this.getBugDataView().getStore().each(function(model){
model.set('status', 'Open');
model.commit();
}, this);
},
onClearFilterButtonClick: function(btn){
this.getBugDataView().getStore().clearFilter();
},
onBugStatusComboboxChange: function(combo, value, options){
this.getBugDataView().getStore().clearFilter();
this.getBugDataView().getStore().filter('severity', combo.
getValue());
}
Attaching user interactions to controller actions
Our app.js file contains our Ext.Loader configuration and the application's definition using
the following code:
Ext.Loader.setConfig({
enabled: true
});
Ext.application({
name: 'Cookbook',
autoCreateViewport: true,
launch: function(){
console.log('App Launch');
}
});
Ext.define('Cookbook.view.Viewport', {
extend: 'Ext.container.Viewport',
initComponent: function(){
Ext.apply(this, {
layout: 'fit',
items: [Ext.create('Cookbook.view.LoginForm')]
});
this.callParent(arguments);
}
});
The LoginForm view extends the Ext.form.Panel class and contains a Username and
Password field and a Login button:
Ext.define('Cookbook.view.LoginForm', {
extend: 'Ext.form.Panel',
initComponent: function(){
Ext.apply(this, {
items: [{
xtype: 'textfield',
name: 'Username',
fieldLabel: 'Username'
}, {
xtype: 'textfield',
inputType: 'password',
name: 'Password',
fieldLabel: 'Password'
}, {
xtype: 'button',
text: 'Login',
action: 'login'
}]
});
this.callParent(arguments);
}
});
1. We start by creating a controller that will contain our login logic. We do this by
creating a file in our controller folder called Main.js and define a class called
Cookbook.controller.Main extending from the Ext.app.Controller class:
Ext.define('Cookbook.controller.Main', {
extend: 'Ext.app.Controller',
});
2. Next we add the init method to our controller, which will be executed when the
controller is loaded:
Ext.define('Cookbook.controller.Main', {
extend: 'Ext.app.Controller',
init: function(){
console.log('Main Controller Init');
}
});
3. Add a configuration option called controllers to our application definition, located
in our app.js file. This will tell our application that we have a controller called Main
to load and initialize:
Ext.application({
name: 'Cookbook',
autoCreateViewport: true,
controllers: ['Main'],
launch: function(){
console.log('App Launch');
}
});
4. Now that our controller is being initialized, we can hook up the Login
button's click event to an action. We start by creating an action method
called onLoginButtonClick in our Main controller and simply output
a console message:
...
onLoginButtonClick: function(){
console.log('Log me in!');
}
...
5. Now, in our Main controller's init method we use the control method to attach
this action method to the Login button's click event:
...
init: function(){
console.log('Main Controller Init');
this.control({
'button[action=login]': {
click: this.onLoginButtonClick,
scope: this
}
});
}
...
...
refs: [{
ref: 'usernameField',
selector: 'textfield[name=Username]'
}, {
ref: 'passwordField',
selector: 'textfield[name=Password]'
}]
...
onLoginButtonClick: function(){
console.log('Log me in!');
console.log(this.getUsernameField().getValue());
console.log(this.getPasswordField().getValue());
}
the following code:
Ext.Loader.setConfig({
enabled: true
});
Ext.application({
name: 'Cookbook',
autoCreateViewport: true,
launch: function(){
console.log('App Launch');
}
});
Ext.define('Cookbook.view.Viewport', {
extend: 'Ext.container.Viewport',
initComponent: function(){
Ext.apply(this, {
layout: 'fit',
items: [Ext.create('Cookbook.view.LoginForm')]
});
this.callParent(arguments);
}
});
The LoginForm view extends the Ext.form.Panel class and contains a Username and
Password field and a Login button:
Ext.define('Cookbook.view.LoginForm', {
extend: 'Ext.form.Panel',
initComponent: function(){
Ext.apply(this, {
items: [{
xtype: 'textfield',
name: 'Username',
fieldLabel: 'Username'
}, {
xtype: 'textfield',
inputType: 'password',
name: 'Password',
fieldLabel: 'Password'
}, {
xtype: 'button',
text: 'Login',
action: 'login'
}]
});
this.callParent(arguments);
}
});
1. We start by creating a controller that will contain our login logic. We do this by
creating a file in our controller folder called Main.js and define a class called
Cookbook.controller.Main extending from the Ext.app.Controller class:
Ext.define('Cookbook.controller.Main', {
extend: 'Ext.app.Controller',
});
2. Next we add the init method to our controller, which will be executed when the
controller is loaded:
Ext.define('Cookbook.controller.Main', {
extend: 'Ext.app.Controller',
init: function(){
console.log('Main Controller Init');
}
});
3. Add a configuration option called controllers to our application definition, located
in our app.js file. This will tell our application that we have a controller called Main
to load and initialize:
Ext.application({
name: 'Cookbook',
autoCreateViewport: true,
controllers: ['Main'],
launch: function(){
console.log('App Launch');
}
});
4. Now that our controller is being initialized, we can hook up the Login
button's click event to an action. We start by creating an action method
called onLoginButtonClick in our Main controller and simply output
a console message:
...
onLoginButtonClick: function(){
console.log('Log me in!');
}
...
5. Now, in our Main controller's init method we use the control method to attach
this action method to the Login button's click event:
...
init: function(){
console.log('Main Controller Init');
this.control({
'button[action=login]': {
click: this.onLoginButtonClick,
scope: this
}
});
}
...
...
refs: [{
ref: 'usernameField',
selector: 'textfield[name=Username]'
}, {
ref: 'passwordField',
selector: 'textfield[name=Password]'
}]
...
onLoginButtonClick: function(){
console.log('Log me in!');
console.log(this.getUsernameField().getValue());
console.log(this.getPasswordField().getValue());
}
Architecting your applications with the MVC pattern
1. Start by editing index.html and add the files we require for our app:
Enhancement Log
2. We start the application with an instance of the Ext.app.Application class.
This contains our application name, a reference to the controller(s), and the launch
method that runs once everything has loaded. In app.js add:
Ext.application({
name: 'EnhancementLog',
controllers: ['Enhancement'],
launch: function(){
Ext.create('Ext.container.Viewport', {
layout: 'fit',
items: [{
xtype: 'EnhancementGrid'
}]
});
}
});
3. Now that we have our application defined and ready to launch, let's deal with the
controller. Add the following code to Enhancement.js in the controller directory:
Ext.define('EnhancementLog.controller.Enhancement', {
extend: 'Ext.app.Controller',
stores: ['Enhancement'],
models: ['Enhancement'],
views: ['enhancement.EnhancementGrid'],
init: function() {
//initialization code
}
});
4. Then define the view (in our case an enhancement grid) in EnhancementGrid.js:
Ext.define('EnhancementLog.view.enhancement.EnhancementGrid', {
extend: 'Ext.grid.Panel',
alias: 'widget.EnhancementGrid',
title: 'System Enhancements',
store: 'Enhancement',
columns: [{
header: 'Title',
dataIndex: 'title',
flex: 1
}, {
header: 'Enhancement Description',
dataIndex: 'description',
flex: 3
}]
});
5. We now need to create a model and bind it to a store. The model is defined as follows:
Ext.define('EnhancementLog.model.Enhancement', {
extend: 'Ext.data.Model',
fields: ['id', 'title', 'description']
});
6. Finally we define a store (with some pre-defined data) like so:
Ext.define('EnhancementLog.store.Enhancement', {
extend: 'Ext.data.Store',
model: 'EnhancementLog.model.Enhancement',
data: [{
id: 1,
title: 'Search Field Autocomplete',
description: 'Could the main search field have an
autocomplete facility to increase my productivity.'
}]
});
2. We start the application with an instance of the Ext.app.Application class.
This contains our application name, a reference to the controller(s), and the launch
method that runs once everything has loaded. In app.js add:
Ext.application({
name: 'EnhancementLog',
controllers: ['Enhancement'],
launch: function(){
Ext.create('Ext.container.Viewport', {
layout: 'fit',
items: [{
xtype: 'EnhancementGrid'
}]
});
}
});
3. Now that we have our application defined and ready to launch, let's deal with the
controller. Add the following code to Enhancement.js in the controller directory:
Ext.define('EnhancementLog.controller.Enhancement', {
extend: 'Ext.app.Controller',
stores: ['Enhancement'],
models: ['Enhancement'],
views: ['enhancement.EnhancementGrid'],
init: function() {
//initialization code
}
});
4. Then define the view (in our case an enhancement grid) in EnhancementGrid.js:
Ext.define('EnhancementLog.view.enhancement.EnhancementGrid', {
extend: 'Ext.grid.Panel',
alias: 'widget.EnhancementGrid',
title: 'System Enhancements',
store: 'Enhancement',
columns: [{
header: 'Title',
dataIndex: 'title',
flex: 1
}, {
header: 'Enhancement Description',
dataIndex: 'description',
flex: 3
}]
});
5. We now need to create a model and bind it to a store. The model is defined as follows:
Ext.define('EnhancementLog.model.Enhancement', {
extend: 'Ext.data.Model',
fields: ['id', 'title', 'description']
});
6. Finally we define a store (with some pre-defined data) like so:
Ext.define('EnhancementLog.store.Enhancement', {
extend: 'Ext.data.Store',
model: 'EnhancementLog.model.Enhancement',
data: [{
id: 1,
title: 'Search Field Autocomplete',
description: 'Could the main search field have an
autocomplete facility to increase my productivity.'
}]
});
Advanced functionality with plugins
var form = Ext.create('Ext.form.Panel', {
renderTo: Ext.getBody(),
bbar: [{
xtype: 'button',
text: 'Edit'
}, {
xtype: 'button',
text: 'Save'
}, {
xtype: 'button',
text: 'Cancel'
}],
items: [{
xtype: 'textfield',
fieldLabel: 'Email Address'
}]
});
1. Plugins are simply classes, in the same way that all components are, so we start
by defining our Ext.ux.ReadOnlyField class that will, by default, extend the
Ext.Base class:
Ext.define('Ext.ux.ReadOnlyField', {
});
2. The next step is to define our plugin's init method, which is the starting point
of every plugin. To start with, we simply cache a reference to the plugin's parent
component (that is, the text field) so we can easily access it later:
Ext.define('Ext.ux.ReadOnlyField', {
init: function(parent){
this.parent = parent;
}
});
3. We will use the parent component's render event to create our plugin's extra
markup. We attach a handler method that creates a new DIV element inside
the field's body element:
Ext.define('Ext.ux.ReadOnlyField', {
init: function(parent){
this.parent = parent;
this.initEventHandlers();
},
initEventHandlers: function(){
this.parent.on({
render: this.onParentRender,
scope: this
});
},
onParentRender: function(field){
field.displayEl = Ext.DomHelper.append(field.bodyEl, {
tag: 'div',
style: {
height: '22px',
"line-height": '18px',
margin: '2px 0 0 5px'
}
}, true).setVisibilityMode(Ext.Element.DISPLAY);
field.inputEl.setVisibilityMode(Ext.Element.DISPLAY);
}
});
4. We now add three methods, which will switch between read-only and edit modes.
These methods show and hide the appropriate elements and set the values of them
as needed:
...
edit: function(){
if(this.rendered){
this.displayEl.hide();
this.inputEl.show();
this.cachedValue = this.getValue();
}
},
save: function(){
if(this.rendered){
this.displayEl.update(this.getValue());
this.displayEl.show();
this.inputEl.hide();
}
},
cancel: function(){
if(this.rendered){
this.setValue(this.cachedValue);
this.displayEl.show();
this.inputEl.hide();
}
}
...
5. In order for these methods to be called from the field directly we create a reference to
them in the field's class inside the init method:
init: function(parent){
this.parent = parent;
this.initEventHandlers();
this.parent.edit = this.edit;
this.parent.save = this.save;
this.parent.cancel = this.cancel; }
6. We can now add handlers to our three toolbar buttons to call the relevant method:
...
bbar: [{
xtype: 'button',
text: 'Edit',
handler: function(){
form.items.get(0).edit();
}
}, {
xtype: 'button',
text: 'Save',
handler: function(){
form.items.get(0).save();
}
}, {
type: 'button',
text: 'Cancel',
handle: function(){
form.items.get(0).cancel();
}
}]
...
7. Finally, we attach the plugin to our text field by creating a new plugin instance and
including it in the field's plugins array:
{
xtype: 'textfield',
fieldLabel: 'Email Address',
plugins: [Ext.create('Ext.ux.ReadOnlyField')]
}
renderTo: Ext.getBody(),
bbar: [{
xtype: 'button',
text: 'Edit'
}, {
xtype: 'button',
text: 'Save'
}, {
xtype: 'button',
text: 'Cancel'
}],
items: [{
xtype: 'textfield',
fieldLabel: 'Email Address'
}]
});
1. Plugins are simply classes, in the same way that all components are, so we start
by defining our Ext.ux.ReadOnlyField class that will, by default, extend the
Ext.Base class:
Ext.define('Ext.ux.ReadOnlyField', {
});
2. The next step is to define our plugin's init method, which is the starting point
of every plugin. To start with, we simply cache a reference to the plugin's parent
component (that is, the text field) so we can easily access it later:
Ext.define('Ext.ux.ReadOnlyField', {
init: function(parent){
this.parent = parent;
}
});
3. We will use the parent component's render event to create our plugin's extra
markup. We attach a handler method that creates a new DIV element inside
the field's body element:
Ext.define('Ext.ux.ReadOnlyField', {
init: function(parent){
this.parent = parent;
this.initEventHandlers();
},
initEventHandlers: function(){
this.parent.on({
render: this.onParentRender,
scope: this
});
},
onParentRender: function(field){
field.displayEl = Ext.DomHelper.append(field.bodyEl, {
tag: 'div',
style: {
height: '22px',
"line-height": '18px',
margin: '2px 0 0 5px'
}
}, true).setVisibilityMode(Ext.Element.DISPLAY);
field.inputEl.setVisibilityMode(Ext.Element.DISPLAY);
}
});
4. We now add three methods, which will switch between read-only and edit modes.
These methods show and hide the appropriate elements and set the values of them
as needed:
...
edit: function(){
if(this.rendered){
this.displayEl.hide();
this.inputEl.show();
this.cachedValue = this.getValue();
}
},
save: function(){
if(this.rendered){
this.displayEl.update(this.getValue());
this.displayEl.show();
this.inputEl.hide();
}
},
cancel: function(){
if(this.rendered){
this.setValue(this.cachedValue);
this.displayEl.show();
this.inputEl.hide();
}
}
...
5. In order for these methods to be called from the field directly we create a reference to
them in the field's class inside the init method:
init: function(parent){
this.parent = parent;
this.initEventHandlers();
this.parent.edit = this.edit;
this.parent.save = this.save;
this.parent.cancel = this.cancel; }
6. We can now add handlers to our three toolbar buttons to call the relevant method:
...
bbar: [{
xtype: 'button',
text: 'Edit',
handler: function(){
form.items.get(0).edit();
}
}, {
xtype: 'button',
text: 'Save',
handler: function(){
form.items.get(0).save();
}
}, {
type: 'button',
text: 'Cancel',
handle: function(){
form.items.get(0).cancel();
}
}]
...
7. Finally, we attach the plugin to our text field by creating a new plugin instance and
including it in the field's plugins array:
{
xtype: 'textfield',
fieldLabel: 'Email Address',
plugins: [Ext.create('Ext.ux.ReadOnlyField')]
}
피드 구독하기:
글 (Atom)