Sencha Touch Sync and Get New Data from Server - sencha-touch

My app is a list of ToDo's of forms that need to be completed.
When the app is opened, it goes to the server and collects (from a database) a list of forms to be completed.
When you click on a form you can then fill in the data (using LocalStorage proxy) and then save/update the data. The data is stored locally on the device.
As of now : When I open the app again, it collects the same list of ToDo's and overwrites the data in the LocalStorage (ie my filled up forms) with new empty forms and therefore I need to fill them again.
What I want : Instead of overwriting filled up forms I need to only collect those forms that are not already in my localstorage.
My Code :
Store :-
Code:
FMS.stores.onlineTodo = new Ext.data.Store({
model: 'ToDoMod',
proxy: {
id : 'fmsonlinetodo',
type: 'ajax',
url: 'app/data/dummydata.json',
reader: new Ext.data.JsonReader({
root: 'items'
}),
timeout: 2000,
listeners: {
exception:function () {
console.log("I think we are offline");
flagoffline = 1;
//
}
}
}
});
FMS.stores.offlineTodo = new Ext.data.Store({
model : 'ToDoMod',
proxy : {
type : 'localstorage',
id : 'fmsofflinetodo'
}
});
Controller function that loads data into store :
Code:
loadDataInitial : function(){
FMS.stores.onlineTodo.addListener('load', function () {
console.log("I think we are online");
FMS.stores.offlineTodo.proxy.clear();
FMS.stores.onlineTodo.each(function (record) {
FMS.stores.offlineTodo.add(record.data)[0];
});
FMS.stores.offlineTodo.sync();
FMS.stores.offlineTodo.load();
flagoffline = 0;
});
if(flagoffline == 0){
FMS.stores.onlineTodo.load();
}
else{
FMS.stores.offlineTodo.load();
}
},
HELP !!!!!

If I'm not mistaken, you are clearing all of your localStorage records when you use this:
FMS.stores.offlineTodo.proxy.clear();
What you would want to do is use the online store to collect all of the database records and then for each record, query local storage for the same record and if it exists, don't update it.
Basically a version control approach but definitely don't clear the store, you will delete everything in it!
UPDATED:
Here is some sample code:
//load remotestore
remoteStore.load({
scope: this,
callback: function (records, operation, success) {
//get record count
var localCount = localStore.getCount();
if (localCount == 0) {
//iterate each record in remotestore
remoteStore.each(function (record) {
//add record to localStorage
localStore.add(record.copy());
});
//save localstore
localStore.sync();
} else {
//set count var
var count = 0;
//iterate each record in remotestore
remoteStore.each(function (record) {
//reset var
var localRecord = null;
//find matching record in localstore
localRecord = localStore.findRecord('xid', record.data.xid, null, false, false, true);
//if the record exists
if (localRecord) {
//version check
if (record.data.version > localRecord.data.version) {
//remove record from localstore and add new one
localStore.remove(localRecord);
localStore.add(record.copy());
//increment counter
++count;
}
} else {
//add record to localstore
localStore.add(record);
}
});
//save localstore
if (localStore.sync()) {
alert("store saved");
}
//if records were added we need to reload
if (count > 0) {
this.onUpdate();// or whatever your function is.
}
}
}
}); //ends

in your store's load method, just pass addRecords:true, like so:
FMS.stores.onlineTodo.load({addRecords: true});

Related

Angularjs - what are the possible reasons for duplicate records being inserted by the following code?

The following code is called on the click of a button
$scope.someFunction = function () {
$scope.submitting = true; // the button is disabled if submitting is true
var query = { query: { id: $scope.employeeID } };
// this api call inserts a record in a table
httpFactory.patch("/someURL", query).then(function (data) {
$scope.submitting = false;
if (data.error) {
// display error message
}
else {
// display success message
}
$scope.submitting = false;
}, function () {
$scope.submitting = false;
});
};
can duplicate records be inserted from the call above if a user has poor connectivity or if the server is slow and the request is not completed and soon another same request is received?
If so.. could any one please suggest a suitable way to handle this?

In an ExtJS Grid, how do I get access to the data store fields that are part of the sort set

How do I get access to the columns/datastore fields that are part of the sort set.
I am looking to modify the a grid's sort parameters for remote sorting. I need the remote sort param's sort key to match the column's field's mapping property. I need these things to happen though the normal 'column header click sorts the data' functionality.
Remote sorting and field mapping (ExtJS 4.1)
This functionality seems not to be implemented in ExtJS. Here is a solution using the encodeSorters function provided since ExtJS 4. Accessing fields map throught the model's prototype is a bit dirty but it does the job :
var store = Ext.create('Ext.data.Store', {
...,
proxy: {
...,
encodeSorters: function (sorters) {
var model = store.proxy.model,
map = model.prototype.fields.map;
return Ext.encode(Ext.Array.map(sorters, function (sorter) {
return {
property : map[sorter.property].mapping || sorter.property,
direction: sorter.direction
};
}));
}
}
});
However, it would be more relevant to override the original method :
Ext.data.proxy.Server.override({
encodeSorters: function(sorters) {
var min, map = this.model.prototype.fields.map;
min = Ext.Array.map(sorters, function (sorter) {
return {
property : map[sorter.property].mapping || sorter.property,
direction: sorter.direction
};
});
return this.applyEncoding(min);
}
});
Assuming you are using simpleSortMode, you could do something like this in your store.
listeners: {
beforeload: function( store, operation, eOpts ) {
if (store.sorters.length > 0) {
var sorter = store.sorters.getAt(0),
dir = sorter.direction,
prop = sorter.property,
fields = store.model.getFields(),
i,
applyProp = prop;
for (i = 0; i < fields.length; i++) {
if (fields[i].name == prop) {
applyProp = fields[i].mapping || prop;
break;
}
}
//clearing the sorters since the simpleSortMode is true so there will be only one sorter
store.sorters.clear();
store.sorters.insert(0, applyProp, new Ext.util.Sorter({
property : applyProp,
direction: dir
}));
}
}
},

Filtering epics from Kanban board

I would like to start by saying I have read Rally Kanban - hiding Epic Stories but I'm still having trouble on implementing my filter based on the filter process from the Estimation Board app. Currently I'm trying to add an items filter to my query object for my cardboard. The query object calls this._getItems to return an array of items to filter from. As far as I can tell the query calls the function, loads for a second or two, and then displays no results. Any input, suggestions, or alternative solutions are welcomed.
Here's my code
$that._redisplayBoard = function() {
that._getAndStorePrefData(displayBoard);
this._getItems = function(callback) {
//Build types based on checkbox selections
var queries = [];
queries.push({key:"HierarchicalRequirement",
type: "HierarchicalRequirement",
fetch: "Name,FormattedID,Owner,ObjectID,Rank,PlanEstimate,Children,Ready,Blocked",
order: "Rank"
});
function bucketItems(results) {
var items = [];
rally.forEach(queries, function(query) {
if (results[query.key]) {
rally.forEach(results[query.key], function(item) {
//exclude epic stories since estimates cannot be altered
if ((item._type !== 'HierarchicalRequirement') ||
(item._type === 'HierarchicalRequirement' && item.Children.length === 0)) {
items = items.concat(item);
}
});
}
});
callback(items);
}
rallyDataSource.findAll(queries, bucketItems);
};
function displayBoard() {
artifactTypes = [];
var cardboardConfig = {
types: [],
items: that._getItems,
attribute: kanbanField,
sortAscending: true,
maxCardsPerColumn: 200,
order: "Rank",
cardRenderer: KanbanCardRenderer,
cardOptions: {
showTaskCompletion: showTaskCompletion,
showAgeAfter: showAgeAfter
},
columnRenderer: KanbanColumnRenderer,
columns: columns,
fetch: "Name,FormattedID,Owner,ObjectID,Rank,Ready,Blocked,LastUpdateDate,Tags,State,Priority,StoryType,Children"
};
if (showTaskCompletion) {
cardboardConfig.fetch += ",Tasks";
}
if (hideLastColumnIfReleased) {
cardboardConfig.query = new rally.sdk.util.Query("Release = null").or(kanbanField + " != " + '"' + lastState + '"');
}
if (filterByTagsDropdown && filterByTagsDropdown.getDisplayedValue()) {
cardboardConfig.cardOptions.filterBy = { field: FILTER_FIELD, value: filterByTagsDropdown.getDisplayedValue() };
}
cardboardConfig.types.push("HierarchicalRequirement");
if (cardboard) {
cardboard.destroy();
}
artifactTypes = cardboardConfig.types;
cardboard = new rally.sdk.ui.CardBoard(cardboardConfig, rallyDataSource);
cardboard.addEventListener("preUpdate", that._onBeforeItemUpdated);
cardboard.addEventListener("onDataRetrieved", function(cardboard,args){ console.log(args.items); });
cardboard.display("kanbanBoard");
}
};
that.display = function(element) {
//Build app layout
this._createLayout(element);
//Redisplay the board
this._redisplayBoard();
};
};
Per Charles' hint in Rally Kanban - hiding Epic Stories
Here's how I approached this following Charles' hint for the Rally Catalog Kanban. First, modify the fetch statement inside the cardboardConfig so that it includes the Children collection, thusly:
fetch: "Name,FormattedID,Children,Owner,ObjectID,Rank,Ready,Blocked,LastUpdateDate,Tags,State"
Next, in between this statement:
cardboard.addEventListener("preUpdate", that._onBeforeItemUpdated);
And this statement:
cardboard.display("kanbanBoard");
Add the following event listener and callback:
cardboard.addEventListener("onDataRetrieved",
function(cardboard, args){
// Grab items hash
filteredItems = args.items;
// loop through hash keys (states)
for (var key in filteredItems) {
// Grab the workproducts objects (Stories, defects)
workproducts = filteredItems[key];
// Array to hold filtered results, childless work products
childlessWorkProducts = new Array();
// loop through 'em and filter for the childless
for (i=0;i<workproducts.length;i++) {
thisWorkProduct = workproducts[i];
// Check first if it's a User Story, since Defects don't have children
if (thisWorkProduct._type == "HierarchicalRequirement") {
if (thisWorkProduct.Children.length === 0 ) {
childlessWorkProducts.push(thisWorkProduct);
}
} else {
// If it's a Defect, it has no children so push it
childlessWorkProducts.push(thisWorkProduct);
}
}
filteredItems[key] = childlessWorkProducts;
}
// un-necessary call to cardboard.setItems() was here - removed
}
);
This callback should filter for only leaf-node items.
Mark's answer caused an obscure crash when cardboard.setItems(filteredItems) was called. However, since the filtering code is actually manipulating the actual references, it turns out that setItems() method is actually not needed. I pulled it out, and it now filters properly.
Not sure this is your problem but your cardboard config does not set the 'query' field. The fetch is the type of all data to retrieve if you want to filter it you add a "query:" value to the config object.
Something like :
var cardboardConfig = {
types: ["PortfolioItem", "HierarchicalRequirement", "Feature"],
attribute: dropdownAttribute,
fetch:"Name,FormattedID,Owner,ObjectID,ClassofService",
query : fullQuery,
cardRenderer: PriorityCardRenderer
};
Where fullQuery can be constructed using the the Rally query object. You find it by searching in the SDK. Hope that maybe helps.

Problem with list search in sencha

I am using search option in the list . There is provision to add new items to the list. The problem is that when I add a new item to the list, the list is getting updated , but I cannot search that item through the search field. But after refreshing the browser, we can. But it is not possible to refresh browser each time....... Is there any solution for this problem?
Here is the code I am using to search the list.
xtype: 'searchfield',
placeHolder: 'Search',
name: 'searchfield',
id:'subListSearch',
listeners : {
scope: this,
'focus': function() {
Ext.getCmp('xbtn').show();
},
keyup: function(field) {
var value = field.getValue();
if (!value) {
Store.filterBy(function() {
return true;
});
} else {
var searches = value.split(' '),
regexps = [],
i;
for (i = 0; i < searches.length; i++) {
if (!searches[i]) return;
regexps.push(new RegExp(searches[i], 'i'));
};
Store.filterBy(function(record) {
var matched = [];
for (i = 0; i < regexps.length; i++) {
var search = regexps[i];
if (record.get('Name').match(search)) matched.push(true);
else matched.push(false);
};
if (regexps.length > 1 && matched.indexOf(false) != -1) {
return false;
} else {
return matched[0];
}
});
}
}
}
There is also some other problems. I using some provision to filter the list. But when I uses the search option, it is searching through the entire list, not the filtered list.why?
Thanks
Arun A G
Thanks for responding to my question .
The problem is fixed with bindStore() method. Earlier I was doing load() method to render the new data into the store. But we can not search the last entered item with this method. After binding the Changed store into the list with bindStore() method, the issue was solved.

looping through DOM / mootools sortables

I can't seem to get a handle on my list of sortables. They are a list of list elements, each with a
form inside, which I need to get the values from.
Sortables.implement({
serialize: function(){
var serial = [];
this.list.getChildren().each(function(el, i){
serial[i] = el.getProperty('id');
}, this);
return serial;
}
});
var sort = new Sortables('.teams', {
handle: '.drag-handle',
clone: true,
onStart: function(el) {
el.fade('hide');
},
onComplete: function(el) {
//go go gadget go
order = this.serialize();
alert(order);
for(var i=0; i<order.length;i++) {
if (order[i]) {
//alert(order[i].substr(5, order[i].length));
}
}
}
});
the sortables list is then added to a list in a loop with sort.addItems(li); . But when I try to get the sortables outside of the sortables onComplete declaration, js says this.list is undefined.
Approaching the problem from another angle:
Trying to loop through the DOM gives me equally bizarre results. Here are the firebug console results for some code:
var a = document.getElementById('teams').childNodes;
var b = document.getElementById('teams').childNodes.length;
try {
console.log('myVar: ', a);
console.log('myVar.length: ', b);
} catch(e) {
alert("error logging");
}
Hardcoding one li element into the HTML (rather than being injected via JS) changes length == 1, and allows me to access that single element, leading me to believe that accessing injected elements via the DOM is the problem (for this method)
Trying to get the objects with document.getElementById('teams').childNodes[i] returns undefined.
thank you for any help!
not sure why this would fail, i tried it in several ways and it all works
http://www.jsfiddle.net/M7zLG/ test case along with html markup
here is the source that works for local refernece, using the native built-in .serialize method as well as a custom one that walks the dom and gets a custom attribute rel, which can be your DB IDs in their new order (I tend to do that)
var order = []; // global
var sort = new Sortables('.teams', {
handle: '.drag-handle',
clone: true,
onStart: function(el) {
el.fade('hide');
},
onComplete: function(el) {
//go go gadget go
order = this.serialize();
}
});
var mySerialize = function(parentEl) {
var myIds = [];
parentEl.getElements("li").each(function(el) {
myIds.push(el.get("rel"));
});
return myIds;
};
$("saveorder").addEvents({
click: function() {
console.log(sort.serialize());
console.log(order);
console.log(mySerialize($("teams")));
}
});