In this tutorial you will learn how to use the hasMany association, a feature of Sencha Touch models that allows you to connect two models in a one-to-many relationship. The model configs you will learn about in this post are the following:
- hasMany
- belongsTo
The back-end code for this tutorial is in C#. I also published a version of this Sencha Touch tutorial using PHP.
Let’s create a simple Sencha Touch application with the following files:
In the model/Hotel.js file, you will define a Hotel model like so:
Ext.define('App.model.Hotel', { extend: 'Ext.data.Model', config: { fields: [ { name: 'id', type: 'int' }, { name: 'name', type: 'string' }, { name: 'address', type: 'string' }, { name: 'status', type: 'int' } ], hasMany: { model: 'App.model.Room', name:'rooms' } } });
The interesting part of this model definition is the hasMany config, consisting of the model and name properties, which reference a Room model. You will create this model in the model/Room.js file:
Ext.define('App.model.Room', { extend: 'Ext.data.Model', requires: ['Ext.data.Store'], config: { fields: [ { name: 'id', type: 'int' }, { name: 'floor', type: 'string' }, { name: 'type', type: 'string' }, { name: 'standardRate', type: 'float' }, { name: 'smoking', type: 'string' }, { name: 'status', type: 'int' }, { name: 'hotelId', type: 'int'} // This works with belongsTo property below. ], belongsTo: { model: 'App.model.Hotel', foreignKey: 'hotelId' }, proxy: { type: 'ajax', api: { create: '../../services/rooms.ashx?act=createrooms', read: '../../services/rooms.ashx?act=loadrooms', update: '../../services/rooms.ashx?act=updaterooms', destroy: '../../services/rooms.ashx?act=eraserooms' }, reader: { rootProperty: 'hotels' } } } });
The model’s belongsTo config has a model and foreignKey properties which reference the Hotel model. The proxy config references the services/rooms.ashx page. This is the asp.net handler you will use to interact with the database.
This is the code for the rooms.ashx page:
public class Rooms : IHttpHandler { public void ProcessRequest(HttpContext context) { string roomsJson; Room[] rooms; string result = "{'success':false}"; string action = context.Request.QueryString["act"]; if (action != null) { switch (action.ToLower()) { case "loadrooms": string filterJson = context.Request.QueryString["filter"]; // Returning hard-coded rooms for demo purposes. result = @"{'success':'true','rooms':[{'id': 1, 'floor': '3', 'type': 'Royal Suite', 'standardRate': '2', 'smoking': 'false', 'status': '1', 'hotelId': '1'}]}"; break; case "createrooms": roomsJson = GetRequestPayload(context); rooms= JsonConvert.DeserializeObject(roomsJson, typeof(Room[])) as Room[]; // Here you would save the rooms to the database... // Returning success for demo purposes. result = @"{'success':true,'rooms':" + JsonConvert.SerializeObject(rooms) + "}"; break; case "updaterooms": roomsJson = GetRequestPayload(context); rooms = JsonConvert.DeserializeObject(roomsJson, typeof(Room[])) as Room[]; // Here you would save the rooms to the database... // Returning success for demo purposes. result = @"{'success':true,'rooms':[" + JsonConvert.SerializeObject(rooms) + "]}"; break; case "eraserooms": roomsJson = GetRequestPayload(context); rooms = JsonConvert.DeserializeObject(roomsJson, typeof(Room[])) as Room[]; // Here you would delete the rooms from the database... // Returning success for demo purposes. result = @"{'success':true}"; break; } } context.Response.ContentType = "application/json"; context.Response.Write(result); } private string GetRequestPayload(HttpContext context) { StreamReader r = new StreamReader(context.Request.InputStream); context.Request.InputStream.Position = 0; return r.ReadToEnd(); } public bool IsReusable { get { return false; } } }
Now you can check out the behaviors that the related models have. In the app.js file, you can try the following code:
Ext.application({ name: 'App', models: ['Room', 'Hotel'], launch: function () { var myHotel = Ext.create('App.model.Hotel', { id: 1, name: 'Siesta by the Ocean', address: '1 Ocean Front, Happy Island', status: 1 }); var rooms = myHotel.rooms(); rooms.each(function (item, index, length) { console.log('Room Id: ' + item.id + ', room type: ' + item.type); }); rooms.load(); // Should see request with: act:loadrooms, page: 1, start: 0, limit: 25, filter: [{ "property": "hotelId", "value": 1}] } });
Using Google Chrome, you can browse to the index.html file, open the developer tools and inspect the request to the rooms.php file. The console’s output should look similar to this:
The query string parameters clearly show a filter property that contains the id of the hotel the rooms belong to. This is one of the behaviors that the hasMany association provides for us.
How It Works
As you can see in the code, you can obtain the rooms associated with a hotel by invoking the rooms() method of the Hotel model, which returns an instance of a data store. The method is automatically created based on the hasMany config:
hasMany: { model: 'App.model.Room', name:'rooms' }
In the app.js file, you will now add a couple of rooms to the current hotel:
Ext.application({ name: 'App', models: ['Room', 'Hotel'], launch: function () { var myHotel = Ext.create('App.model.Hotel', { id: 1, name: 'Siesta by the Ocean', address: '1 Ocean Front, Happy Island', status: 1 }); var rooms = myHotel.rooms(); rooms.each(function (item, index, length) { console.log('Room Id: ' + item.id + ', room type: ' + item.type); }); rooms.load(); // Should see request with: act:loadrooms, page: 1, start: 0, limit: 25, filter: [{ "property": "hotelId", "value": 1}] var room1 = Ext.create('App.model.Room', { id: 1, standardRate: 5, floor: 3, type: 'Royal Suite', status: 1, smoking: false }); var room2 = Ext.create('App.model.Room', { id: 2, standardRate: 2, floor: 1, type: 'Single', status: 1, smoking: false }); rooms.add(room1); rooms.add(room2); rooms.sync(); // Rooms have the hotelId property populated. } });
The call to rooms.sync() will upload the rooms to the server. Chrome’s console should reflect the operation:
The hotelId field of these rooms is set to 1, which is the id of their parent hotel. Note that you did not have to take care of this. It happens automatically thanks to the hasMany association you defined.
Want To Learn More?
My Sencha Touch books will teach you how to create a Sencha Touch application, step by step, from mockups to production build.
Stay Tuned
Don’t miss any articles. Get free updates in your inbox.
Hi Jorge.
I have downloaded your ebook and started with chapter 1.
I have downloaded the sencha sdk , copied sencha-touch,css and sencha-touch.js from it and placed in the root folder, referred it from index.html but when I am running the application, I am getting below error.
Failed to load resource file:///path/NotesApp-Book-Code-Ch1/src/event/Dispatcher.js?_dc=1354982532236
Failed to load resource file:///path/NotesApp-Book-Code-Ch1/src/event/publisher/Dom.js?_dc=1354982532238
Uncaught Error: [Ext.Loader] Failed loading ‘file:///path/ebook-building-a-sencha-touch-2-app%20(1)/NotesApp-Book-Code-Ch1/src/event/Dispatcher.js’, please verify that the file exists sencha-touch-debug.js:8324
Uncaught Error: [Ext.Loader] Failed loading ‘file:///path/ebook-building-a-sencha-touch-2-app%20(1)/NotesApp-Book-Code-Ch1/src/event/publisher/Dom.js’, please verify that the file exists
Is it neccesary to generate the folder structure using senchatools and then run the code? Simply copying and placing the lib files and referrring it from index.html will not work?
Please help.
Thanks
Hemish, referring to the files should work. Please review your code, as it looks like the app cannot find the Sencha Touch library.
hello joe,
I want to order your book (sencha touch 2), but I want to know: what it contains as chapter because I understand it contains only the note application.
thank you.
You can see the details on my Sencha Touch book here: jorgeramon.me/sencha-touch-book/