How can I use WebSQL in Sencha Touch with Unique Id - extjs

this is my model:
Ext.define('myApp.model.Category', {
extend: 'Ext.data.Model',
requires: [
'Ext.data.Field'
],
config: {
fields: [
{
name: 'id',
type: 'int'
},
{
name: 'slug',
type:'string'
},
{
name: 'title',
type:'string'
},
{
name: 'post_count',
type:'int'
}
],
}});
and this is my store:
Ext.define('myApp.store.LocalCate',{
extend: 'Ext.data.Store',
requires: [
'Ext.data.proxy.Sql'
],
config: {
autoLoad:false,
model:'myApp.model.Category',
storeId:'LocalCate',
proxy:{
type: 'sql',
database: 'myApp',
table: 'Category'
},
}
});
now the problem is that :
when i use store.add({id:123,slug:'123',title:'123',post_count:123});
or store.add(Ext.create('myApp.model.Category'{id:123,slug:'123',title:'123',post_count:123});
they both doesn't work out;
It appears that when i delete the 'id:123' and only add the data
{slug:'123',title:'123',post_count:123}
it works out and when i do store.sync(), the WebSql DataBase show the data without 'id'.
I do know that if i change the id to something else just like 'cate_id',it will work.
However, I want this 'id' as the idproperty, and I don't want to change it's name.
What should i do? How do you use the websql in sencha touch? I find the same problem in LocalStorage.

answer it by myself.
the model will do this when created
// If it does not have an id in the data at this point, we use the one generated by the id strategy.
// This means that we will treat this record as a phantom record from now on
id = me.data[idProperty];
if(!id && id !==0){
me.data[idProperty]= me.internalId = me.id;
me.phantom =true;
and if i created a model with id, it's phantom is false that tell the sencha it has it data in database so that when i sync(),it won't be pushed to the database.
only if i created the model with phantom = true can i sync() it to the database.
here is the code for addrecords:
getNewRecords:function(){
returnthis.data.filterBy(function(item){
// only want phantom records that are valid
return item.phantom ===true&& item.isValid();
}).items;
},

Related

Adding a proxy section to a model in Sencha Touch 2 not MVC

My goal here is to understand how models and stores are working and more specifically with 'localstorage'.
Here's the function I'm working on:
createModelAndStore: function() {
console.log('Kitchensink.view.LocalStorage createModelAndStore starts');
// Set up a model to use in our Store
Ext.define('User', {
extend: 'Ext.data.Model',
config: {
//proxy: {
// type: 'localstorage'
//},
fields: [
{name: 'firstName', type: 'string'},
]
}
});
var myStore = Ext.create('Ext.data.Store', {
id: 'UserStore',
model: 'User',
data: [
{
'firstName': 'Tommy'
}
]
});
},
It's working perfectly like that. As soon as I remove the comments on the proxy section it fails miserably with 'Model with name "User" does not exist.'.
Can someone please explain to me what's happening?
I am not interested in moving the proxy definition in the store as I would like to use the '.save()' method on a model instance along with autosync.

extjs model associations in a list view

I have two models: Page and Department. I am showing pages in a list view in extjs and I would like to display the Department's Name instead of the department_id in the List view. I have not gotten to the part of actually adding the department to the page VIA the GUI, only through direct db insert statements, but I would like to at least be able to display the department name in the list view.
I have the following so far, this is showing department_id
models
Ext.define('ExtMVC.model.Department', {
extend: 'Ext.data.Model',
fields: ['name']
});
Ext.define('ExtMVC.model.Page', {
extend: 'Ext.data.Model',
fields: ['title','body','department_id'],
associations: [
{type: 'belongsTo', model: 'Department'}
]
});
stores
Ext.define('ExtMVC.store.Pages', {
extend: 'Ext.data.Store',
model: 'ExtMVC.model.Page',
autoLoad: true,
proxy: {
type: 'rest',
url: '/admin/pages',
format: 'json'
}
});
Ext.define('ExtMVC.store.Departments', {
extend: 'Ext.data.Store',
model: 'ExtMVC.model.Department',
autoLoad: true,
proxy: {
type: 'rest',
url: '/admin/departments',
format: 'json'
}
});
List View
Ext.define('ExtMVC.view.page.List' ,{
extend: 'Ext.grid.Panel',
alias : 'widget.pagelist',
title : 'All Pages',
store: 'Pages',
initComponent: function() {
this.tbar = [{
text: 'Create Page', action: 'create'
}];
this.columns = [
{header: 'Title', dataIndex: 'title', flex: 1},
{header: 'Department', dataIndex: 'department_id', flex: 1}
];
this.callParent(arguments);
}
});
controller (fwiw)
Ext.define('ExtMVC.controller.Pages', {
extend: 'Ext.app.Controller',
init: function() {
this.control({
'pagelist': {
itemdblclick: this.editPage
},
'pagelist > toolbar > button[action=create]': {
click: this.onCreatePage
},
'pageadd button[action=save]': {
click: this.doCreatePage
},
'pageedit button[action=save]': {
click: this.updatePage
}
});
},
onCreatePage: function () {
var view = Ext.widget('pageadd');
},
onPanelRendered: function() {
console.log('The panel was rendered');
},
doCreatePage: function (button) {
var win = button.up('window'),
form = win.down('form'),
values = form.getValues(),
store = this.getPagesStore();
if (form.getForm().isValid()) {
store.add(values);
win.close();
this.getPagesStore().sync();
}
},
updatePage: function (button) {
var win = button.up('window'),
form = win.down('form'),
record = form.getRecord(),
values = form.getValues(),
store = this.getPagesStore();
if (form.getForm().isValid()) {
record.set(values);
win.close();
this.getPagesStore().sync();
}
},
editPage: function(grid, record) {
var view = Ext.widget('pageedit');
view.down('form').loadRecord(record);
},
stores: [
'Pages',
'Departments'
],
models: [
'Page'
],
views: [
'page.List',
'page.Add',
'page.Edit'
]
});
Ext's associations have visibly not been designed for use in stores, but rather for working with single records... So, I agree with what has already been said, that you'd better flatten your model on the server-side. Nevertheless, it is possible to achieve what you want.
The association won't load your associated model (i.e. Department) until you call the generated getter method (i.e. getDepartment()). Trying to go this way, that is calling this method for each Page record loaded in your store would require an incredible amount of hack because the grid reacts synchronously to the refresh event of the store, while the getDepartment() method returns asynchronously...
That's why you will have to load your departments data in the same request that loads your pages. That is, your server must return records of the form:
{title: 'First Page', body: 'Lorem', department_id: 1, department: {name: 'Foo'}}
In order for your Page model's proxy to consume this, you need to configure your association this way:
Ext.define('ExtMVC.model.Page', {
// ...
associations: [{
type: 'belongsTo'
// You need the fully qualified name of your associated model here
// ... which will prevent Ext from generating everything magically
,model: 'ExtMVC.model.Department'
// So you must also configure the getter/setter names (if you need them)
,getterName: 'getDepartment'
// Child data will be loaded from this node (in the parent's data)
,associationKey: 'department'
// Friendly name of the node in the associated data (would default to the FQ model name)
,name: 'department'
}]
});
Then comes the really ugly part. Your grid's columns cannot access the associated data with the classic dataIndex property. However, provided the associated records have already been loaded, this can be accessed from a TemplateColumn like this:
{
header: 'Department'
,xtype: 'templatecolumn'
,tpl: '{department.name}'
}
Unfortunately, that will prevent you from using some more appropriate column class (date column, etc.), that you may have configured globally. Also, this column loses track of the model field it represents, which means that some features based on introspection won't be able to do their magic (the grid filter ux, for example, uses the field type to decide automatically on the type of filter).
But in the particular case you've exposed, that won't matter...
Complete Example
Here's what it gives when you bring it all together (or see it in action)...
Ext.define('ExtMVC.model.Department', {
extend: 'Ext.data.Model',
fields: ['name'],
proxy: {
type: 'memory'
,reader: 'json'
,data: [
{id: 1, name: 'Foo'}
,{id: 2, name: 'Bar'}
,{id: 30, name: 'Baz'}
]
}
});
Ext.define('ExtMVC.model.Page', {
extend: 'Ext.data.Model',
fields: ['title','body','department_id'],
associations: [{
type: 'belongsTo'
,model: 'ExtMVC.model.Department'
,getterName: 'getDepartment'
,associationKey: 'department'
,name: 'department'
}],
proxy: {
type: 'memory'
,reader: 'json'
,data: [
{title: 'First Page', body: 'Lorem', department_id: 1, department: {name: 'Foo'}}
,{title: 'Second Page', department: {name: 'Bar'}}
,{title: 'Last Page', department: {name: 'Baz'}}
]
}
});
Ext.define('ExtMVC.store.Pages', {
extend: 'Ext.data.Store',
model: 'ExtMVC.model.Page',
autoLoad: true
});
Ext.define('ExtMVC.view.page.List', {
extend: 'Ext.grid.Panel',
alias : 'widget.pagelist',
title : 'All Pages',
store: Ext.create('ExtMVC.store.Pages'),
initComponent: function() {
this.tbar = [{
text: 'Create Page', action: 'create'
}];
this.columns = [
{header: 'Title', dataIndex: 'title', flex: 1}
,{header: 'Department', xtype: 'templatecolumn', flex: 1, tpl: '{department.name}'}
];
this.callParent(arguments);
}
});
Ext.widget('pagelist', {renderTo: 'ct', height: 200});
As one of the comments mentions, it would be a lot simpler to bind the department name to the Pages model when working with a grid/list. Denormalisation for display is not a bad thing.
An alternative is perhaps reverse the modelling, in that you could say the a department 'hasMany' Pages.
This allows you define the primary and foreign keys on the relationship and then your department store will (per row) have a automatic 'pages()' store which will contain the child info.
I've typically done this for master/detail forms where I want to bind 'pages()' to a list/grid, but keep the department model as the master record on a form for example.
Why shouldn't use renderer config function on the grid's column ?
This implies to (auto)load the departments store upfront on application launch (you might need it in more places anyway, think of grid cell editor as full department list).
{
name:'Department',
dataIndex:'department_id',
renderer: function(deptId){
return Ext.data.StoreManager.lookup('Departments').getById(deptId).get('name');
}
}
PS: I was using myself denormalisation for display, but doesn't feel good ;)

unique field in the model

i am looking for a solution to make one of the data field as unique field in the model in sencha, somehow i am not able to find documentation for it. Here is the code
Ext.define('handfree.model.CategoryM', {
extend: 'Ext.data.Model',
requires: [
'Ext.data.identifier.Uuid'
],
config: {
fields: [
{ name: 'id', type: 'int' },
{ name: 'name', type: 'string' }
],
idProperty: 'id',
identifier : 'uuid'
}
});
I need 'name' field to be unique. Thank you very much!
Assuming that you are manually popping records into a store, you can put a listener on your store, and when a record is added, unset it.
http://docs.sencha.com/touch/2-0/#!/api/Ext.data.Store-event-addrecords

EXTJS4--Why don't my associated stores load child data?

So I have a parent and child store, illustrated here:
Parent Model
Ext.define('APP.model.Client', {
extend: 'Ext.data.Model',
requires: [
'APP.model.Website', 'Ext.data.association.HasMany', 'Ext.data.association.BelongsTo'],
fields: [{
name: 'id',
type: 'string'
}, {
name: 'name',
type: 'string'
}, {
name: 'slug',
type: 'string'
}, {
name: 'active',
type: 'boolean'
}, {
name: 'current',
type: 'boolean'
}],
hasMany: {
model: 'APP.model.Website',
name: 'websites'
}
});
Child Model
Ext.define('APP.model.Website', {
extend: 'Ext.data.Model',
fields: [{
name: 'id',
type: 'string'
}, {
name: 'client_id',
type: 'string'
}, {
name: 'sub_domain',
type: 'string'
}, {
name: 'active',
type: 'boolean'
}],
belongsTo: 'APP.model.Client'
});
Using an AJAX call via the server, I am loading the Clients store, and that is loading fine. But the Websites store isn't populated, and when I breakpoint on the Clients store on.load function, to see what it's populated with, the Client store is only populated with the client data, but in the raw property for that store, I can see all the websites data. So it's being returned correctly, but my extjs isn't correct. Here are the stores:
Client Store
Ext.define('APP.store.Clients', {
extend: 'Ext.data.Store',
autoLoad: false,
model: 'APP.model.Client',
proxy: {
type: 'ajax',
url: '/client/list',
reader: {
type: 'json',
root: 'items'
}
},
sorters: [{
property: 'name',
direction: 'ASC'
}]
});
Websites Store
Ext.define('APP.store.Websites', {
extend: 'Ext.data.Store',
requires: ['Ext.ux.Msg'],
autoLoad: false,
model: 'APP.model.Website',
proxy: {
type: 'ajax',
url: '/client/list',
reader: {
type: 'json',
root: 'items'
},
writer: {
type: 'json'
}
},
sorters: [{
property: 'sub_domain',
direction: 'ASC'
}]
});
My final result is...I would like to populate both stores so I can click on an element, and when it loads something from the parent store, I can access the child store(s) (there will be more when I figure out this problem) to populate a couple grid(s) in tabs.
What am I missing as far as my setup? I just downloaded extjs4 a couple days ago, so I am on 4.1.
Put your proxies in your models, unless you have a good reason not to [1]
Make sure you require the related model(s), either in the same file, or earlier in the application
Use foreignKey if you want to load the related data at will (i.e. with a later network request).
Use associationKey if the related data is loaded in the same (nested) response
Or just use both
Always name your relationships (otherwise the name will be weird if using namespaces).
Always use the fully qualified model name for the model property in your relationships
Working code:
model/Contact.js:
Ext.define('Assoc.model.Contact', {
extend:'Ext.data.Model',
requires:[
'Assoc.model.PhoneNumber'
],
fields:[
'name' /* automatically has an 'id' field */
],
hasMany:[
{
model:'Assoc.model.PhoneNumber', /*use the fully-qualified name here*/
name:'phoneNumbers',
foreignKey:'contact_id',
associationKey:'phoneNumbers'
}
],
proxy:{
type:'ajax',
url:'assoc/data/contacts.json',
reader:{
type:'json',
root:'data'
}
}
});
model/PhoneNumber.js:
Ext.define('Assoc.model.PhoneNumber', {
extend:'Ext.data.Model',
fields:[
'number',
'contact_id'
],
proxy:{
type:'ajax',
url:'assoc/data/phone-numbers.json',
reader:{
type:'json',
root:'data'
}
}
});
data/contacts.json:
{
"data":[
{
"id":1,
"name":"neil",
"phoneNumbers":[
{
"id":999,
"contact_id":1,
"number":"9005551234"
}
]
}
]
}
data/phone-numbers.json
{
"data":[
{
"id":7,
"contact_id":1,
"number":"6045551212"
},
{
"id":88,
"contact_id":1,
"number":"8009996541"
},
]
}
app.js:
Ext.Loader.setConfig({
enabled:true
});
Ext.application({
requires:[
'Assoc.model.Contact'
],
name:'Assoc',
appFolder:'Assoc',
launch:function(){
/* load child models that are in the response (uses associationKey): */
Assoc.model.Contact.load(1, {
success: function(record){
console.log(record.phoneNumbers());
}
});
/* load child models at will (uses foreignKey). this overwrites child model that are in the first load response */
Assoc.model.Contact.load(1, {
success: function(record){
record.phoneNumbers().load({
callback:function(){
console.log(arguments);
}
});
}
});
}
});
[1] A store will use its model's proxy. You can always override the store's proxy if need be. You won't be able to use Model.load() if the model has no proxy.
Your assumption is wrong. You expect that the WebSite store loads itself but that is not how it works. What you can do is the following (this is untested but is how I do it in all my projects):
In your clients grid add a listener for the itemclick event to call the following method (showWebSites). It will receive the selected client record containing the selected APP.model.Client instance. Then, and given that each client has a set of WebSites, the method will load the APP.store.Websites store and the client´s websites will be displayed in your view.
showWebSites: function (sender, selectedClient) {
Ext.StoreManager.lookup('APP.store.Websites')
.loadData(selectedClient.data.WebSites);
}
This is the way. I hope you find it useful.

Sencha touch 2.0 many-to-many associations - how?

I've been having a hell of a time getting Sencha Touch 2.0 hasMany associations working, especially since it looks like their data models don't directly allow for many-to-many relationships. I've got two models - People and Roles (theere are a bunch more, but these are the two that matter in this example) , each has a many-to-many to the other.
I originally thought that I could do this with a hasMany in each of the models, but snce the data is stored in third-normal form in my db, I figure that I need a third, person-to-role model. Code is here:
Ext.define('SMToolkit.model.Person', {
extend: 'Ext.data.Model',
config: {
fields: [
'id',
'first_name',
'last_name',
'email',
'address',
'phone1',
'phone2',
'type',
'location'
],
hasMany: [
{
model: 'SMToolkit.model.Person_Role',
name: 'role'
}
],
proxy: {
type: 'rest',
url : 'index.php/api/persons'
}
}
});
Ext.define('SMToolkit.model.Role', {
extend: 'Ext.data.Model',
config: {
fields: [
'id',
'name',
'description',
'type',
'show_id'
],
hasMany: [
{
model: 'SMToolkit.model.Person_Role',
name: 'person'
},
{
model: 'SMToolkit.model.Scene_Role',
name: 'scene'
},
{
model: 'SMToolkit.model.Thing',
name: 'thing'
}
],
proxy: {
type: 'rest',
url : 'index.php/api/roles'
}
}
});
Ext.define('SMToolkit.model.Person_Role', {
extend: 'Ext.data.Model',
config: {
fields: [
'person_id',
'role_id'
],
associations: [
{
type: 'belongsTo',
model: 'SMToolkit.model.Person',
name: 'person'
},
{
type: 'belongsTo',
model: 'SMToolkit.model.Role',
name: 'role'
},
],
proxy: {
type: 'rest',
url : 'index.php/api/personsroles'
}
}
});
I've confirmed that the personsroles url above does in fact return a valid data set, so I know that there should be something in there...
When I look at a Role record, I can see fields for the associated stores, but even if I know for certain that there's an appropriate record in the Person_Role table in the db, the Persons array in the record is empty.
I'm getting the record like so:
onRoleSelect: function(list, index, node, record) {
var editButton = this.getEditButton();
if (!this.showRole) {
this.showRole = Ext.create('SMToolkit.view.role.Show');
}
person = record.person();
thing = record.thing();
scene = record.scene();
person.load();
thing.load();
scene.load();
// Bind the record onto the view
this.showRole.setRecord(record);
// Push the show show view into the navigation view
this.getRoleContainer().push(this.showRole);
},
What am I doing wrong? Why is there no association data?
Here's an alternative approach for handling complex model relations in Sencha.
I've not yet tested it but I think it will likely handle Many-Many relations as well.
Recursive M-M relations might cause you grief with the linkChildAssociations() function.
http://appointsolutions.com/2012/07/using-model-associations-in-sencha-touch-2-and-ext-js-4/

Resources