PDA

View Full Version : Nested Models Help!



epiphanydigital
13 Jul 2011, 4:49 AM
Hi person with the correct answer,

I have a pretty basic setup of a model "profile" that each has many "orders" or whatever. I'm targeting phones, not iPads so I have these panels on separate screens... eg. Add a user, then add an order to that user.

Adding a user works great.

Adding an order adds an order to a NEW empty user, instead of to the current user.

Here's my model structure:



Ext.regModel('ProfileModel', {
proxy : {
type : 'localstorage',
id : 'profileModel'
},
fields : [
{name: 'id', type: 'int'},
{name: 'nid', type: 'int'},
{name: 'create', type: 'date'},
{name: 'modded', type: 'date'},
{name: 'title', type: 'string'}
],
associations: [{
type : 'hasMany',
model : 'orderModel',
name : 'orders'
}]
});

Ext.regStore({
model : 'ProfileModel',
storeId : 'ProfileStore'
});

Ext.regModel('OrderModel', {
proxy : {
type : 'localstorage',
id : 'orderModel'
},
fields : [
{name: 'id', type: 'int'},
{name: 'did', type: 'int'},
{name: 'create', type: 'date'},
{name: 'modded', type: 'date'},
{name: 'name', type: 'string'},
{name: 'amount', type: 'int'},
{name: 'unit', type: 'int'},
{name: 'frequency', type: 'int'},
{name: 'frequency_unit', type: 'int'},
{name: 'notes', type: 'string'}
]
});

Ext.regStore({
model : 'OrderModel',
storeId : 'OrderStore'
});

mbalsam
13 Jul 2011, 6:02 AM
Could you share the code where you are adding an order? What are you adding it to? Another thing: Why do you have an extra store for the orders?

epiphanydigital
13 Jul 2011, 6:08 AM
Sure thing, the code below is the handler for a save button on the order form panel. Re: extra store, maybe this isn't the best way of doing it, but I'm trying to have a one-to-many relationship setup, which you can see in the associations above. Are you suggesting having two models and one store?




handler : function() {
var currentOrder = MyDS.OrderFormPanel.getRecord();
MyDS.OrderFormPanel.updateRecord(currentOrder);
var orderStore = MyDS.OrderList.getStore();
if (null == orderStore.findRecord('id', currentOrder.data.id)) {
orderStore.add(currentOrder);
}
orderStore.sync();
MyDS.OrderList.refresh();
MyDS.MenuPanel.setActiveItem('orderPanel', {
type:'fade'
});
}

mbalsam
13 Jul 2011, 6:20 AM
You definitely should not just add the order to your order store but find the user in the profile store, add the order to the user and sync the profile store.

Note that every record in the profile store has its own order storte since you set up the hasMany relation!

epiphanydigital
13 Jul 2011, 6:21 AM
Gotcha, so keep both models and delete the orders store and only reference the profiles store from now on and that should do it?

mbalsam
13 Jul 2011, 7:49 AM
From what i understood you wanted to achieve i'd say, yes. There may be situations when a separate order store would make sense (e.g. one to search for particular orders in). But it is important to understand that the models are linked to each other but not stores.

epiphanydigital
13 Jul 2011, 7:57 AM
Thanks! I really appreciate the help... now it's just a matter of figuring out what specifically is wrong now that I have a bird's eye view of the problem.

epiphanydigital
14 Jul 2011, 6:16 AM
I'm really missing something here. Anyone willing to rewrite the handler posted above to help solve this problem? ...or have an example of this kind of thing working?

mbalsam
14 Jul 2011, 10:44 AM
Below you add the current order to the store if it does not already exists. There is no line in your code that links the order to an existing user profile. So how should the order know what user it belongs to?



handler : function() {
var currentOrder = MyDS.OrderFormPanel.getRecord();
MyDS.OrderFormPanel.updateRecord(currentOrder);
var orderStore = MyDS.OrderList.getStore();
if (null == orderStore.findRecord('id', currentOrder.data.id)) {
orderStore.add(currentOrder);
}
orderStore.sync();
MyDS.OrderList.refresh();
MyDS.MenuPanel.setActiveItem('orderPanel', {
type:'fade'
});
}


You'd want something like the following



handler : function() {
var user, profileStore;
// get the profileStore and the user here
...
var currentOrder = MyDS.OrderFormPanel.getRecord();
MyDS.OrderFormPanel.updateRecord(currentOrder); // I believe this line is not necessary here
// now get the collection of orders associated to the user and update the current order
// or add it to the collection
orders = user.orders();
if(orders.contains(currentOrder) === false) {
orders.add(currentOrder);
}
orders.sync();
if (null == profileStore.findRecord('id', user.getId())) {
profileStore.add(user);
profileStore.sync();
}
...
}


You may also have a look at this thread:
http://www.sencha.com/forum/showthread.php?125218-LocalStore-and-hasMany-association

epiphanydigital
18 Jul 2011, 8:36 AM
I've been staring at your response off and on for a couple of days... as much as I know there answer is there, I think I need a more thorough explanation. Mind being patient with a Sencha n00b on this one?

Thanks!

mbalsam
18 Jul 2011, 11:45 PM
To be honest I am not sure what it is that you don't understand about nested models and stores. You should probably go to the API docs and read the parts about models/stores again carefully.
As I already mentioned in a previous post you have to explicitly add the order to the user's orders. You don't do that in your code. How is the system supposed to know that the order belongs to a specific user if you don't have it in your code???

epiphanydigital
19 Jul 2011, 5:29 AM
I get it from an overview, and I get what you're saying. The models setup make sense to me as well. I just don't understand how to tell the system to shoot a list of orders over for a given profile to a list. Or how to add/delete a specific order to/from a profile.

I see a lot of info in the Sencha docs on Models, and a lot more on Lists. I don't see a whole lot of info available on how they interact except in basic usage scenarios.

mbalsam
19 Jul 2011, 7:14 AM
If you want to add the order to a specific user you need to have the specific instance of your ProfileModel that represents the user. Since you have set up the hasMany association from ProfileModel to OrderModel under the name orders every instance of the ProfileModel automatically has a method orders that returns an Ext.util.MixedCollection (you may also want to check the API for that class) with all the orders of the user. If you have the collection for an instance of ProfileModel just add/remove orders to/from that collection and call sync on it to synchronize the underlying order store (which is automatically there because of the hasMany association). Simple as that just as in the example I gave a few posts back.



// user is your specific instance of ProfileModel
orders = user.orders();
// you can add your currentOrder like this...
if(orders.contains(currentOrder) === false) {
orders.add(currentOrder);
}
// or remove it like that...
if(orders.contains(currentOrder) === true) {
orders.remove(currentOrder);
}
// and call sync afterwards
orders.sync();

epiphanydigital
19 Jul 2011, 8:02 AM
Thanks for continuing to follow up with me. As I understand it then, this (below) should work to populate my OrderList, but it still calls the parent list. Ideas?



var currentProfile = new MyDS.Profiles({id: profile.data.id});
MyDS.OrderList.update(currentProfile.orders());
MyDS.MenuPanel.setActiveItem('orderPanel');

mbalsam
19 Jul 2011, 11:12 PM
I assume MyDS.OrderList is of type Ext.List? First, the update method only works properly if you set up the itemTpl config for your list. Have you done that? Mind to show me the code of your components? Second problem: you create a new profile with an existing id (the profile will have no orders since it is new) and update the list with this profile's (non-existing) orders. I see that you have a profile instance there called profile from which you obtain the id. Why don't you just do the following?



MyDS.OrderList.update(profile.orders());
MyDS.MenuPanel.setActiveItem('orderPanel');


It would really help if you could elaborate in greater detail what your application is supposed to do what panels/components you have and in what order your panels are supposed to be shown.

epiphanydigital
20 Jul 2011, 6:02 AM
* The first page is Profiles which is an Ext.List. Adding a new Profile from a Form.FormPanel works fine.
* The second page is accessed by clicking on a Profile ListItem, and it takes you to an Ext.List of Supplements (or orders), that unfortunately shows a list of users currently. This list also has an add and delete feature on the toolbar that hasn't yet been implemented.

Trying the ideas you just mentioned doesn't change the result. Here's the list code, let me know if anything else would be helpful. I believe my model code is already posted in this thread.



MyDS.ProfileList = new Ext.List({
id : 'profileList',
store : 'ProfileStore',
emptyText : '<div style="margin:5px;">Click the plus button on the top right to add your first profile.</div>',
allowDeselect : false,
listeners : {
itemtap : function(record,index){
var profile = record.store.getAt(index);
MyDS.SupplementPanelToolbar.setTitle('Supplements for ' + profile.data.title);
var currentProfile = new MyDS.Profiles({id: profile.data.id});
MyDS.SupplementList.update(currentProfile.supplements());
MyDS.MenuPanel.setActiveItem('supplementPanel');
},
'render' : function(thisComponent) {
thisComponent.getStore().load();
}
},
itemTpl: '<div class="list-item-title">{title}</div>'
});

MyDS.SupplementList = new Ext.List({
store : 'ProfileStore',
model : 'SupplementModel',
itemTpl: '<div class="list-item-title">Parent: [{title}] Name: [{name}]</div>',
emptyText : 'No supplements have been added.',
allowDeselect : false,
listeners : {
itemtap:function(record,index){
var current = record.store.getAt(index);
//console.log(current);
if (current) {
MyDS.SupplementFormPanel.load(current);
}
MyDS.MenuPanel.setActiveItem('supplementFormPanel');
},
'render' : function(thisComponent) {
thisComponent.getStore().load();
}
}
});

mbalsam
20 Jul 2011, 6:18 AM
Well your SupplementList has the ProfileStore as it's store and is therefore showing profiles.
You should not set the list's store when you create it but only call MyDS.SupplementList.update(profile.orders()).
Or you could dynamically bind the profile's order store by calling MyDS.SupplementList.bindStore(profile.ordersStore).

epiphanydigital
20 Jul 2011, 7:22 AM
I get an error on the supplementlist when I don't specify the store.


Uncaught DataView requires tpl, store and itemSelector configurations to be defined.

Where would I bind this? on the itemtap listener?