2.2. Working with Data in Ext JS

Having all these widgets is great! It allows us to create some truly great web UIs with a minimum of effort. We can create applications that not only look fantastic but that expose advanced functionality to the user.

However, at the end of the day, nearly all applications have to have some data to operate on. It's one thing to be able to create a Grid, but a Grid isn't much good without information to put into it. With many other libraries, data is something that is left entirely to you. Sure, the library may give you an easy way to create a Grid, but putting data into it is your job.

With Ext JS, you can do things that way too. You can take control of every last detail and take all responsibility for populating widgets with data. However, if you're looking for something a bit better, Ext JS is there for you.

Data binding is the name of the game! The term "data binding" refers to a technique where something, often GUI widgets, is declaratively bound to a data source. This binding provides the capability to automatically update the widget when the underlying data changes. You don't have to poll the data source and update anything yourself, and you don't even have to tie into events and do some processing. True data binding gives you everything for free (or at least very close to free).

In Ext JS, two key concepts underlie data binding: Records and Stores (or data stores, if you want to be more pedantic). A Store is a client-side cache of Record objects. The data might have originated on the client, read in from cookies or some other mechanism (like Gears, which we'll be discussing shortly), or it may have come from a server-side data store.

A Record is a low-level description of the data. Let's jump right in and see some code:

var PersonRecord = Ext.data.Record.create([
  { name : "id", mapping : "id" },
  { name : "firstName", mapping : "firstName" },
  { name : "lastName", mapping : "lastName" }
]);

This code creates a type of Record. The Ext.data.Record class exposes a create() method that creates a constructor for a given record layout. In simpler terms, it creates a new subclass of the Record class itself. The object you pass into the create() method describes the structure of the data. It's a simple structure that mimics rows in a database table. In this example we have three fields that describe a person: id, firstName, and lastName. The mapping attribute maps a given field to some underlying JavaScript object. Ext JS seeks to abstract the underlying data store from its own data implementation, so at some point you will create a Record based on some JavaScript object. The fields in that object may not match the fields in the Record—in name, that is—so the mapping attribute allows you to map a Record field to an object field whose name may not match.

NOTE

In theory, the mapping attribute is only necessary if the fields of the Record don't match the names of the fields in the underlying object. In practice, however, I find that my code doesn't work if I don't explicitly include the mapping attribute, even when the field names are the same. I'm not sure why this is, so you may want to include the mapping attribute even when it isn't necessary. I don't see where there's any harm in doing so.

Once you have a Record, the next step is to create a Store for instances of that Record. While you could have Records that you put in simple arrays, or just have individual variables pointing to the Records, putting them in a Store is the most common approach. In addition, it provides a host of capabilities, such as filtering, retrieval, event-based updates, and more.

To create a Store, you write code like this:

var peopleStore = new Ext.data.Store({});

Yep, that's right: strictly speaking, that's all you need to do! There are a couple of different types of stores to choose from, but this gives you a plain-vanilla Store, which oftentimes is all you need. There is also a JsonStore, which includes built-in Ajax mechanisms for loading remote data in JSON form from a server. Also available is the GroupingStore, which adds capabilities for grouping Records based on a specified field. You will also see the SimpleStore floating around, which is an extended Store that makes loading data from JavaScript arrays a littler easier. In this book we'll primarily be dealing with your basic, run-of-the-mill Store, but at the end of the day the basic concepts are still the same.

Although most of the time you don't need to concern yourself with it, a Store will use some implementation of the Reader abstract class. A Reader knows how to take data in some underlying JavaScript form, be it an array or JSON string, and turn it into a Record object, as well as some metadata that the Store needs.

Another concept that you sometimes need to think about is the DataProxy. A DataProxy implementation (DataProxy is an abstract class) knows how to retrieve data in the underlying JavaScript form. In conjunction with a Reader, the DataProxy provides a batch of Records to a Store. Some available DataProxy implementations include the ScriptTagProxy, which allows you to read JSON-P data from a remote URL using dynamic <script> tag insertion; HttpProxy, which supports Ajax requests to retrieve data; and DataProxy, which accepts data during its construction and returns it when the Reader calls the proxy's load() method (which is the method always called by the Reader implementation to request data from the proxy).

Figure 2-28 illustrates the relationship between these components.

Figure 2.28. Diagram of the components of the Ext JS data system

In this book, we won't need to concern ourselves with DataProxys or Readers; we'll only focus on Records and Stores.

We saw how to create a type of Record, and we saw how to create a basic Store. So how do we load data into the Store? There are several ways; here's a simple one:

var people = [
  { firstName : "James", lastName : "Cameron" },
  { firstName : "Ron", lastName : "Howard" },
  { firstName : "Uwe", lastName : "Bole" }
];

for (var i = 0; i < people.length; i++) {
  peopleStore.add(
    new PersonRecord({
      id : new Date().getTime(),
      firstName : people[i].firstName, lastName : people[i].lastName
    })
  );
}

Here we have a basic JavaScript array of objects; each object contains a firstName and a lastName field. So, we simply iterate over this array and for each object we call the add() method of the Store. Passed to this method is a new instance of PersonRecord. The constructor to the PersonRecord accepts an object whose fields are mapped to the fields of the newly created Record based on the mapping attribute specified in the Record specification. The id field is autogenerated using the millisecond value of a Date object. That's all it takes! From this point on, we have a Store with three Records in it; each Record has the data taken from the array.

The next order of business is to take this populated Store and bind it to a widget. That's even simpler than you might think, as you can see for yourself:

new Ext.grid.GridPanel({
  store : peopleStore, renderTo : "panelTarget", width : 500,
  height : 150, autoExpandColumn : "lastName", title : "List of people",
  columns: [
    { header : "ID", width : 120, dataIndex : "id"},
    { header : "First Name", width : 150, dataIndex : "firstName" },
    { id : "lastName", header : "Last Name", dataIndex : "lastName" }
  ]
});

It literally takes only two things: the store attribute points to the data Store to use, and the elements in the columns array include a dataIndex attribute to specify which field in the Records returned by the Store each column maps to. Just like that, our Grid shows the Records in the Store!

Now, the neat thing is that if we modify a record in the Store, the Grid will automatically be updated! For example:

var record = store.getAt(0);
record.set("firstName", "Mike");

This will retrieve the first Record in the store, the one with the firstName "James" and the lastName "Cameron", and changes the firstName to "Mike" (thereby changing a famous movie director to a not-quite-as-famous baseball player[]). Best of all, that change will instantly appear in the Grid, without our having to write any code or do anything at all. That, my friend, is the power of data binding!

[] James Cameron is the famous director of such Hollywood hits as Aliens, Titanic, The Abyss, and Terminator 2. Mike Cameron on the other hand is a Major League Baseball player, an outfielder, who has played for such teams as the Chicago White Sox, Cincinnati Reds, and New York Mets.

Many Ext JS widgets include data-binding capabilities, but not all. Some that do include the ComboBox, DataView (which we'll look at next), and the Grid.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.186.147