Large-scale client applications are often poorly implemented and poorly maintained, and because of the increasing functionality and manpower, the scale of these applications is quickly beyond control, and ExtJS 4 brings a new application architecture that not only organizes the code, but also reduces the content of the implementation.
The new application architecture follows an MVC-like pattern in which models (Models) and controllers (Controllers) are introduced for the first time. There are many kinds of MVC architectures in the industry, basically the same, ExtJS 4 is defined as follows:
This tutorial will create a simple example of managing user data, and you'll learn how to use the ExtJS 4 architecture to organize simple applications together.
The application architecture is about providing structure and consistency, about real class and framework code, and adherence to conventions can bring many important benefits:
ExtJS 4 Applications follow a unified directory structure, each of which should be the same. For details, see ExtJS 4 Getting Started, MVC, where all classes are placed in app
a directory, this directory can have subdirectories, which represent namespaces (a subdirectory for a namespace), a different directory to store,,, views
models
controllers
stores
When we complete the example, the directory structure should be the same as the following:
Creating the application in App.js creating an app in App.jsEach ExtJS 4 application starts with an instance of a Application
class that contains the application's global configuration (for example, the name of the app), which maintains the maintenance of references to all models, views, controllers, and a launch
function that is called after all add-ins have completed loading.
Let's create a simple Account Manager app to manage user accounts. First you need to select a global namespace, and all ExtJS4 applications need to have a global namespace for all classes in the application, in this case we use AM (account Manager)
Ext.application({ requires: ‘Ext.container.Viewport‘, name: ‘AM‘, appFolder: ‘app‘, launch: function() { Ext.create(‘Ext.container.Viewport‘, { layout: ‘fit‘, items: [ { xtype: ‘panel‘, title: ‘Users‘, html : ‘List of users will go here‘ } ] }); }});
First we call Ext.application
to create an application instance and set the app name "AM", which will automatically create a global variable "AM", and automatically register the namespace "AM" to Ext.Loader
, in a similar way also set app
as appFolder
. In addition launch
, in the function, one is created Viewport
, containing only one full browser window Panel
.
Defining a controller defines a control
The controller is the adhesive for the application, and what they do is listen to the event and perform the action, continue our Account Manager application, and create a controller. Create app/controller/Users.js
the file and add the following code:
Ext.define(‘AM.controller.Users‘, { extend: ‘Ext.app.Controller‘, init: function() { console.log(‘Initialized Users! This happens before the Application launch function is called‘); }});
Next, app.js
Add a reference to the Users
controller in:
Ext.application({ ... controllers: [ ‘Users‘ ], ...});
When we look at the index.html
application, the Users
controller is automatically loaded (because a app.js
reference is added to the application), and Users
the method is init
launch
called before the
init
The method is an excellent place to set up how to interact with the view, usually using Controller
a method control
that control
makes it easier to listen to the events of the view, update the controller, and let it tell us when the panel renders:
Ext.define(‘AM.controller.Users‘, { extend: ‘Ext.app.Controller‘, init: function() { this.control({ ‘viewport > panel‘: { render: this.onPanelRendered } }); }, onPanelRendered: function() { console.log(‘The panel was rendered‘); }});
We have updated the init
method to use the this.controll
listener to set the view. This controll
method, using the latest component query engine (componentquery), can quickly and easily find the components on the page. If you are unfamiliar with componentquery, you can review the Componentquery documentation for more information. Briefly, Componentquery allows us to find components in a way similar to CSS selectors.
In the example init
method we have applied ‘viewport > panel‘
, which can be interpreted as "finding all panel components in viewport direct descendants", then we provide an object matching event name (only used in this example render
) to provide a response function. The whole effect is that no matter which component conforms to our selector, render
our onPanelRendered
function is called when its event is triggered.
When we run the application, we can see the following:
It's not the most exciting app, but it shows us how easy it is to start a piece of organized code. Let's add a little bit of content and add a grid. Defining a view defines a viewing
Until now, our application only has very little code, only two files app.js
and app/controller/Users.js
now we want to add a grid to display all the users in the system, it is time to better organize the logic and start using the view.
The view is also a component, usually a subclass of an existing component of ExtJS, and is now ready to create the user table, create app/view/user/List.js
it, add the code:
Ext.define(‘AM.view.user.List‘ ,{ extend: ‘Ext.grid.Panel‘, alias : ‘widget.userlist‘, title : ‘All Users‘, initComponent: function() { this.store = { fields: [‘name‘, ‘email‘], data : [ {name: ‘Ed‘, email: ‘[email protected]‘}, {name: ‘Tommy‘, email: ‘[email protected]‘} ] }; this.columns = [ {header: ‘Name‘, dataIndex: ‘name‘, flex: 1}, {header: ‘Email‘, dataIndex: ‘email‘, flex: 1} ]; this.callParent(arguments); }});
Our view class is an ordinary class, in this case we extend the component and Grid
set the alias so that we can invoke the component in xtype
a way that we have added store
and columns
the configuration.
Next we need to add this view to the Users
controller. Because we use ‘widget.‘
aliases, we can use userlist
them as xtype, just as we used before ‘panel‘
.
Ext.define(‘AM.controller.Users‘, { extend: ‘Ext.app.Controller‘, views: [ ‘user.List‘ ], init: ... onPanelRendered: ...});
Next modify to app.js
make the view render in viewport, need to modify the launch
method
Ext.application({ ... launch: function() { Ext.create(‘Ext.container.Viewport‘, { layout: ‘fit‘, items: { xtype: ‘userlist‘ } }); }});
The only thing to note is that we specify in the views array ‘user.List‘
, which tells the application to automatically load the corresponding file, and the ExtJS4 dynamic loading system will automatically pull files from the server according to the rules, such as user. List is the rule, replace the./is the file storage path. Refresh the page:
Controlling the Grid control of the list
Note that the onPanelRendered
method is still called because our grid still satisfies the ‘viewport > panel‘
selector because our view inherits from the Grid
Panel
Now we need to tighten the selector, we use xtype as the selector to replace the previous ‘viewport > panel‘
, listen to the double-click event, in order to continue to do the editing user information:
Ext.define(‘AM.controller.Users‘, { extend: ‘Ext.app.Controller‘, views: [ ‘user.List‘ ], init: function() { this.control({ ‘userlist‘: { itemdblclick: this.editUser } }); }, editUser: function(grid, record) { console.log(‘Double clicked on ‘ + record.get(‘name‘)); }});
Note that we replaced the component query selector for ‘userlist‘
, listen for the event to change to, the ‘itemdblclick‘
response function is set to ‘editUser‘
, now just simple log out double-click the row's Name property:
You can see that the log is correct, but what we actually want to do is edit the user information, let's do it now, create a new viewapp/view/user/Edit.js
ext.define (' AM.view.user.Edit ', {extend: ' Ext.window.Window ', alias: ' Widget.useredit ', title: ' Edi T User ', layout: ' Fit ', Autoshow:true, Initcomponent:function () {this.items = [{ Xtype: ' Form ', items: [{xtype: ' TextField ', Name: ' Name ', Fieldlabel: ' name '}, { Xtype: ' TextField ', Name: ' Email ', Fieldlabel: ' Email ' } ] } ]; This.buttons = [{text: ' Save ', Action: ' Save '}, { Text: ' Cancel ', Scope:this, handler:this.close}]; This.callparent (arguments); }});
This time we still inherit from an existing class Ext.window.Window
, or use the initComponent
method to specify complex items
and buttons
objects, we used a ‘fit‘
layout and a form, the form includes the user information to edit the field, finally we created two buttons, one to close the window, The other is used to save changes.
Now all we have to do is load this view in the controller, render and load the user information:
Ext.define(‘AM.controller.Users‘, { extend: ‘Ext.app.Controller‘, views: [ ‘user.List‘, ‘user.Edit‘ ], init: ... editUser: function(grid, record) { var view = Ext.widget(‘useredit‘); view.down(‘form‘).loadRecord(record); }});
First we created the view using a method, which is Ext.widget
equivalent to Ext.create(‘widget.useredit‘)
, and then we find the form in the window with the help of the component query, and each component in the ExtJS4 has a down
method that can quickly find any underlying component by querying the supported selectors with the component.
Double-click a row in the table to see:
Creating a Model and a Store
Now that we have the form, we can start editing and saving the user information, but this requires a little refactoring.
AM.view.user.List
Created an inline so that it Store
works but we need to Store
separate it so that we can reference and update the information in other places in the application, and we put it in the file it should be in app/store/Users.js
:
Ext.define(‘AM.store.Users‘, { extend: ‘Ext.data.Store‘, fields: [‘name‘, ‘email‘], data: [ {name: ‘Ed‘, email: ‘[email protected]‘}, {name: ‘Tommy‘, email: ‘[email protected]‘} ]});
Now we need to make two changes, first we need to let the Users
initialization load this Store
:
Ext.define(‘AM.controller.Users‘, { extend: ‘Ext.app.Controller‘, stores: [ ‘Users‘ ], ...});
Then we're going to change the store that we used to inline in the view, and we'll app/view/user/List.js
use the ID to reference the store.
Ext.define(‘AM.view.user.List‘ ,{ extend: ‘Ext.grid.Panel‘, alias : ‘widget.userlist‘, //we no longer define the Users store in the `initComponent` method store: ‘Users‘, ...});
The introduction of Store,store in the controller's code is automatically loaded into the page and given a StoreID, which makes it easy to use the store in the view (in this case, as long as the configuration store: ‘Users‘
is available)
Now we are just inline in the store defined two fields (' Name ' and ' email '), so it works, but there is a powerful class in ExtJS4, Ext.data.Model
we can use it when editing the user, using the model to reconstruct the store, app/model/User.js
Create a model in:
Ext.define(‘AM.model.User‘, { extend: ‘Ext.data.Model‘, fields: [‘name‘, ‘email‘]});
This is what we need to do to define our model, and now we need to let the store reference model replace the way in which inline fields are used, and let the controller refer to model:
//the Users controller will make sure that the User model is included on the page and available to our appExt.define(‘AM.controller.Users‘, { extend: ‘Ext.app.Controller‘, stores: [‘Users‘], models: [‘User‘], ...});// we now reference the Model instead of defining fields inlineExt.define(‘AM.store.Users‘, { extend: ‘Ext.data.Store‘, model: ‘AM.model.User‘, data: [ {name: ‘Ed‘, email: ‘[email protected]‘}, {name: ‘Tommy‘, email: ‘[email protected]‘} ]});
Our refactoring can make the next work a little bit simpler, but without affecting the existing functionality, we refresh the page and check to see if it works:
Saving data with the model uses models to save
Now we have a user data table, double-click each row can open an edit window, now to do is to save the editing changes, the editing window has an edit form, and the Save button, now we update the controller so that the Save button responds:
Ext.define(‘AM.controller.Users‘, { init: function() { this.control({ ‘viewport > userlist‘: { itemdblclick: this.editUser }, ‘useredit button[action=save]‘: { click: this.updateUser } }); }, updateUser: function(button) { console.log(‘clicked the Save button‘); }});
We this.control
added a component query selector to the method, which ‘useredit button[action=save]‘
shows that this selector is the same as the first one, and note that we added a configuration when we defined the Save button. {action: ‘save‘}
This selector means that selecting XYTPE is the button for all action properties under the Useredit component that are save
Check to see updateUser
if it is called:
The instructions work fine, then populate updateUser
the real logic. We need to take the data out of the form and set it back to the store:
updateUser: function(button) { var win = button.up(‘window‘), form = win.down(‘form‘), record = form.getRecord(), values = form.getValues(); record.set(values); win.close();}
Let's break down what we've done here. Our response function receives a reference to the button, but what we care about is the form and the window, through the buttons and the component query, can find something to care about, here first used to button.up(‘window‘)
find the window, and then used to win.down(‘form‘)
find the form.
After that we removed the record associated with the form and updated the record with the input values in the form, and finally closed the window focus back to the table, and we changed the user name to ‘Ed Spencer‘
Click Save should be able to see:
Saving to the server
It's simple. Let's increase the interaction with the server side to complete this example. Now we should encode the data for two rows of tables, and now let's load it through Ajax:
Ext.define(‘AM.store.Users‘, { extend: ‘Ext.data.Store‘, model: ‘AM.model.User‘, autoLoad: true, proxy: { type: ‘ajax‘, url: ‘data/users.json‘, reader: { type: ‘json‘, root: ‘users‘, successProperty: ‘success‘ } }});
Here we go apart from ‘data‘
attributes, replaced by proxy
, proxies are a way to get the store or model to load and save data, with AJAX,JSONP,HTML5 Localstorage local storage, and so on. Here we use a simple Ajax proxy that allows it to load data through a URL ‘data/users.json‘
.
We also attach a reader,reader to the agent to decode the data returned by the server into a format that the store can understand, this time using JSON reader, and specifying the root and successProperty
configuration (the detailed configuration of JSON reader to look at the document), Finally we create a data file data/users.json
, enter the content:
{ success: true, users: [ {id: 1, name: ‘Ed‘, email: ‘[email protected]‘}, {id: 2, name: ‘Tommy‘, email: ‘[email protected]‘} ]}
The other change is that we set the property for the store autoLoad
and set true
it to, which means that the store will automatically have the proxy load data, refresh the page should see the same results as before, the difference is now not in the program should be encoded data
The final thing is to return the changes to the server side, this example we use a static JSON file, not using the database, but enough to illustrate our example, first make a little change to tell the proxy for the updated URL:
proxy: { type: ‘ajax‘, api: { read: ‘data/users.json‘, update: ‘data/updateUsers.json‘ }, reader: { type: ‘json‘, root: ‘users‘, successProperty: ‘success‘ }}
Still reading from the users.json
data, but the changes will be sent to updateUsers.json
, here we do a mock reply back to the package so that we know that the program can work correctly, updateUsers.json
only need to include {"success": true}
, the other thing is to let the store after editing to synchronize, need updateUser
to Add a line of code to the function:
updateUser: function(button) { var win = button.up(‘window‘), form = win.down(‘form‘), record = form.getRecord(), values = form.getValues(); record.set(values); win.close(); this.getUsersStore().sync();}
Now we can run through the entire example, check whether each function is normal, edit a row in the table, and see if we can correctly send the request toupdateUser.json
Deployment Release
The new Sencha SDK Tools point This download makes it easy to publish ExtJS4 apps. This tool allows you to generate a dependency manifest and generate a minimized version of the
Details can be viewed ExtJS getting started next Steps next
Here we create a very simple example, but the basic features are included, you can start extrapolate development of other functions, remember to follow the development model, code organization, the above code example in the EXT JS 4 SDK, the examples/app/simple
directory
ExtJS 4 MVC Architecture Explained