I've been assigned a task to extent and modify a Shopware plugin. The original author isn't in the company anymore. Before that I've never dealt with Shopware and ExtJs.
So I spend the last couple of days getting myself into it and I think I understood the principles and paradigm so far.
The only thing I'm having trouble with right now is the following issue:
I've got an Ext.tree.Panel which I want to save into a database using Ajax. The node is being added to the tree, I can see it appearing in the GUI. But after calling optionsTree.getStore().sync() there is nothing arriving in the database. The createProductOptionAction() in the PHP controller isn't called, but I can't figure out why. There is no error message in Browser console log, no error message in the Shopware log files. Nothing. Everything seems to work fine. But the data isn't being stored.
The original plugin had an Ext.grid.Panel to store and display data. And this works fine. But after changing to Ext.tree.Panel and modifying the code, it doesn't work anymore. From my point of view it should work tho. But it doesn't and I can't see my mistake(s).
Any help is really appreciated, I'm still a bloody beginner with ExtJs. :)
Here is what I've got so far:
app.js
Ext.define('Shopware.apps.CCBConfigurablePhotoProductsManager', {
extend:'Enlight.app.SubApplication',
name:'Shopware.apps.CCBConfigurablePhotoProductsManager',
bulkLoad: true,
loadPath:'{url controller="CCBConfigurablePhotoProductsManager" action="load"}',
controllers:['ProductConfigurator'],
stores:['ProductOptionsList'],
models:['ProductOption'],
views: ['ProductOptions', 'Window' ],
launch: function() {
var me = this,
mainController = me.getController('ProductConfigurator');
return mainController.mainWindow;
}
});
controller/controller.js
Ext.define('Shopware.apps.CCBConfigurablePhotoProductsManager.controller.ProductConfigurator', {
extend:'Ext.app.Controller',
refs: [
{ ref: 'productOptionsTree', selector: 'product-configurator-settings-window product-options-tree' }
],
init:function () {
var me = this;
me.mainWindow = me.createMainWindow();
me.addControls();
me.callParent(arguments);
return me.mainWindow;
},
addControls: function() {
var me = this;
me.control({
'product-configurator-settings-window product-options-tree': {
addProductOption: me.onAddProductOption
}
});
},
createMainWindow: function() {
var me = this,
window = me.getView('Window').create({
treeStore: Ext.create('Shopware.apps.CCBConfigurablePhotoProductsManager.store.ProductOptionsList').load()
}).show();
return window;
},
onAddProductOption: function() {
var me = this,
optionsTree = me.getProductOptionsTree(),
parentNode = optionsTree.getRootNode(),
nodeCount = parentNode.childNodes.length + 1,
productOption = Ext.create('Shopware.apps.CCBConfigurablePhotoProductsManager.model.ProductOption', {
parent: 0,
type: 0,
title: Ext.String.format('{s name="group/default_name"}New Group [0]{/s}', optionsTree.getRootNode().childNodes.length + 1),
active: true,
leaf: false
});
productOption.setDirty();
parentNode.appendChild(productOption);
optionsTree.getStore().sync(); // Nothing arrives at DB
optionsTree.expandAll();
},
// ...
view/window.js
Ext.define('Shopware.apps.CCBConfigurablePhotoProductsManager.view.Window', {
extend:'Enlight.app.Window',
cls:Ext.baseCSSPrefix + 'product-configurator-settings-window',
alias:'widget.product-configurator-settings-window',
border:false,
autoShow:true,
maximizable:true,
minimizable:true,
layout: {
type: 'hbox',
align: 'stretch'
},
width: 700,
height: 400,
initComponent:function () {
var me = this;
me.createItems();
me.title = '{s name=window/title}Configurator Settings{/s}';
me.callParent(arguments);
},
createItems: function() {
var me = this;
me.items = [
me.createProductOptionsTree()
];
},
createProductOptionsTree: function() {
var me = this;
return Ext.create('Shopware.apps.CCBConfigurablePhotoProductsManager.view.ProductOptions', {
store: me.treeStore,
width: '20%',
flex: 1
});
}
});
store/product_options_list.js
Ext.define('Shopware.apps.CCBConfigurablePhotoProductsManager.store.ProductOptionsList', {
extend: 'Ext.data.TreeStore',
pageSize: 30,
autoLoad: false,
remoteSort: true,
remoteFilter: true,
model : 'Shopware.apps.CCBConfigurablePhotoProductsManager.model.ProductOption',
proxy:{
type:'ajax',
url:'{url controller="CCBConfigurablePhotoProductsManager" action="getProductOptionsList"}',
reader:{
type:'json',
root:'data',
totalProperty:'total'
}
}
});
model/product_option.js
Ext.define('Shopware.apps.CCBConfigurablePhotoProductsManager.model.ProductOption', {
extend : 'Ext.data.Model',
fields : [
{ name : 'id', type : 'int', useNull: true },
{ name : 'parent', type : 'int' },
{ name : 'title', type : 'string' },
{ name : 'active', type: 'boolean' },
{ name : 'type', type : 'int' }
],
idProperty : 'id',
proxy : {
type : 'ajax',
api: {
create: '{url controller="CCBConfigurablePhotoProductsManager" action="createProductOption"}',
update: '{url controller="CCBConfigurablePhotoProductsManager" action="updateProductOption"}',
destroy: '{url controller="CCBConfigurablePhotoProductsManager" action="deleteProductOption"}'
},
reader : {
type : 'json',
root : 'data',
totalProperty: 'total'
}
}
});
php/controller.php
<?php
use Shopware\CustomModels\CCB\ProductOption;
class Shopware_Controllers_Backend_CCBConfigurablePhotoProductsManager extends Shopware_Controllers_Backend_ExtJs
{
public function createProductOptionAction()
{
// Never being called
file_put_contents('~/test.log', "createProductOptionAction\n", FILE_APPEND);
$this->View()->assign(
$this->saveProductOption($this->Request()->getParams())
);
}
public function getProductOptionsListAction()
{
// Works fine
file_put_contents('~/test.log', "getProductOptionsListAction\n", FILE_APPEND);
// ...
}
// ...
EDIT 1
I tried adding a writer for both, the store and the model, as suggested by Saki. But unfortunately it still doesn't work. The createProductOptionAction() in the PHP controller is never being called.
Ext.define('Shopware.apps.CCBConfigurablePhotoProductsManager.model.ProductOption', {
extend : 'Ext.data.Model',
fields : [
{ name : 'id', type : 'int', useNull: true },
{ name : 'parent', type : 'int' },
{ name : 'title', type : 'string' },
{ name : 'active', type: 'boolean' },
{ name : 'type', type : 'int' }
],
idProperty : 'id',
proxy : {
type: 'ajax',
api: {
create: '{url controller="CCBConfigurablePhotoProductsManager" action="createProductOption"}',
update: '{url controller="CCBConfigurablePhotoProductsManager" action="updateProductOption"}',
destroy: '{url controller="CCBConfigurablePhotoProductsManager" action="deleteProductOption"}'
},
reader: {
type : 'json',
root : 'data',
totalProperty: 'total'
},
writer: {
type: 'json'
}
}
});
What I'm wondering tho, the original plugin had no writer implemented. But when adding an entry it immediately appeared in the database.
EDIT 2
I added several listeners to the store.ProductOptionsList:
Ext.define('Shopware.apps.CCBConfigurablePhotoProductsManager.store.ProductOptionsList', {
extend: 'Ext.data.TreeStore',
pageSize: 30,
autoLoad: false,
remoteSort: true,
remoteFilter: true,
model : 'Shopware.apps.CCBConfigurablePhotoProductsManager.model.ProductOption',
root: {
text: 'Product Options',
id: 'productOptions',
expanded: true
},
proxy:{
type: 'ajax',
url: '{url controller="CCBConfigurablePhotoProductsManager" action="getProductOptionsList"}',
reader: {
type:'json',
root:'data',
totalProperty:'total'
}
},
listeners: {
add: function(store, records, index, eOpts) {
console.log("**** Add fired");
console.log(records);
},
append: function(store, node, index, eOpts) {
console.log("**** Append fired");
console.log(node);
},
beforesync: function(operations) {
console.log("**** Beforesync fired");
console.log(operations);
}
}
});
All these Events are getting fired. The beforesync event shows
**** Beforesync fired
Object {create: Array[1]}
create: Array[1]
...
But still, the API requests of the model.ProductOption are not getting fired. It should work. Shouldn't it? Maybe this is a bug in ExtJS 4.1? Or something with Shopware + ExtJS?
EDIT 3
Ok, this is really getting weird.
I added a "write"-Listener to the TreeStore.
write: function(store, operation, opts){
console.log("**** Write fired");
console.log(operation);
Ext.each(operation.records, function(record){
console.log("**** ...");
if (record.dirty) {
console.log("**** Commiting dirty record");
record.commit();
}
});
}
After adding a Node and calling .getStore().sync(), the write-event IS fired, he iterates operation.records, finds one record (the one I just added)... but it isn't dirty, even though I do productOption.setDirty() before adding it to the Tree?!
Thanks alot for your time! :)
A little note on your code:
There is an error in the beforesync event: the function must return true, else sync() will not get fired.
I don't think this is the only problem. Since ExtJs is usually extensive code, I cannot tell you what is the reason of your problem. All I can give, is a simple working example with some explanations.
I'm following the recommended MVC layout, i.e. one file for each class. Here is the complete code:
Ext.define('Sandbox.Application', {
name: 'Sandbox',
extend: 'Ext.app.Application',
controllers: [
'Sandbox.controller.Trees'
]
});
Ext.define('Sandbox.controller.Trees', {
extend: 'Ext.app.Controller',
requires: ['Ext.tree.*', 'Ext.data.*', 'Ext.grid.*'],
models: ['TreeTest'],
stores: ['TreeTest'],
views: ['TreeGrid'],
init: function(){
this.control({
'treegrid toolbar button#addchild': {click: this.onAddChild},
'treegrid toolbar button#removenode': {click: this.onRemoveNode}
})
},
onAddChild: function(el){
var grid = el.up('treepanel'),
sel = grid.getSelectionModel().getSelection()[0],
store = grid.getStore();
store.suspendAutoSync()
var child = sel.appendChild({task: '', user: '', leaf: true});
sel.set('leaf', false)
sel.expand()
grid.getView().editingPlugin.startEdit(child);
store.resumeAutoSync();
},
onRemoveNode: function(el){
var grid = el.up('treepanel'),
sel = grid.getSelectionModel().getSelection()[0];
sel.remove()
}
});
Ext.define('Sandbox.model.TreeTest', {
extend: 'Ext.data.TreeModel',
fields: [
{name: 'id', type: 'int'},
{name: 'task', type: 'string'},
{name: 'user', type: 'string'},
{name: 'index', type: 'int'},
{name: 'parentId', type: 'int'},
{name: 'leaf', type: 'boolean', persist: false}
]
});
Ext.define('Sandbox.store.TreeTest', {
extend: 'Ext.data.TreeStore',
model: 'Sandbox.model.TreeTest',
proxy: {
type: 'ajax',
url: 'resources/treedata.php',
api: {
create: 'resources/treedata.php?action=create',
read: undefined,
update: 'resources/treedata.php?action=update',
destroy: 'resources/treedata.php?action=destroy'
}
},
autoSync: true,
autoLoad: false,
root: {id: 1, text: "Root Node", expanded: false}
});
Ext.define('Sandbox.view.TreeGrid', {
extend: 'Ext.tree.Panel',
alias: 'widget.treegrid',
store: 'TreeTest',
columns: [{
xtype: 'treecolumn',
text: 'Task',
flex: 2,
sortable: true,
dataIndex: 'task',
editor: {xtype: 'textfield', allowBlank: false}
},{
dataIndex: 'id',
align: 'right',
text: 'Id'
}, {
dataIndex: 'user',
flex: 1,
text: 'Utilisateur',
editor: {xtype: 'textfield', allowBlank: false}
}],
plugins: [{
ptype: 'rowediting',
clicksToMoveEditor: 1,
autoCancel: false
}],
viewConfig: {
plugins: [{
ptype: 'treeviewdragdrop',
containerScroll: true
}]
},
tbar:[
{xtype: 'button', text: 'Add Child', itemId: 'addchild'},
{xtype: 'button', text: 'Remove', itemId: 'removenode'}
]
});
I didn't elaborate the server side code. I just copied the Kitchensink example code. To get to work a create, update or delete request, it has to return the modified rows along with success: true.
Explanations:
I needed to launch sencha app build after adding the required classes in order to display everything correctly
file model/TreeTest.js : the field index is required if we want a reorder to be saved back to the server. If it is ommitted, only rows with edited fields are saved back. It was necessary to add persist: false for the leaf field, because this data is not needed on the server.
file store/TreeTest.js :
autoSync: true worked out of the box, with the restriction mentionned on reordering.
the tree autoLoads when the root node is expanded: true or if autoLoad: true. If we don't want to autoLoad, autoLoad and expanded must be both false.
root is required for a good working store. If it is missing, we must load the store manually, even if autoLoad: true.
It was necessary to add the api configuration. Without it, it was not possible to tell appart an update, create and delete request.
file view/TreeGrid.js :
a column with xtype: 'treecolumn' is required.
The removal of a row is simple and syncs the store automatically. The server side is responsible for deleting children if there are.
The creation of a new row is trickier, because the store is sync()'d as soon as appendChild() is called (suspendAutoSync() is used to avoid to write the new child before it is edited). Also, the grid gets only updated, if we control the leaf property manually (.set('leaf', false)). I expect ExtJs to correctly manage the leaf property and consider this as a bug.
The proxy used by the store must have a writer configured for sync operations to talk to the server.
Related
Good day,
I'm pretty new in extjs 5 and mvvm. I want to make an ajax request in order to display a treepanel with datas caught with a php.
Here is my store
Ext.define('MyApp.store.servicesStore', {
extend: 'Ext.data.TreeStore',
// alias: 'store.servicesStore',
storeId : 'servicesStore',
model : 'MyApp.model.servicesModel',
proxy: {
type: 'ajax',
url: 'app/store/data/GetServices.php'
},
root: {
text: 'Events',
id: 'root'
},
autoLoad: true,
folderSort: true
});
I've seen that a "success" can resolve that issue but I don't need a succes as it's only displayed in a treePanel
Ext.define('MyApp.view.tabServices.servicesTab', {
extend: 'Ext.tree.Panel',
xtype: 'servicesTab',
layout: {
type: 'border'
},
useArrows: true,
rootVisible: false,
store: {type: 'servicesStore'},
forceFit: true,
columns: [{
xtype: 'treecolumn',
dataIndex: 'text',
width: 600
}, {
dataIndex: 'mbt',
cls: 'mbtcss',
width: 80
}, {
dataIndex: 'bt',
cls: 'btcss',
width: 75
}, {
dataIndex: 'details', // port separated from rest
width: 60
}, {
dataIndex: 'code',
width: 80
}]
});
So, when I launch my app, the "You're trying to decode an invalid JSON String" appears, how can I do to make it understand that I actually use a php file?
More precisely, that code is working in extjs 3.4
To run the PHP code which did work under ExtJS 4 you must either modify your PHP to return the data in JSON format. Or otherwise you set your "enable compatibility" to version 4.
See "Enabling Compatibility" under http://docs.sencha.com/extjs/5.0/whats_new/5.0/extjs_upgrade_guide.html#Enabling_Compatibility.
compatibility: {
ext: '4.2'
}
I've such a simple question but can't find answer (documentation) on it. I've created Grid , where information is retrieved from MySQL database. Using Ext JS 4.2 .
Let's take a look of script ...
Ext.define("AppStore",{
extend: "Ext.data.Model",
fields: [
{name: "nickname" , type: "auto"},
{name: "email" , type: "auto"}
]
});
var store = Ext.create("Ext.data.Store",{
model: "AppStore",
proxy: {
type: "ajax",
api: {
read : "./read.php",
update : "./update.php"
},
reader: {
type: "json",
root: ""
},
writer: {
type: "json",
writeAllFields: true,
encode: false,
root: ""
}
},
listeners: {
read: function(operation, callback, scope){
},
update: function(operation, callback, scope){
// Do I have to do something from here ?
}
},
autoLoad: true
});
Ext.create("Ext.grid.Panel",{
store: store,
selMode: "cellmodel",
columns: [
{
text: "Nickname",
flex: 1,
dataIndex: "nickname",
editor: {
xtype: "textfield",
allowBlank: false
}
},
{
text: "Email",
flex: 1.5,
dataIndex: "email",
editor: {
xtype: "textfield",
allowBlank: false
}
}
],
plugins: [
Ext.create("Ext.grid.plugin.CellEditing",{
clicksToEdit: 2
})
]
});
Everything is working fine , just interested in how I have to send request to MySQL for updating data after changing it in Grid cell . Any example , documentation or the way how to accomplish this task will be appreciated , thanks ...
Typically, you'll want to call sync() on your grid's store in order to persist the model changes to the server. This can be configured to occur automatically on every edit (see the autoSync property of the store). However, I would suggest it's better to handle the sync() call based on some specific action (e.g., a "Save" button being clicked, etc.).
I'm migrating from extjs 2.2 to extjs 4.0. My code filters my grid data trough an php (the proxy url) that POST the some ext fields.
etx store:
var store = Ext.create('Ext.data.Store', {
model: 'Company',
remoteSort: true,
remoteFilter: true,
proxy: {
// load using script tags for cross domain, if the data in on the same domain as
// this page, an HttpProxy would be better
type: 'ajax',
url: "logica_de_aplicacao/usuario/grid_usuarios_dados.php",
reader: {
root: 'results',
totalProperty: 'total'
},
// sends single sort as multi parameter
simpleSortMode: true
},
sorters: [{
property: 'nome',
direction: 'ASC'
}]
});
ext fields (two exemples, there are too many fields):
var txtLogin = new Ext.form.TextField({
fieldLabel: "Login",
width: 200
});
var txtAtivo = new Ext.form.ComboBox({
fieldLabel: 'Ativo',
width: 200,
name: 'ativo',
editable: false,
disableKeyFilter: true,
forceSelection: true,
triggerAction: 'all',
mode: 'local',
store: new Ext.data.SimpleStore({
id: 0,
fields: ['value', 'text'],
data : [['S', 'Sim'], ['N', 'Não']]
}),
valueField: 'value',
displayField: 'text',
hiddenName: 'ativo'
});
Filtering:
tbar: [{
text: "Adicionar Filtro", //add filter
tooltip: "Filtre os resultados",
iconCls:"icone_filtro",
handler: function() {
iniciaPesquisa();
}
}, {
text: "Remover Filtro", //remove filter
tooltip: "Cancelar o filtro",
iconCls:"icone_cancel_filtro",
handler: function() {
store.baseParams = {
login: "",
nome: "",
privilegio: "",
ativo: "",
municipio: ""
};
store.removeAll();
store.load();
}
}],
PHP:
...
$login = $_POST["login"];
...
$ativo = $_POST["ativo"];
In ext 2.2 that would normally post the fields content on the store.load() action, but nothing happens now. How could I post those fields in ext 4?
(apologizes for the bad english)
It's actually simpler now, just use store.clearFilter()
store.clearFilter();
store.removeAll();
store.load();
var store = Ext.create('Ext.data.Store', {
model: 'Company',
remoteSort: true,
remoteFilter: true,
proxy: {
// load using script tags for cross domain, if the data in on the same domain as
// this page, an HttpProxy would be better
type: 'ajax',
url: "logica_de_aplicacao/usuario/grid_usuarios_dados.php",
baseParams: { //here you can define params you want to be sent on each request from this store
login: "",
nome: "",
privilegio: "",
ativo: "",
municipio: ""
},
reader: {
root: 'results',
totalProperty: 'total'
},
// sends single sort as multi parameter
simpleSortMode: true
},
sorters: [{
property: 'nome',
direction: 'ASC'
}]
});
tbar: [{
text: "Adicionar Filtro", //add filter
tooltip: "Filtre os resultados",
iconCls:"icone_filtro",
handler: function() {
iniciaPesquisa();
}
}, {
text: "Remover Filtro", //remove filter
tooltip: "Cancelar o filtro",
iconCls:"icone_cancel_filtro",
id : 'BtnRemoveFilter', // added this
handler: function() {
store.baseParams = {
login: "",
nome: "",
privilegio: "",
ativo: "",
municipio: ""
};
store.removeAll();
store.load();
}
}],
var Btn = Ext.getCmp('BtnRemoveFilter');
Btn.on('click', function(){
store.load({
params: { //here you can define params on 'per request' basis
login: "the value u want to pass",
nome: "the value u want to pass",
privilegio: "the value u want to pass",
ativo: "the value u want to pass",
municipio: "the value u want to pass"
}
})
});
try this code is working or not.i think this is what u want
I have a xtype:'combo' which looks like this:
xtype: 'combo',
id: 'records_list_author_id',
emptyText: 'Filter By author',
editable: false,
store: 'FilterRecordsByAuthor',
displayField: 'firstname',
valueField: 'id',
lastQuery: '',
triggerAction: 'all',
queryMode: 'local',//'remote',
typeAhead: false,
width: 200
I use very simple story with proxy defined as this:
proxy: {
type: 'ajax',
actionMethods: 'POST',
api: {
read: g_settings.baseUrl + 'index.php/record/getAuthorsOfRecords'
},
reader: {
type: 'json',
root: 'data',
idProperty: 'id',
successProperty: 'success'
//totalProperty: 'totalCount'
}
}
and in my controller I have this:
var comboBoxFilterByAuthorSt = this.getFilterRecordsByAuthorStore();
comboBoxFilterByAuthorSt.clearFilter(true);
comboBoxFilterByAuthorSt.filter ({
filterFn: function(item) {
return item.get('category_id') == sel.raw.categoryId;
}
})
So for example when I have sel.raw.categoryId = 1 it matches records with 2 different authors in my store, but in the combobox I get only one of the names. In other cases I also get uncorrect result. I checked my sql query and it works OK and returns the correct info, but when I filter the store I don't get matches I know they exist. Maybe the problem is somewhere else, but maybe I miss something when making the filters. So - any advice is appreciated.
Thanks
Leron
load failed -- arguments: [Object api=Object, Object request=Object reader=Object scope=Object, Object tId=0 status=200 statusText=OK, SyntaxError: missing } after property list message=missing } after property list]
I got that error by adding an exception to my store but don't see any real error in my code...maybe another set of eyes will help.
php:
case 'messages':
if(isset($_SESSION['id'])){
$stmt = $dbh->prepare("Select ID, ReceivedAt, Message from SystemEvents Limit 100");
$stmt->execute();
while($tmp = $stmt->fetch()){
$y .= '{"ID":"'.$tmp['ID'].'","ReceivedAt":"'.$tmp['ReceivedAt'].'","Message":"'.$tmp['Message'].'"},';
}
$y = trim($y,',');
if(isset($_REQUEST['callback'])){
echo $_REQUEST['callback'].'({"dates":['.$y.']});';
}else{
echo '{"dates":['.$y.']}';
}
}else{
if(isset($_REQUEST['callback'])){
echo $_REQUEST['callback'].'({success: false, data{"error_title": "Error", "errormsg": "Cannot display dates"}})';
}
else{
echo '{success: false, data{"error_title": "Error", "errormsg": "Cannot display dates"}}';
}
}
break;
extjs:
Ext.onReady(function(){
var logStore = new Ext.data.JsonStore({
autoLoad: true,
url: 'inc/interface/config.php?list=messages',
root: 'dates',
idProperty: 'ID',
fields: ['ID', 'ReceivedAt', 'Message'],
listeners: {
loadexception: function() {
console.log('load failed -- arguments: %o', arguments);
}
}
});
var dateStore = new Ext.data.JsonStore({
autoLoad: true,
url: 'inc/interface/config.php?list=date_options',
root: 'dates',
idProperty: 'ID',
fields: ['ID', 'ReceivedAt'],
listeners: {
loadexception: function() {
console.log('load failed -- arguments: %o', arguments);
}
}
});
var dateSelect = new Ext.form.DateField({
fieldLabel: 'Pick a date',
width: 190,
align: 'center',
frame: true
});
var dateCombo = new Ext.form.ComboBox({
store: dateStore,
mode: 'local',
valueField: 'ID',
displayField: 'ReceivedAt',
editable: false,
emptyText: 'Select a Date',
width: 250,
listeners:{
activate: function(){
dateStore.reload();
}
}
});
var searchField = new Ext.form.TextField({
fieldLabel: 'Search Criteria',
emptyText: 'Search....',
width: 190,
frame: true
});
var searchButton = new Ext.Button({
text: 'Search',
});
var clearButton = new Ext.Button({
text: 'Clear',
tooltip: 'Clears all your search data'
});
var searchPanel = new Ext.Panel({
layout: 'form',
region: 'east',
width: 300,
collapsible: true,
alignButton: 'right',
title: "Search Panel",
items: [dateSelect, dateCombo, searchField],
buttons: [clearButton, searchButton]
});
var logGrid = new Ext.grid.GridPanel({
region: 'center',
store: logStore,
colModel: new Ext.grid.ColumnModel({
columns: [{
id: 'received',
header: 'Received',
dataIndex: 'ReceivedAt',
width: 250
},{
id: 'message',
header: 'Logs',
dataIndex: 'Message',
width: 750
}]
}),
});
var mainViewport = new Ext.Viewport({
layout: 'border',
items: [logGrid, searchPanel]
});
});
I don't think posting the rest of my php would be relevant since it all works but hopefully someone can spot something that my bad eyes cannot.
I see an extra comma here:
var searchButton = new Ext.Button({
text: 'Search',
});
Also on the LogGrid. That might be it
EDIT: The response sent back from PHP does not look like it will be valid JSON if there is an error data{"error_title" is wrong, should be data:{"error_titel"
You really should look at building objects/arrays in PHP and echo these using json_encode instead of building JSON manually.
BTW: To find stray commas, I do a search with the following pattern:
\,\s*(}|])
This saves heaps of time, especially since different browsers are more robust than others to misplaced commas.