Updated records don't match Store.sync() - extjs

I am having trouble when updating my store
Ext.define("ManageShows.model.Show", {
extend: "Ext.data.Model",
config: {
idProperty: 'id',
fields: [
{ name: 'id' , type: 'int'},
{ name: 'name', type: 'string' }
],
validations: [
{ type: 'presence', field: 'name', message: 'Please enter a name for this show.'}
]
}
});
When i try to sync() i get this error
[WARN][Ext.data.Operation#process] Unable to match the updated record that came back from the server.
The List holding the data then contains duplicate entries of the first element in the list. But if i refresh the page my list displays correctly with the updated term.
I don't know what part of the records that don't match. The only different i can see is that Store.id = "ext-record-4" which seem to be auto generated and i am not saving this value in my database but Store.data.id = {id from database}.
How can i find what is not matching?
EDIT:
Important note is that i get the error 7 times in a row and i have 8 entries. If i updated the first entry then the changes work and is displayed corrently

I stopped my servlet from returning json data after a sync() call. This wasn't need because sencha touch updates the data locally automatically

Related

Extjs - How to access/modify the data before the Ext.data.Batch starts

-- Extjs - 6.6.0, Extjs Grid with Cell Editing plugin, record save operation to the server
There is a grid in my application interface with a proper store and model attached to the store. The grid has a date column with a date editor with the render format as 'd-M-Y' and the date value to submit to the server is 'Y-m-d'. During the save operation through a batch start with the session, I am unable to access the data to modify the date format to 'Y-m-d' and is being submitted as 'd-M-Y' which causing an issue.
Can anyone please provide the information on accessing/modifying the data before the Ext.Data.Batch starts.
Note:
I cannot replace the batch with Ajax call.
I already tried dateWriteFormat for the Model class as well.
The date display & submit formats must be 'd-M-Y' & 'Y-m-d'.
I cannot post the code as it is too large.
Any sort of help would be much appreciated.
There are a couple of ways.
FIELD
You might want to edit the Model of the store via serialize.
Ext.define('MyApp.model.Users', {
extend: 'Ciss.data.Model',
fields: [{
name: 'BirthDate',
type: 'date',
dateFormat: 'd m Y', // how the date format comes from the server
serialize: function (val) {
return Ext.Date.format(val, 'Y-m-d');
}
}];
});
WRITER
You can use the writer using the dateFormat
writer: {
type: 'json',
dateFormat: 'Y-m-d',
writeAllFields: true
}
while your field should be of the type date, all the time
If you still need more control you can use the transform method.
Ext.create('Ext.data.Store', {
model: 'User',
proxy: {
type: 'ajax',
url : 'users.json',
writer: {
type: 'json',
transform: {
fn: function(data, request) {
// do some manipulation of the unserialized data object
return data;
},
scope: this
}
}
},
});
EVENT
You can use the store beforesync event
LAST HOPE
If all this does not work for you you can grab every request before it's send.

Trying to get a basic combo to work in ExtJS

I want to use a very simple combo box in ExtJS, but I was surprised to learn that it seems as though I have to complexify things by using a store.
I have a single array of data :
var states = [
{"name":"Alabama"},
{"name":"Alaska"}
]
I create my model 'State' linking to the 'name' field, and then I create my store linking to the model, and the array of data.
Ext.regModel('State', {
fields: [
{type: 'string', name: 'name'}
]
});
var store1 = Ext.create('Ext.data.Store', {
model: 'State',
data: states
});
Now I create my combo, as a field in my panel :
var f = Ext.create('Ext.form.Panel', {
items: [
{
fieldLabel: 'hi there',
xtype: 'combobox',
name: 'XXXX',
store:store1,
maxLength: 64,
allowBlank: false
}
]
})
Nothing tells me that I am doing anything wrong, but I get an 'Uncaught TypeError: Cannot read property 'indexOf' of undefined ' whenever I try and open the combo.
My fiddle is here :
http://jsfiddle.net/sr61tpmd/1/
Another aside to my question is, what is the simplest way I can present a combobox in ExtjS?
As long as you only want a combo box with same value as displayed, it is entirely possible to define the store as an array.
xtype:'combo',
store:['Alabama','Arkansas',...]
A real extjs store is necessary where your displayed text differs from the value. You can see a working example for this (also using the us states, actually) in the ext docs: http://docs.sencha.com/extjs/4.2.2/#!/api/Ext.form.field.ComboBox

Paging Grid displaying all records - Ext JS

EDIT
It turns out that a store cannot have duplicate IDs. When i remove this field, All records display - which means the grid is not respecting the pageSize
I'm having an issue getting all my store records to display in my grid. The data is returning appropriately from a JSON request, and the pagingtoolbar behaves correctly.
Here's my store:
var store = Ext.create('Ext.data.Store', {
storeId : 'resultsetstore',
autoLoad : false,
model : model,
pageSize : itemsPerPage, // 3
proxy: {
type : 'ajaxwithpayload', //custom ajax proxy to read 'jsonData'
url : 'MCApp',
jsonData: searchquery,
reader: {
type : 'json',
root : 'elements'
}
}
});
I load the store here, and all the correct results are returned:
store.load({
params: { start: 0, limit: itemsPerPage },
callback : function(records, operation, success) {
if(success) {
mainresponse = operation.response.responseText;
if( mainresponse.length == 0 ){
alert('No results returned');
return;
}
var jsonResponse = Ext.JSON.decode(mainresponse);
}
else {
alert('Search Failed: Could not reach the server');
}
And lastly, a simple grid with pagingtoolbar:
var grid = Ext.create('Ext.grid.Panel', {
title: 'Test Data',
store: store,
columns: [{
text: 'Technical Name',
dataIndex: 'tech'
}, {
text: 'ID',
dataIndex: 'element.id'
}, {
text: 'Name',
dataIndex: 'name',
mapping: 'element.name'
}, {
text: 'View',
dataIndex: 'view'
}, {
text: 'Description',
dataIndex: 'description'
}],
dockedItems: [{
xtype: 'pagingtoolbar',
store: store,
pageSize: itemsPerPage,
dock: 'bottom',
displayInfo: true
}],
renderTo: Ext.getBody()
});
The problem here is that not all of my results load into the grid. It seems that when a duplicate ID is encountered, it cuts the results short. That may not be the problem, just my guess
In my logs I see all the data I need returned. In the picture below, I should have 3 rows with the same Element ID 001, however only one is displayed:
Another thing to note is that when I click the Next Button on my pagingtoolbar, the results do not change, and it also reads as a POST on my console. I'm not sure if it's supposed to do that or be a GET?
I used lightning bolts to show how shocking it is that this isn't working
Any ideas?
A very important thing to note is that the server is responsible for paging not ExtJS. The server must not return all rows, but only the rows of the current page. You server side code must take in account the parameters page, start and limit for getting the client side to work.
I think that, beside the duplicated ids, is the main reason for your problems. It is especially the reason, why you see the same data on page one and on page two:
ExtJs asks the server for the data of page 1 and gets all rows. Later, ExtJs asks the server for the data of page 2 and gets again the same rows. This cannot work.
This might be the case when you are giving name property as "id" in the fields array of your Model. try to change that.

How to insert association/child data into Ext.data.list as separate entries

I'm using ST2 and using MVC. I'm very new to ExtJS and Sencha, so am not au fair the best practices for many things - and on this issue I've hit a dead end despite research.
I'll use a toy example to illustrate my issue below, but essentially I have a relationship as follows (which all works correctly from an association perspective).
Business X -- Location A
|
-- Location N
The Problem
I want to then populate the data into (for instance) an Ext.dataview.List, but to process it such that each location (i.e. child location) has its own separate entry in the table; not just a simple itemTpl formatting a single entry. However, at present I can't find any way to do that. Is it possible to hook into a List and format the data as I want, or should I be creating a new store? Ideally I want to make best use of the associations.
As a rough example, each entry would look like this, with some parent data and some child data:
---------------------------
|Smith Co - 1 Smith Street|
---------------------------
|Smith Co - 24 High Street|
---------------------------
|Tea[...] - 12 Tea Leaf |
---------------------------
|Tea[...] - 3 Bis Kit |
---------------------------
Example Code
Raw data
[
{
"id":1,
"name":"Smith Co",
"locations":[
{
"address":"1 Smith Street"
},
{
"address":"24 High Street"
}
]
},
{
"id":2,
"name":"Tea So Good",
"locations":[
{
"address":"12 Tea Leaf"
},
{
"address":"3 Bis Kit"
}
]
}
]
Location Model
Ext.define('Example.model.Location', {
extend: 'Ext.data.Model',
config: {
fields: [
{ name: 'address', type: 'string' }
],
proxy: { ... }, // Rest proxy that loads data as shown above.
BelongsTo: 'Example.model.Company'
}
});
Company Model
Ext.define('Example.model.Company', {
extend: 'Ext.data.Model',
config: {
fields: [
{ name: 'id', type: 'int' },
{ name: 'name', type: 'string' }
],
proxy: { ... }, // Rest proxy that loads data as shown above.
hasMany: { model: 'Example.model.Location', name: 'locations' }
}
});
Store
Ext.define('Example.store.Companies', {
extend: 'Ext.data.Store',
require: 'Example.model.Company',
config: {
model: 'Example.model.Company'
}
});
Controller
// (works correctly, relationships are traversable)
// Companies store is looked up and loaded in #launch()
View
Ext.define('Example.view.CompaniesList', {
extend: 'Ext.List',
xtype: 'companieslist',
config: {
layout: 'fit',
store: 'Businesses',
itemTpl: [
// Tpl is only to format inside each element
]
}
});
Solution Edit (15th Sept 2013):
Solution
I used the solution #rixo suggested (and I had been hoping to avoid in the original question).
I created a separate store for the list, and loaded the data I need into it by using a load listener on the Companies store. This seems to be the most graceful solution available, although it means you may need to add extra logic in various places to ensure it remains satisfactorily synchronised.
By pushing the location objects themselves into the new store the associations remain intact (i.e. you can still do location.getCompany()).
Yes, create another store for locations.
You may have tried a template like this:
itemTpl: [
'{name}',
'<tpl for="locations">',
', {address}',
'</tpl>'
]
But that will indeed only display the information, it won't let you interact with each location as an individual list item.
You could get it working by hacking the view's doRefresh method, but that's just going against the lib's intention and other developer' expectations.
Maybe the problem is that you can get the data only in this format, that is with locations as children of companies, and you can't get the server to send you a flat list of companies. In that case, I think the most meaningful approach would be to customize a reader to flatten locations from companies, and feed a standalone location store. The extractData method seems a very promising start for that (see how the JSON reader uses it to implements its root property).

ExtJS 4.2: Multiple Inserts

Ok, so say I have a store server side so we are doing remote everything. Example of stores:
Ext.create('Ext.data.Store', {
model: 'MyApp.model.ContactModel',
remoteFilter: true,
remoteSort: true,
autoLoad: true,
autoSync: true,
storeId: 'ContactStore-1'
});
Ext.create('Ext.data.Store', {
model: 'MyApp.model.ContactModel',
remoteFilter: true,
remoteSort: true,
autoLoad: true,
autoSync: true,
storeId: 'ContactStore-2'
});
I hit a problem when I do the following:
Ext.getStore('ContactStore-1').insert(0,{'name':'say'});
Ext.getStore('ContactStore-2').insert(0,{'name':'hi'});
What happens is that when I look at the DB I end up having 2 entries. I get 'hi' once and 'say' twice. From the looks of it what is happening is that the first insert statement gets sent and then the second insert statement gets sent but with data from both inserts (I assume it's cause they share the same model and thus the same proxy)
Thoughts on how I can resolve this so that it doesn't auto merge insertion requests?
Model for your viewing pleasure:
Ext.define('MyApp.model.ContactModel', {
extend: 'Ext.data.Model',
idProperty: 'idContact',
fields: [
{
name: 'idContact',
type: 'int'
},
{
name: 'name',
type: 'string'
}
],
proxy: {
type: 'direct',
api: {
create: contact.createRecord,
read: contact.getResults,
update: contact.updateRecords,
destroy: contact.destroyRecord
},
reader: {
type: 'json',
root: 'data'
}
}
});
I think you are not returning the correct data from the server side on the create. If you do not return the id the server created on the first insert, ExtJS will still think your "say" item is a phantom. That is, it has not yet been stored server side.
When you do the second insert, the store will do a sync as you have autosync on. Sync will send ALL pending changes. Since your "hi" item is new that will be sent in a POST as is expected. But since your previous "hi" item does not have a server generated id and is still a phantom, it too will be sent in a POST with your second sync (triggered by the insert).
Basically the server must return the new id with the success result set so that ExtJS knows that the item has been stored by the server. Here's an example from my REST API:
Request has this payload (check Chrome Developer tools network tab).
POST to http://localhost:8081/api/channel?_dc=1372594759864
{"id":0,"number":0,"name":"test"}
This is the server response (200 OK):
{
"result": {
"id": 4, // <- important
"number": 3,
"name": "test",
},
"success": true,
"location": "http://localhost:8081/api/item/4",
"userMessage": null,
"userTitle": "Success",
"devErrors": null
}
All fields in the model are updated by the data in the server response, and as such your hi and say items will get their server id's set. When the id is set, the phantom property is set to false. You can have a look in the Ext.data.Model js-file for the source code for this if you want to dig deeper. :)
In your case you must have idContact in the returned object as that is you idProperty.
If you want to suspend auto sync, do your inserts and sync manually and then turn auto syncs back on you can use SuspendAutoSync and ResumeAutoSync.
The best way to add models to stores in my opinion is to create your model, save it, and on success put it in the store. That requires you to have the proxy in each model. That would look something like this:
var hi = Ext.create('MyApp.model.ContactModel', {
name: 'hi'
});
hi.save({
success: function (record, operation) {
Ext.getStore('ContactStore-1').add(hi);
// You could do your second insert here that is dependent on the first to be completed
},
failure: function (record, operation) {
Ext.MessageBox.alert("Error", "Could not save model at this time...");
},
scope: this
});
In this way you could add your second item in the success handler of the first item's save.

Resources