Pull to refresh and list pagging duplicating records on listing page - sencha-touch

I am new for sencha touch. I have a Listing page which using Ext.plugin.PullRefresh and Ext.plugin.ListPaging plugin to refresh my page, but I'm having an issue with duplicate items appearing after I pull to refresh.
Here is my coding
~View
Memberlist.js
Ext.define('bluebutton.view.BlueButton.MemberList', {
extend: 'Ext.List',
xtype: 'memberlistcard',
requires: [
'Ext.field.Select',
'Ext.field.Search',
'bluebutton.view.BlueButton.MemberDetail',
'Ext.plugin.ListPaging',
'Ext.plugin.PullRefresh'
],
config: {
iconCls: 'team1',
title: 'Member List',
styleHtmlContent: true,
scrollable: 'vertical',
store : { xclass : 'bluebutton.store.BlueButton.MemberList'},
grouped: true,
indexBar: true,
limit: 5,
plugins: [
{ xclass: 'Ext.plugin.ListPaging',
autoPaging: true },
{ xclass: 'Ext.plugin.PullRefresh' }
],
id :'memberlist',
items: [
{
xtype: 'toolbar',
docked: 'top',
items: [
{
xtype: 'selectfield',
name: 'gender',
cls: 'txtwhite',
options: [
{ text: 'Active Member', value: 'both' },
{ text: 'Delete Member', value: 'male' },
{ text: 'Suspended Member', value: 'female' }
]
},
{ xtype: 'spacer' },
{ xtype: 'searchfield' ,
itemId:'membersearch',
id :'membersearch'
}
]
}
],
emptyText: '<p class="no-search-results">No member record found matching that search</p>',
itemTpl: Ext.create(
'Ext.XTemplate',
'<div class="tweet-wrapper">',
'<table>',
'<tr>',
'<td rowspan="2" width="1%">',
' <img src="{imgUrl}" width="170" height="170" />',
'</td>',
'<td>',
' <div class="tweet">',
' <h2>{memberId}</h2>',
' <h3>Name: {name}</h3>',
' <h3>Age : {age}</h3>',
' <h3>Address : {address}</h3>',
' <h3>Point Avalaible : {pointAvalaible}</h3>',
' <h3>Last Visited : {lastVisited}</h3>',
' </div>',
'</td>',
'</tr>',
'</table>',
'</div>'
),
},
});
~Store
Memberlist.js
Ext.define('bluebutton.store.BlueButton.MemberList', {
extend: 'Ext.data.Store',
config: {
grouper: {
groupFn: function (record) {
return record.get('name')[0];
}
},
fields: ['memberId', 'name','age' ,'imgUrl','address','lastVisited','pointAvalaible'],
pageSize: 5,
autoLoad: false,
storeId :'memberlist',
data: [{
memberId: 'Kenny',
name: 'Kenny Chow',
imgUrl: '/bluebutton/resources/images/user3.png',
age: '20',
address:'The Business Centre , 61 Wellfield Road , Roath, Cardiff, CF24 3DG',
pointAvalaible :'10',
lastVisited: '26/11/2012, 11:52 AM',
}, {
memberId: 'Anthony',
name: 'Anthony Tan',
imgUrl: '/bluebutton/resources/images/user3.png',
age: '21',
address:'3 Edgar Buildings , George Street , Bath , England , BA1 2FJ',
pointAvalaible :'44',
lastVisited: '27/11/2012, 09:52 AM'
},
{
memberId: 'Nicholas',
name: 'Nicholas Chen',
imgUrl: '/bluebutton/resources/images/user3.png',
age: '22',
address: '91 Western Road , Brighton ,East Sussex ,England ,BN1 2NW ',
pointAvalaible :'30',
lastVisited: '30/11/2012, 15:52 PM'
},
{
memberId: 'Admin2',
name: 'Admin2',
imgUrl: '/bluebutton/resources/images/user3.png',
age: '30',
address: '50 Eestern Road , Brighton ,West Sussex ,England ,BN1 34W ',
pointAvalaible :'120',
lastVisited: '01/12/2012, 15:52 PM'
},
{
memberId: 'User2',
name: 'User2',
imgUrl: '/bluebutton/resources/images/user3.png',
age: '25',
address:'Office 33 ,27 Colmore Row ,Birmingham, England ,B3 2EW',
pointAvalaible :'32',
lastVisited: '30/11/2012, 18:52 PM'
}
]
}
});
Please help. Thanks

I also faced this problem. I override the refresh function in pullrefresh and added
refreshFn(){
Ext.getStore(storeid).load()
}

Currently, your store has no way of determining which records are new upon refresh. To fix this, you will need to create an Ext.data.Model instance with a defined idProperty that your store can use. Once, created, set your store's 'model' property to be the name of the model. Then, when your store refreshes, it will be able to see which records are actually new and only insert those.
Remove the 'fields' property in your store config and replace with this:
model: 'bluebutton.model.BlueButton.MemberList'
Model sample:
Ext.define('bluebutton.model.BlueButton.MemberList', {
extend: 'Ext.data.Model',
config: {
idProperty: 'memberId',
fields: ['memberId', 'name','age' ,'imgUrl','address','lastVisited','pointAvalaible']
}
}

I have the very same issue as you. This is a strange problem though since my application used to work, and one day, it started duplicating fields in the list using the pull to refresh plugin.
Adding the idProperty in the data store caused the refresh to fail in the onLatestFetched function on the Pull to Refresh plugin. My investigation seem to indicate that the faulty line was :
oldRecord = oldRecords.getByKey(newRecord.getId());
newRecord.getId() was always undefined, even though newRecord is correctly populated.
I solved my issue by patching this line in the following way:
oldRecord = oldRecords.getByKey(newRecord.internalId;);
This worked for me. Not really sure this is really future proof nor that it works in all situation but it can help you.

Related

Date range vtype, cannot understand how it works, extjs 4

i have a form with two date fields, they have to define a date range, so the end cannot be before the start; and they both are made by two datepicker, which should disable the dates before the "start" in the "end". I've found this manual page with some code to be pasted, but i cannot paste this code in my page without getting an error. Maybe my page is too complicated but i don't think so, i think it's only me who cannot figure out where to paste the codewithout making all the other code fail. The following is the code of my page:
Ext.define('MyDesktop.count', {
extend: 'Ext.ux.desktop.Module',
requires: [
'Ext.ux.desktop.DatabaseModel',
//'Ext.ux.desktop.UserModel',
'Ext.data.TreeStore',
'Ext.layout.container.Accordion',
'Ext.toolbar.Spacer',
'Ext.tree.Panel'
],
id:'count',
init : function(){
this.launcher = {
text: 'count',
iconCls:'accordion'
};
},
createWindow : function(){
var App = this.app;
var desktop = this.app.getDesktop();
var win = desktop.getWindow('count-win');
if (!win) {
win = desktop.createWindow({
id: 'count-win',
title: 'count',
width: 350,
height: 200,
animCollapse: false,
maximizable: false,
resizable: false,
//constrainHeader: true,
//bodyBorder: true,
layout : 'anchor',
items:[
{
xtype : 'fieldset',
border: false,
defaultType: 'textfield',
items: [
{
xtype: 'datefield',
fieldLabel: 'Start date',
format: 'Ymd',
id:"start",
//altFormats: 'Ymd',
vtype: 'daterange',
endDateField: 'end'
},
{
xtype: 'datefield',
fieldLabel: 'End date',
format: 'Ymd',
id: "end",
//minValue: Ext.getCmp('start').getValue(),
//altFormats: 'm/d/Y',
vtype: 'daterange',
startDateField: 'start'
},
{
xtype: 'combo',
fieldLabel: 'Client',
id:"client",
hiddenName: 'client',
store: new Ext.data.SimpleStore({
data: [
['applix', 'applix'],
['banzai', 'banzai'],
['banzai-cooking', 'banzai - cooking'],
['integralAS', 'integralAS'],
['LinkSmart', 'LinkSmart'],
['nbcuniversal', 'nbcuniversal'],
['primenetwork', 'primenetwork'],
['quag', 'quag'],
['turnEU', 'turnEU'],
['turnUS', 'turnUS']
],
id: 0,
fields: ['value', 'text']
}),
valueField: 'value',
displayField: 'text',
triggerAction: 'all',
editable: false
}
]
}
],
buttons: [{
text: 'Submit',
handler: function() {
console.log(this);
start= Ext.util.Format.date(Ext.getCmp('start').getValue(),'Ymd');
end= Ext.util.Format.date(Ext.getCmp('end').getValue(),'Ymd');
//period = Ext.getCmp('period').value;
client = Ext.getCmp('client').value;
Ext.Ajax.request({
method : "GET",
url : 'http://whatever.com/count?client='+client+'&start='+start+'&end='+end,
timeout : 30000,
success :
function (response) {
//console.log(Ext.getCmp('to_add'))
Ext.Msg.alert(response.responseText);
desktop.getWindow('count-win').doClose();
}//handler
,
failure :
function(response) {
alert("Wrong request");
}
});
}
}]
});
}
return win;
}
});
The vtypes are already there, where should i put the listener and/or the function to handle the daterage property without cutting away all the rest of the code?Should i use a listener as in some examples here on stackoverflow (but i tryed and the window of the form wasn't working any more) or should i use the code from the manual? Anyone has any experience with this issue?
Ok then, the answer is more simple then it seems at first sight You have to leave the solution with vtype (which is difficult and it doesn't work). Then you have to add a listener to yours:
xtype: "datefield",
the code is here:
{
xtype: 'datefield',
fieldLabel: 'Start date',
format: 'Ymd',
id:"start",
//altFormats: 'Ymd',
vtype: 'daterange',
endDateField: 'end',
listeners:{
'change': function(th,a){
Ext.getCmp('end').setMinValue(a);
}
}},
It changes the minValue of the second datefield when the first datefield is changed.
It is simple, it worked for me.

Selectfield in sencha touch doesn't respect an empty value

I've noticed a problem with ST2 and selectfield pickers. I'm testing this on Desktop browser and tablet and both seem to show the same problem.
The problem seems to stem from having form data that is empty or uninitialised.
My example is a user logs into their account and needs to set their marital status. As this has never been set before the backing store model is actually 'null' for their marital status. When they click the picker, the pick for some reason picks the first item in the checklist automatically. This is evident by the check-mark on the right side of the item. The 2nd side-effect of this is, if you then select the first item, ST2 doesn't see this as an item change and so doesn't then propagate the selection change back to the form.
Is this is a bug? How do I get round this problem?
Ext.define('Gender', {
extend: 'Ext.data.Model',
config: {
fields: [
{name: 'Id', type: 'int'},
{name: 'ItemName', type: 'string'}
]
}
});
Ext.define('Details', {
extend: 'Ext.data.Model',
config: {
fields: [
{name: 'Gender', type: 'int'}
]
}
});
var myGenderStore = Ext.create('Ext.data.Store', {
model: 'Gender',
data : [
{Id: 1, ItemName: 'Male'},
{Id: 2, ItemName: 'Female'}
]
});
var myDetailsStore = Ext.create('Ext.data.Store', {
model: 'Details',
data : [
{ Gender: null }
]
});
var p = Ext.create('Ext.form.Panel', {
fullscreen: true,
items: [
{
xtype: 'fieldset',
title: 'Select',
items: [
{
xtype: 'selectfield',
label: 'Choose one',
displayField: 'ItemName',
valueField: 'Id',
store: myGenderStore,
name: 'Gender'
}
]
}
]
});
p.setRecord(myDetailsStore.getAt(0))
Ext.Viewport.setActiveItem(p);
// notice the picker has 'Male' selected even though the backing store for the Gender field is null
// also, we want to select Male from the list, but this isn't reflected on the form
// run below command in console window after selecting 'Male' even though it is selected and it shows null
// It only seems to like changes to the value as selecting female works. If then select Male from Female this also works.
p.getValues().Gender;
you can set value for the select field
p.down('selectfield[name=Gender]').setValue(myGenderStore.getAt(0).get('Id'))
see example: http://www.senchafiddle.com/#OuCtQ#GQJ2C
thanks
Just set the autoSelect config property to false,to prevent default selection like this:
{
xtype: 'selectfield',
label: 'Choose one',
displayField: 'ItemName',
valueField: 'Id',
store: myGenderStore,
name: 'Gender',
autoSelect:false
}

Generate items in Ext.dataview.List from hasMany models the MVC way

I have a Blog model with hasMany Posts (and many other fields). Now I want to list these posts in a List-view like that:
[My post #1]
[My post #2]
[My post #3]
As far as the API described, I'm able to pass either a store or a data attribute to Ext.dataview.List. But I was not able to find out how to pass the hasMany records to the list so it will display an item for each of them.
Do I really have to create another store? Isn't it possible to configure my dataview to something like store: 'Blog.posts' or data: 'Blog.posts' or even records: 'Blog.posts'?
Extend the dataview.List to define the itemtpl to loop through the posts
itemTpl: new Ext.XTemplate(
'<tpl for="**posts**" >',
'<div>{TheBlogPost}</div>',
'</tpl>'
)
As #Adam Marshall said, this doesn't work as easy as I imagined.
Sencha autogenerates stores from associations if you know how to access them.
So you simply can switch out the list's store for the autogenerated "substore" when it has loaded.
This approach probably has some problems, e.g. when listpaging plugin is used, but it is quick.
Example:
MODELS
Ext.define('Conversation', {
extend: 'Ext.data.Model',
config: {
fields: [
],
associations:
[
{
type: 'hasMany',
model: "Message",
name: "messages",
associationKey: 'messages'
}
]
}
});
Ext.define('Message' ,
{
extend: "Ext.data.Model",
config: {
idProperty: 'id_message',
fields: [
{ name: 'text', type: 'string' },
{ name: 'date', type: 'string' },
{ name: 'id_message', type: 'int' },
{ name: 'me', type: 'int'} // actually boolean
]
}
}
);
JSON
[
{
"messages": [
{"id_message": 11761, "date": 1378033041, "me": 1, "text": "iiii"},
{"id_message": 11762, "date": 1378044866, "me": 1, "text": "hallo"}
]}
]
CONTROLLER
this.getList().getStore().load(
{
callback: function(records, operation, success) {
//IMPORTANT LINE HERE:
getList().setStore(Ext.getStore(me.getList().baseStore).getAt(0).messages());
},
scope: this
}
);
LIST-VIEW
{
flex: 1,
xtype: 'list',
itemId: 'ConversationList',
data: [],
store: 'ConversationStore',
baseStore: 'ConversationStore',
itemTpl:
' {[app.util.Helpers.DateFromTimestamp(values.date)]}<br><b>{name}</b>' +
' {[app.util.Helpers.fixResidualHtml(values.text)]} </div>' +
},

Populating Combobox from remote server

I have a combobox, i need to populate data from the server. In the server side i have the following data to be displayed.
PersonID
PersonFName
PersonLName
In the combobox, i need to display the text as PersonFName + PersonLName (Like James Smith- This is what it will display in the drop down) , and when a user selects a record, I need to display the corresponding PersonID (Like Person with PersonFName and PersonLName has the PersonID of 1) of that user.
I am unable to figure this out, here's my code
View :
{
xtype: 'combobox',
id: 'personcombo',
readOnly: false,
selectOnFocus: true,
forceSelection: true,
store: 'Person'
}
Store :
Ext.define('MyApp.store.PersonStore', {
extend: 'Ext.data.Store',
requires: [
'MyApp.model.Person'
],
constructor: function(cfg) {
var me = this;
cfg = cfg || {};
me.callParent([Ext.apply({
model: 'MyApp.model.Person',
proxy: {
type: 'ajax',
api: {
read: 'person.php',
create: 'person.php'
},
reader: {
type: 'array'
}
}
}, cfg)]);
}
});
Model :
Ext.define('MyApp.model.Person', {
extend: 'Ext.data.Model',
fields: [
{
name: 'PersonID'
},
{
name: 'PersonFName'
},
{
name: 'PersonLName'
}
]
});
I think your question is: how to display PersonFName + PersonLName in the combobox but keep the PersonID field as the value.
You should add a converted field which joins the first and last names in your data model and then make that one your combobox displayField config.
Though the other answer did bring up a good point that the defined store in your combo is Person but you are showing code for a store named PersonStore.
It would look something like this:
Model:
Ext.define('MyApp.model.Person', {
extend: 'Ext.data.Model',
fields: [
{
name: 'PersonID'
},
{
name: 'PersonFName'
},
{
name: 'PersonLName'
},
{
name: 'PersonName',
convert: function(value, record) {
return record.data.PersonFName + ' ' +
record.data.PersonLName;
}
}
]
});
Store:
// changed to "Person" instead of "PersonStore"
Ext.define('MyApp.store.Person', {
extend: 'Ext.data.Store',
requires: [
'MyApp.model.Person'
],
model: 'MyApp.model.Person',
proxy: {
type: 'ajax',
api: {
read: 'person.php',
create: 'person.php'
},
reader: 'array'
}
});
View:
{
xtype: 'combobox',
id: 'personcombo',
readOnly: false,
selectOnFocus: true,
forceSelection: true,
store: 'Person',
valueField: 'PersonID',
displayField: 'PersonName' // the converted field
}
Your combobox has 'Person' as the store, but I don't see you create a store called Person anywhere. Try store: Ext.create('MyApp.store.PersonStore', {autoLoad: true}).
You can also simplify your store:
Ext.define('MyApp.store.PersonStore', {
extend: 'Ext.data.Store',
requires: [
'MyApp.model.Person'
],
model: 'MyApp.model.Person',
proxy: {
type: 'ajax',
api: {
read: 'person.php',
create: 'person.php'
},
reader: 'array'
}
});

sencha touch insert data to localstorage

Hi i have found a lot of examples about loading data from a db in sencha.
i try to make a list with notes and on second step i want to be able to add(save) a note to my db. i try that on localstorage.
for now i load data from a array in my Arraystore.
where shall i set my proxy? (in store or in model?)
how could i insert data in my store?
i have tried something like that on my current arraystore but with no luck:
(this is the code run by pressing a code):
MyArrayStore.add({title:"newnote",narrative:"bla bla bla",date:now,id:noteid});
MyArrayStore.sync();
the browser console gets an error:
Uncaught ReferenceError: MyArrayStore is not defined
shall i make an instance of my store or something?
my model is this:
thanx for the answer. i try that on architect.
my model is this:
Ext.define('MyApp.model.NoteModel', {
extend: 'Ext.data.Model',
alias: 'model.NoteModel',
config: {
fields: [
{
name: 'id',
type: 'int'
},
{
name: 'date',
type: 'date'
},
{
name: 'title',
type: 'string'
},
{
name: 'narrative',
type: 'string'
}
],
proxy: {
type: 'localstorage',
id: 'local'
}
}
});
and my store is this:
Ext.define('MyApp.store.MyArrayStore', {
extend: 'Ext.data.Store',
requires: [
'MyApp.model.NoteModel'
],
config: {
data: [
{
title: 'Note 1',
narrative: 'test1 1'
},
{
title: 'Note 2',
narrative: 'narrative 2'
},
{
title: '3 ertyyh',
narrative: 'narrative 3'
},
{
title: '4 asdf',
narrative: 'narrative 4'
},
{
title: 'Note 5',
narrative: 'narrative 5'
},
{
title: 'weadf',
narrative: 'narrative 6'
}
],
model: 'MyApp.model.NoteModel',
storeId: 'MyArrayStore'
}
});
You should set your proxy in your model OR in your store.
Here's how to do it within your model.
Ext.define('MyModel', {
extend: 'Ext.data.Model',
config: {
fields: ['field1'],
proxy: {
type: 'localstorage',
id : 'my-model-localstorage-id'
}
});
The same can alternatively be done in your store.
After that, given that 'MyArrayStore' is an instance of such a store, the code you propose should work just fine.
Hope this helps.
If you want to access your store (the one you updated in your question), then you can use:
Ext.StoreManager.get('MyArrayStore')
So, for instance, the operations you wanted to perform could be done in the following way:
var store=Ext.StoreManager.get('MyArrayStore');
store.add({title:"newnote",narrative:"bla bla bla",date:now,id:noteid});
store.sync();