понедельник, 15 декабря 2008 г.

Учебник: работа с данными в Ext

Основная концепция данных
- Stores - Store это хранилище данных для таблиц (Grid). Это кэш объектов Ext.data.Record, который содержит те поля, которые вы указываете. Поля могут быть доступны по имени.
-Proxies - Хранилище Store использует объект Proxy для получения данных из заданного их месторасположения. Если у вас допустим просто javascript массив прямо в коде страницы, то вам понадобиться MemoryProxy, конструктор которого просто работает с этими данными. Если вы получаете данные с сервера, расположенного в том же домене, то вам нужен HttpProxy. Proxy достаточно прозрачен. Он всего лишь обеспечивает доступ к порциям данных. Вы назначаете объект Reader взависимости от того, какие данные ожидаются. Вам лучше использовать ArrayReader если у вас просто MemoryProxy, который рабоает с со значениями массивов.
-Readers - Reader, является конструктором для определения данных, как бы расставляя поля. Он выделяет отдельные поля из порции данных и передает их массиву записей, которые передаются в хранилище с помощью метода loadRecords, который и обновляет кэш хранилища.

Основные классы данных
-Ext.data.Connection - класс обеспечивает подключение к соответствующему домену страницы, позволяя делать запросы как к статическому URL, так и к динамическому, указанному в процессе запроса.
-Ext.data.Record - экземпляры этого класса одновременно и определяют описание информации и описание для использования информации записей в хранилище Ext.data.Store или любом другом коде которому нужно иметь доступ к записям, сохраненным в Ext.data.Store объекте.
-Ext.data.SortTypes - Этот класс является уникальным и не может быть создан напрямую.
-Ext.data.Tree -Представляет структуру дерева данных и все события узлов дерева.
-Ext.data.Node -не указано

Источник: http://extjs.com/learn/Manual:Data

среда, 10 декабря 2008 г.

Расширение Ext для новичков

Источник: http://extjs.com/learn/Tutorial:Extending_Ext_for_Newbies

Обычно вы хотите привнести мощь объектно-ориентированного проектирования с помощью классов. Вы делаете это по нескольким причинам как объяснено в этом учебнике.

Классы, используемые многократно

Иногда вам нужны будут компоненты, конфигурационные опции которых вы хотите использовать многократно. Например, у вас может быть набор панелей с одной и той же шириной и высотой, но с разными заголовками. Это назывется предопределенные (предварительно-сконфигурированные) классы.

Модель Конструктора

Один вариант решения это использование функции конструктора, как например здесь:
// функция - конструктор
var MyPanel = function(config) {
Ext.apply(this, {
// создайте нужные опции здесь

width: 300,
height: 300
});
MyPanel.superclass.constructor.apply(this, arguments);

};
// MyPanel расширяет возможности Ext.Panel
Ext.extend(MyPanel, Ext.Panel, {});
 
var myfirstpanel = new MyPanel({

title: 'My First Panel'
});
 
var mysecondpanel = new MyPanel({

title: 'My Second Panel'
});


Фабрика шаблонов (палитр)
Другой путь для создания предопределенных объектов это использование фабрики шаблонов дизайна. Фабричная функция (предопределенная функция) возвращает новый экземпляр объекта. Этот подход не требует расширения классов (краткий обзор не позволяет обсудить это подробно). Фабрика шаблонов это альтернативный вышеупомянутому метод который особенно полезен когда все, что вы хотите сделать, это задать конфигурирование свойств "по-умолчанию", не расширяя/переопрделяя базовый класс.
function createMyPanel(config) {
return new Ext.Panel(Ext.apply({//опции по-умолчанию идут здесь

width: 300,
height: 300
}, config));
};
 
var myfirstpanel = createMyPanel({

title: 'My First Panel'
});
 
var mysecondpanel = createMyPanel({
title: 'My Second Panel'

});


Расширение функциональности

Другая причина, по которой вы хотите использовать ОО классы это когда вы хотите расширить функциональность другого класса. Допустим вы хотите добавить функцию в вышеупомянутую панель и переопределить существующую функцию. Есть один путь сделать это:
// Конструктор
var MyPanel = function(config) {
//Повторно-используемые опции идут здесь
Ext.apply(this,
{

width: 300,
height: 300
}
});
// вызов конструктора суперкласса для сохранения функциональности класса-родителя
MyPanel.superclass.constructor.apply(this, arguments);
// Здесь вы можете добавить функциональность, нужную вам,

//например обработку событий.
this.on('click', function() {alert("You Clicked " + this.title);}, this);

};
// MyPanel расширяет Ext.Panel
Ext.extend(MyPanel, Ext.Panel, {
// Здесь вы можете добавить статические переменные класса, переменные, которые будут иметь
//одинаковое значение для всех объектов класса.

// Если вы не уверены нужно ли включать его в конструктор выше. Не помещайте никакого объекта
// созданного с помощью 'new' или 'xtype' здесь. Безопаснее положить его в конфигурационных
// опциях в конструкторе.
 
// Новая функция добавлена
myNewFunction: function() {

},
// Переопределить существующую функцию
onRender: function() {
MyPanel.superclass.onRender.apply(this, arguments);
this.myNewFunction();
}

});
 
var myfirstpanel = new MyPanel({
title: 'My First Panel'

});
 
var mysecondpanel = new MyPanel({
title: 'My Second Panel'

});



Другой путь для создания конструктора выше показан @Condor :
var MyPanel = function(config) {
// вызов конструктора суперкласса для сохранения функциональности класса-родителя
MyPanel.superclass.constructor.call(this, Ext.apply({

//опции по-умолчанию идут здесь
width: 300,
height: 300
}, config));
 
// После конструктора суперкласса добавьте требуемую функциональность

//для объекта (обработка событий, прослушивание)
this.on('click', function() {alert("You Clicked " + this.title);}, this);

};


Вышеупомянутый способ расширения Ext класса это модель конструктора. Другой путь расширения Ext класса - использование initComponent, один из шаблонных методов Ext JS созданный специально для этого. Как вы можете догадаться этот метод применяется только для расширения Ext компонентов. Вот пример использования:
var MyPanel = Ext.extend(Ext.Panel, {
// Здесь добавляются статические переменные
//
// If you are not sure put it in the constructor above. Do not put
// any object created with 'new' or 'xtype' here. It is safer to
// put it in the config option of the constructor.

// New function added
initComponent: function() {
//Reusable config options here
Ext.apply(this,
width: 300,
height: 300
});
// And Call the superclass to preserve baseclass functionality
MyPanel.superclass.initComponent.apply(this, arguments);
// Here you can add functionality that requires the object to
// exist, like event handling.
this.on(
'click',
function() {
alert("You Clicked " + this.title);
},
this
);
},
myNewFunction: function() {
},
// Override an existing function
onRender: function() {
MyPanel.superclass.onRender.apply(this, arguments);
this.myNewFunction();
}
});

Первое что вы получите это оповещение, что нет конструктора. Ext создает конструктор сам. Конструктор создается Ext с помощью вызова initComponent. Это широко используемый метод, который вы найдете в продвинутых учебниках и примерах. Но запомните сейчас, что он делает тоже самое, что и модель конструктора.

Предпочитаемый путь для обработки событий (listeners) это добавить их после вызова суперкласса в конструктор или initComponent.
MyPanel.superclass.constructor.apply(this, arguments);
// Here you can add functionality that requires the object to exist,
// like event handling.
this.on(

'click',
function() {
alert("You Clicked " + this.title);
},
this

);


В случае фабричного метода можно добавить обработчки событий вне фабричного метода, как здесь:
myFirstPanel.on(
'click',
function() {
alert("You Clicked " + this.title);
},
myFirstPanel //scope

);


Есть и другие пути обработки событий. Но они более для продвинутых пользователей

вторник, 9 декабря 2008 г.

Проектирование больших приложений в Ext

Вступление

Я решил написать эту статью для тех пользователей Ext2.x которые уже выросли из детских потребностей, создавая одну HTML страницу c простыми скриптами которые создают одно простое окно или форму, для тех кто уже решил что Ext - это путь, и для тех кто борется со слишком большими файлами, трудными для поиска и чувствующих, что их приложениям нужна структура.

Количество проблем и путей их решения равно количеству людей, работающих над этим. Способ, который я опишу в тексте, не единственный, и я не хочу сказать что приложение можно написать только моим способом, иначе будет неверно. Ничего подобного.

Все что я хочу сказать, то что этот вариант рабочий, достаточно структурированный, легко разрабатываемый - просто сказать - Он работает!

Что такое Большое приложение?

Если вы сделали Viewport с BorderLayout, таблицой и формой и все это находится в одном файле - это большое приложение, не так ли? Если вы создали десяток окон с таблицами, формами или границами - это большое приложение, да?

У немцев есть отличные слова: Jein - комбинация слов Да и Нет.
Ответ для обоих вариантов - Jein (и да и нет). Когда приложение становится большим? Ответ простой: оно становится большим когда вы чувствуете, что оно большое. Это начинается тогда, когда у вас у самих начинаются проблемы в ориентировании в множестве файлов или вы не можете найти нужный код в одном файле., когда вы уже не понимаете взаимосвязь компонентов и так далее. Я это пишу, но представьте когда у программиста с в 2-3 раза большим опытом появляется это чувство?
Мы может просто установить, что каждое приложение становится большим, когда оно хорошо написано и у нас не вызовет проблем добавиьт в него дополнительные возможности, дописать новый код, новые CSS правила и так далее.
Лучший и безопасный вариант старта нового приложения это чувство : я начинаю большое приложение!

Файлы и директории
Это первое что нам нужно организовать. Всегда существует корневая директория DocumentRoot на Apache или другом HTTP сервере, так что все поддиректории которые я опишу позже расположены относительно нее.
Рекомендуемая структура директорий такова:
./css (optionally link)
./ext (ссылка)
./img (ссылка)
./js
index.html

Ссылка в вышеупомянутой структуре означает мягкую ссылку, указывающую на реальное месторасположение файлов. Это нужно для того, когда вы например, скачаете новую версию Ext в директорию, то просто измените ссылку на нее, без изменений кода, ссылающегося на нее и можете испытать что все работает правильно в новой версии. Если да, можно оставить все как есть, если нет, просто изменить ссылку на прежнюю.

* css содержит все таблицы стилей. Если у вас есть глобальные стили с цветами компании или шрифтами вы можете создать и эту директорию как ссылку.
* ext ссылается на библиотеку Ext JS как было описано выше
* img ссылается на ваши изображения. Также она может иметь поддиректорию с иконками.
* js будет содержать все JavaScript файлы приложения.
* index.html HTML файл, который является стартовой точкой приложения. Вы можете назвать его как захотите и можете добавить другие файлы, например файлы для процесса аутентификации. В любом случает должна быть одна стартовая точка - файл.
* опционально вы можете создать директорию или ссылку на серверную часть приложения (у меня например ./classes). Вы можете называть их как пожелаете, но придерживаться целостности для приложений, которые пишите (./server, ./php хорошие примеры)

index.html

Самое минимальное содержание файла index.html такое

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<link rel="stylesheet" type="text/css" href="./ext/resources/css/ext-all.css">

<link rel="stylesheet" type="text/css" href="./css/application.css">
<script type="text/javascript" src="./ext/adapter/ext/ext-base.js"></script>

<script type="text/javascript" src="./ext/ext-all-debug.js"></script>
<script type="text/javascript" src="./js/application.js"></script>

<title>A Big Application</title>
</head>
<body></body>
</html>


Чтобы я рекомендовал сделать в файле выше, добавить описательный заголовок не только в этот файл но и во все создаваемые файлы. Также маркер End of file имеет значение. Смотрите File Patterns как пример таких заголовков.

js/application.js

Нам нужен файл, где будет располагаться функция onReady, давайте назовем его application.js. Его минимальное содержимое такое:
// vim: sw=4:ts=4:nu:nospell:fdc=4
/**
* An Application
*
* @author Ing. Jozef Sakáloš
* @copyright (c) 2008, by Ing. Jozef Sakáloš
* @date 2. April 2008
* @version $Id$
*
* @license application.js is licensed under the terms of the Open Source
* LGPL 3.0 license. Commercial use is permitted to the extent that the
* code/component(s) do NOT become part of another Open Source or Commercially
* licensed development library or toolkit without explicit permission.
*
* License details: http://www.gnu.org/licenses/lgpl.html
*/

 
/*global Ext, Application */
 
Ext.BLANK_IMAGE_URL = './ext/resources/images/default/s.gif';
Ext.ns('Application');

 
// application main entry point
Ext.onReady(function() {
 
Ext.QuickTips.init();

 
// code here
 
}); // eo function onReady
 
// eof


Ваш заголовок и подвал может изменяться но удостоверьтесь что установили Ext.BLANK_IMAGE_URL на ваш сервер. Это путь на прозрачное изображение 1х1 пикселей которое используется Ext как указатель расположения ихображения, и если он указывает на неверное месторасположение вы можете получить проблемы при отрисовке, такие как потеря изображений в элементах, отсутствие иконок или что-то подобное.
Вам может также понадобится новой глобальной переменной объекта для приложения ( здесь это Application).
В чем вы должны быть уверены то что Ext.onReady это главная начальная точка приложения.

css/application.css

Здесь будут храниться ваши таблицы стилей, в этом файле. Если вам нужно всего лишь пару правил, то создавать отдельный файл нет необходимости, и будет лучше просто включить его в заголовок страницы.
Обратное тоже истина, помните, вы пишите большое приложение, поэтому все должно быть на своем месте. Если вы напишите таблицу стилей прямо в заголовке файла, то рано или поздно у вас могут возникнуть проблемы при отображении и вы забудете где вы сохранили эти стили.

Ошибочный путь

Что обычно следует когда мы начинаем создавать что то? Давайте напишем. Итак, мы сидим и начинаем писать:
var vp = new Ext.Viewport({
layout:'border'
,items:[

new Ext.grid.GridPanel({
store:new Ext.data.Store({

proxy:new Ext.data.HttpProxy({ ...


Подождите минутку. С таким подходом у нас будет 10000 строк в нашем application.js очень скоро и это то, чего мы хотим в последнюю очередь. Очевидно, мы пропустили какой-то шаг, как будто мы не могли написать это все сразу в index.html?

Правильный путь: Разбиваем все на части

Даже самое сложное состоит из маленьких подсистем, а те состоят из еще более мелких частей, которые тоже состоят из каких-либо элементов. И при написании больших приложений нет исключения. Теперь настало время определить эти части, компоненты и взаимосвязи между ними.

Итак, сядьте, обдумайте все, набросайте схему, сделайте список, но в результате вы должны знать компоненты - хотя бы самые основные, из которых состоит ваше приложение.

Предопределенные классы

Теперь, когда вы проанализировали приложение и определили его компоненты вы можете начать писать. Но как? Самый лучший подход - написать расширение для классов Ext компонент, у которых есть все конфигурационные опции, или они передаются встроенными в конфигурационный объект. Я использую такие предопределенные классы расширения так как они удобнее и более функциональны чем базовые Ext классы, но их главное назначение - это их настройка для своих нужд. Например, чтобы создать таблицу Персонал со специфичной моделью колонок, хранилищем, опциями сортировки, редакторами и тд.
Если у нас такой подход, наше конфигурационное Окно могло мы выглядеть так:
var win = new Ext.Window({
title:'Personnel'
,width:600

,height:400
,items:{xtype:'personnelgrid'}
});
win.show();


Создание предопределенных классов.

(Запомните: Следующая структура может работать не во всех ситуациях, есть исключения)

Давайте обсудим один пример:

Application.PersonnelGrid = Ext.extend(Ext.grid.GridPanel, {
border:false

,initComponent:function() {
Ext.apply(this, {
store:new Ext.data.Store({...})

,columns:[{...}, {...}]
,plugins:[...]

,viewConfig:{forceFit:true}
,tbar:[...]
,bbar:[...]

});
 
Application.PersonnelGrid.superclass.initComponent.apply(this, arguments);
} // eo function initComponent

 
,onRender:function() {
this.store.load();

 
Application.PersonnelGrid.superclass.onRender.apply(this, arguments);
} // eo function onRender

});
 
Ext.reg('personnelgrid', Application.PersonnelGrid);


Что мы здесь делаем? Мы расширяем класс Ext.grid.GridPanel создавая новый класс (расширение) Application.PersonelGrid и мы регистрируем его как новый xtype с именем personnelgrid.
Мы передаем главноей табличной панели все конфигурационные опции необходимые для специфичной таблицы персонала. С этой точки у нас есть новый компонент, строительный блок для нашего приложения, который мы сможем использовать где-либо (окна, регион, отдельно-стоящий) где нам понадобится список персонала. Мы можем создать его так:
var pg = new Application.PersonnelGrid();


или с помощью xtype (для ленивых)
var win = new Ext.Window({
items:{xtype:'personnelgrid'}

,....
});


Организация предопределенных классов

Код выше не должен и не нужно запускать его внутри функции onReady, потому что он ничего не делает с DOM структурой документа; он только создает JavaScript объект. Вот почему он может и должен быть написан в отдельном файле (js/Application.PersonnelGrid.js) и он может и должен быть включен в заголовок index.html файла как:
<script type="text/javascript" src="./js/Application.PersonnelGrid.js></script>

Шаг за шагом мы продолжаем создавать наши предопределенные классы, складывать их в /js директорию, включать их в index.html и строить наше приложение из них, как пазл, собирая из кусочков.
Выглядит хорошо, так?

В любом случае, есть еще что добавить.

Межкомпонентное взаимодействие

Представьте что нам нужен экран с каймой со списком ссылок на западе и таб-панелью в центре. Нажатие ссылки в списке должно создавать новую закладку в центре. Теперь, куда мы должны положить логику этого события и процедуру обработки? На запад или в центр?
Ни в одно из них. Почему? Если у нас есть предопределенные классы которые создают и показывают ссылки на западе (слева) и мы положили всю логику в них, то они больше не могут существовать без центрального региона. Мы можем просто не сможем их использовать без центра, так как не сможем создавать закладки в центре.
Если положить логику в центр - результат будет тот же: центр не сможет существовать без запада.
Единственный компонент, который будет независим в этом случае это их контейнер - border layout, и это единственно правильное место куда следует поместить межкомпонентную связь.
Что мы затем должны сделать? Контейнер (с border layout) должен прослушивать все события с запада и должен создавать закладки в центре как ответ на эти клики. Пример такого межкомпонентного взаимодействия можно найти по ссылке http://examples.extjs.eu/?ex=compcomm

Система представлений

По мере создания приложения число файлов растет, число включений JavaScript файлов тоже (у меня в одном приложении включается около 80 файлов и их количество каждый день растет). Это може уменьшить производительность и продуктивность системы.
Лучший путь решения - это слияние всех javascript файлов и создание одного большого файла, и минимизирование его с помощью JavaScript или других инструментов сжатия. Также вам не понадобится отладка библиотеки Ext для готовой системы.

На продуктивной системе нам надо включить:

  • ext-all.js

  • app-all.js and

  • application.js


Для дополнительной информации о минимизации исходных кодов и создания комбинированных файлов у нас есть другой учебник, который раскрывает эту тему более подробно (http://extjs.com/learn/Tutorial:Building_Ext_From_Source).

Заключение
Вот и все почти... Есть специфичные техники для специальных Ext классов, есть множество других ноу-хау сервер-клиентских приложений, но выше изложена практически основная концепция.
Удачного кодинга!

Источник: http://extjs.com/learn/Tutorial:Writing_a_Big_Application_in_Ext

понедельник, 8 декабря 2008 г.

Учебник: Grid PHP SQL Часть 12

Создание контекстного меню (правой кнопки)

Это будет последняя часть учебника!!! Знаю, знаю... было очень весело читать! Ок, достаточно! Теперь мы поговорим о контекстных меню или как их обычно называют меню открывающиеся по правому клику мышки.

Во-первых мы записываем номер ряда, на котором мы нажали:

var PresidentListingSelectedRow;


У нас будет простое меню с тремя пунктами: изменение текущей записи, удаление и печать таблицы.
Ок, давайте определим четыре функции. Первая будет вызываться каждый раз при нажатии правой кнопки мыши на таблице:

  function onPresidentListingEditorGridContextMenu(grid, rowIndex, e) {
e.stopEvent();
var coords = e.getXY();
PresidentListingContextMenu.rowRecord = grid.store.getAt(rowIndex);
grid.selModel.selectRow(rowIndex);
PresidentListingSelectedRow=rowIndex;
PresidentListingContextMenu.showAt([coords[0], coords[1]]);
}


Ок, как вы видите мы показываем переменную названную PresidentListingContextMenu. Мы ее еще не создали, мы сделаем это сразу после этого. Давайте определим три функции которые будут вызываться для каждого пункта меню:

  function modifyPresidentContextMenu(){
PresidentListingEditorGrid.startEditing(PresidentListingSelectedRow,1);
}

function deletePresidentContextMenu(){
confirmDeletePresidents();
}

function printListingContextMenu(){
printListing();
}


Как видите, это те функции, которые мы уже определили. Ок, давайте определим ContextMenu:
  PresidentListingContextMenu = new Ext.menu.Menu({
id: 'PresidentListingEditorGridContextMenu',
items: [
{ text: 'Modify this President', handler: modifyPresidentContextMenu },
{ text: 'Delete this President', handler: deletePresidentContextMenu },
'-',
{ text: 'Print this grid', handler: printListingContextMenu }
]
});


Последнее, что нам понадобится это перехватчик событий (actionlistener) который будет вызывать функцию onPresidentListingEditorGridContextMenu при нажатии правой кнопки (я пытался найти еще более длинное имя для этой функции, но... :))
  PresidentListingEditorGrid.addListener('rowcontextmenu', onPresidentListingEditorGridContextMenu);


Это конец учебника. Надеюсь он помог вам понять как работать с таблицами в ext. Теперь вы понимаете ее код. Не забывайте проверять наши материалы и играйте с различными методами, свойствами и событиями.

Вы можете оставить отзыв на форуме http://extjs.com/forum/showthread.php?p=144865#post144865
For more advanced features and usage of grids, I want to once again refer you to Michael Lecomte's tutorial.
Для более продвинутых функций таблиц посмотрите учебник Michael Lecomte's
Happy Griding!http://extjs.com/learn/Tutorial:Ext20_Grid_Editor_PHP_MySQL


Nicolas Bize.

Что запомнить из этой части

* Перехватчик событий необходимо прикреплять к таблице
* Вам не надо пересоздавать контекстное меню каждый раз заново, так как это глобальная переменная :)

Материалы этой части можно скачать по ссылке http://nicolas.bize.free.fr/ext/tutorial/Tutorial%20Part%2012.zip

Учебник: Grid PHP SQL Часть 11

Печать вашей таблицы.

Отлично... Я собираюсь сказать одну вещь до того, как буду заспамлен тоннами писем с критикой... Метод, описанный здесь для печати вашей таблицы не единственно возможный и не самый оригинальный. Это просто быстрый пример того, как можно распечатать таблицу.

Наш метод - это просто создание HTML файла и заполнение его массивом, содержащим отображаемые в таблице данные (все данные, не только 15 выведенных в данный момент)

Во-первых, мы добавим кнопку Print на нашу панель инструментов:


         ,'-', {
text: 'Print',
tooltip: 'Print me!',
handler: printListing,
iconCls:'print' // defined in our css
}


Давайте теперь определим функцию printListing(). Эта функция вызовет php функцию которая создаст HTML файл таблицы:

    function printListing(){
var searchquery = "";
var searchfirstname = "";
var searchlastname = "";
var searchparty = "";
var searchenteringoffice = "";
var searchleavingoffice = "";
var win; // our popup window
// check if we do have some search data...
if(PresidentsDataStore.baseParams.query!==null){searchquery = PresidentsDataStore.baseParams.query;}
if(PresidentsDataStore.baseParams.firstname!==null){searchfirstname = PresidentsDataStore.baseParams.firstname;}
if(PresidentsDataStore.baseParams.lastname!==null){searchlastname = PresidentsDataStore.baseParams.lastname;}
if(PresidentsDataStore.baseParams.party!==null){searchparty = PresidentsDataStore.baseParams.party;}
if(PresidentsDataStore.baseParams.enteringoffice!==null){searchenteringoffice = PresidentsDataStore.baseParams.enteringoffice;}
if(PresidentsDataStore.baseParams.leavingoffice!==null){searchleavingoffice = PresidentsDataStore.baseParams.leavingoffice;}

Ext.Ajax.request({
waitMsg: 'Please Wait...',
url: 'database.php',
params: {
task: "PRINT",
// we have to send all of the search

query: searchquery, // if we are doing a quicksearch, use this
firstname : searchfirstname, // if we are doing advanced search, use this
lastname : searchlastname,
party : searchparty,
enteringoffice : searchenteringoffice,
leavingoffice : searchleavingoffice,
currentlisting: PresidentsDataStore.baseParams.task // this tells us if we are searching or not
},
success: function(response){
var result=eval(response.responseText);
switch(result){
case 1:
win = window.open('./presidentslist.html','presidentslist','height=400,width=600,resizable=1,scrollbars=1, menubar=1');
win.print();
break;
default:
Ext.MessageBox.alert('Uh hu...','Unable to print the grid!');
break;
}
},
failure: function(response){
var result=response.responseText;
Ext.MessageBox.alert('error','could not connect to the database. retry later');
}
});
}


Ок, вы можете удивится почему мы должны передавать так много параметров... Дело в том что... мы хотим распечатать всю нашу таблицу а наш DataStore содержит лишь 15 записей. Поэтому наш PHP файл собирается сделать запросы заново еще раз. не ограничивая поиск частичными запросами. Затем наш database.php сложит все это в файл listpresidents.html.

Вот наш database.php файл (сначала выбор)
    case "PRINT":
printPresidents();
break;


А затем функция печати:
function printPresidents()
{
// Ok since we limited our queries to 15 entries, we need to do a query again and get everything.
if($_POST['currentlisting']=='LISTING'){ // we are doing a basic listing or using the quicksearch
$query = "SELECT * FROM presidents pr, parties pa WHERE pr.IDparty = pa.IDparty";
// Here we check if we have a query parameter :
if (isset($_POST['query']) && ($_POST['query'] != "")){
$query .= " AND (pr.firstname LIKE '%".$_POST['query']."%' OR pr.lastname LIKE '%".$_POST['query']."%')";
}
$query .= " ORDER BY tookoffice";
$result = mysql_query($query);
$nbrows = mysql_num_rows($result);
} else if($_POST['currentlisting']=='SEARCH'){
$query = "SELECT * FROM presidents pr, parties pa WHERE pr.IDparty = pa.IDparty";
$firstname = $_POST['firstname'];
$lastname = $_POST['lastname'];
$enteringoffice = $_POST['enteringoffice'];
$leavingoffice = $_POST['leavingoffice'];
$party = $_POST['party'];

if($firstname != ''){
$query .= " AND firstname LIKE '%".$firstname."%'";
};
if($lastname != ''){
$query .= " AND lastname LIKE '%".$lastname."%'";
};
if($party != ''){
$query .= " AND pr.IDparty = '".$party."'";
};
if ($enteringoffice) {
$query .= " AND tookoffice >= '".$enteringoffice."'";
};
if ($leavingoffice) {
$query .= " AND leftoffice <= '".$leavingoffice."'";
};
$result = mysql_query($query);
$nbrows = mysql_num_rows($result);
}

// We now have our array, let's build our HTML file :
$file = fopen("presidentslist.html",'w');
fwrite($file, "<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Transitional//EN' 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'><html xmlns='http://www.w3.org/1999/xhtml'><head><meta http-equiv='Content-Type' content='text/html; charset=iso-8859-1' /><title>Printing the Grid</title><link rel='stylesheet' type='text/css' href='printstyle.css'/></head>");
fwrite($file, "<body><table summary='Presidents List'><caption>The Presidents of the United States</caption><thead><tr><th scope='col'>First Name</th><th scope='col'>Last Name</th><th scope='col'>Party</th><th scope='col'>Entering Office</th><th scope='col'>Leaving Office</th><th scope='col'>Income</th></tr></thead><tfoot><tr><th scope='row'>Total</th><td colspan='4'>");
fwrite($file, $nbrows);
fwrite($file, " presidents</td></tr></tfoot><tbody>");
for($i = 0; $i< $nbrows; $i++){
$data = mysql_fetch_array($result);
fwrite($file,'<tr');
if($i%1==0){
fwrite($file," class='odd'");
}
fwrite($file, "><th scope='row' id='r97'>");
fwrite($file, $data['firstname']);
fwrite($file,"</th><td>");
fwrite($file, $data['lastname']);
fwrite($file, "</td><td>");
fwrite($file, $data['name']);
fwrite($file, "</td><td>");
fwrite($file, codeDate($data['tookoffice']));
fwrite($file, "</td><td>");
fwrite($file, codeDate($data['leftoffice']));
fwrite($file, "</td><td> $");
fwrite($file, $data['income']);
fwrite($file, "</td></tr>");
}
fwrite($file, "</tbody></table></body></html>");
fclose($file);
echo '1'; // we are done.

}


На самом деле это очень просто. Мы просто вставляем запросы без ограничения количества записей и сохраняет полученный массив в HTML файл. Затем мы возвращаем 1.
Как будут выглядеть результаты на самом деле зависит от вас!
Некоторые предпочитают решения на основе экспорта в PDF формат. Вы можете найти эту информацию в топиках форумов.

Что запомнить:
Этот способ нельзя применить везде
Он не показывает колонки в нужном выбранном порядке и видимости
Это не самое лучшее решение.

Файлы этой части можно скачать здесь http://nicolas.bize.free.fr/ext/tutorial/Tutorial%20Part%2011.zip

Учебник: Grid PHP SQL Часть 10

Компонент быстрого поиска.

Отлично... Мы почти прошли этот учебник!!! Скоро вы сможете создавать свои собственные впечатляющие таблицы. Давайте изучим компонет QuickSearch.
Мы уже прошли тему как осуществлять обычный поиск по таблице. Для этого нужно чтобы пользователь нажал кнопку Search и ввел поля поиска... Ок, чтож, вы поймете что это может оказаться очень сложной задачей для большинства пользователей. И тут появляется мистер быстрый поиск QuickSearch. Компонент QuickSearch это просто текстовое поле в котором пользователь вводит текст для поиска, нажимает Ввод и тогда... А дальнейшее зависит от того как мы это сделаем..

Давайте продолжим и сделаем так ,чтобы текст искался всей таблице.
Отлично, до того как мы начнем нам нужно добавить еще один .js файл в заголовок HTML файла:
<script type="text/javascript" src="searchfield.js"></script>


Вы можете найти файл в папке examples/form архива extjs или скачайте его по ссылке http://extjs.com/deploy/dev/examples/form/SearchField.js
Во-первых мы просто добавим его на нашу панель инструментов. Нам просто надо установить некоторые основные параметры, которые передаем в datastore.
, '-', new Ext.app.SearchField({
store: PresidentsDataStore,
params: {start: 0, limit: 15},
width: 120
})


Ок, если вы достаточно опытны, мы можем просто открыть файл searchfield.js в любимом Блокноте и посмотреть что там внутри... И там вы увидите параметр называемый query который посылается при перезагрузке datastore. Да... Мы знаем что есть что! Теперь.. DataStore президентов может быть как в LISTING режиме так и в режиме SEARCHING (зависит от параметров). Итак, кнопка QuickSearch будет просто добавлять строку введенную в текстовое поле как дополнительный параметр.
Я просто собираюсь реализовать это при простом получении списка президентов. Я вам покажу как это сделать при поиске (быстрый поиск на существующем поиске)
Давайте откроем наш файл database.php.Теперь, когда мы обращаемся к функции getList() мы должны проверить нет ли у нас параметра query. И если есть мы должны найти похожие данные в нашей таблице. Теперь наша новая функци getList выглядит так:
  $query = "SELECT * FROM presidents pr, parties pa WHERE pr.IDparty = pa.IDparty";
// Here we check if we have a query parameter :
if (isset($_POST['query'])){
$query .= " AND (pr.firstname LIKE '%".addslashes($_POST['query'])."%' OR pr.lastname LIKE '%".addslashes($_POST['query'])."%')";
}
$result = mysql_query($query);
$nbrows = mysql_num_rows($result);
$start = (integer) (isset($_POST['start']) ? $_POST['start'] : $_GET['start']);
$end = (integer) (isset($_POST['limit']) ? $_POST['limit'] : $_GET['limit']);
$limit = $query." LIMIT ".$start.",".$end;
$result = mysql_query($limit);
[...]

Это так просто! :) Ну теперь вы можете брать параметр query, убрать все пробелы, разбить по словам для лучшего поиска. Вы даже можете делать поиск по годам и так далее... Реализация поиска, который мыуже сделали достаточно проста. Просто проверяйте переменную query до того, как сделаете запрос. Вот и все!

Скачать файлы проекта можно по ссылке http://nicolas.bize.free.fr/ext/tutorial/Tutorial%20Part%2010.zip

Учебник: Grid PHP SQL Часть 9

Панель страниц

Ну что, это снова вы... Да... Ок, давайте поговорим о панели страниц, которую вы можете увидеть внизу таблицы. Она мне всегда очень нравилась. Самое хорошее, что реализовать ее очень просто. Так давайте быстро сделаем ее!

Элемент PagingToolbar должен знать в основном две вещи: как много записей вы хотите отображать на странице и куда отсылать информацию о текущей странице.(мы будем посылать эту информацию в DataStore)
Итак... Сразу после панели инструментов в EditorGrid, давайте добавим нижнюю панель:
      bbar: new Ext.PagingToolbar({
pageSize: 15,
store: PresidentsDataStore,
displayInfo: true
})

Мы будем показывать по 15 президентов на странице. Мы уже почти все сделали! Единственная вещь теперь - как сообщить нашему PHP скрипту как мы будем делать наш постраничный просмотр. Элемент PagingToolbar использует два параметра - start (стартовая запись) и limit (количество записей). Они будут доступны в серверном скрипте и мы должны лишь только учесть их в нашем SQL запросе.
Давайте убедимся что посылаем эти параметры каждый раз, когда нам нужен список президентов: Каждую PresidentsDataStore.reload() или PresidentsDataStore.load() мы трансформируем в:
  PresidentsDataStore.reload({params: {start: 0, limit: 15}});

Сделайте изменения сами (они встречаются в mainscript.js) только в 3 местах)


Серверная часть
Это очень просто. Теперь, когда мы имеем переменные start и limit, нам нужно получить их значения и изменить конец нашего SQL запроса.
Вот изменения в фукции getList():
  $query = "SELECT * FROM presidents pr, parties pa WHERE pr.IDparty = pa.IDparty";
$result = mysql_query($query);
$nbrows = mysql_num_rows($result);
// Add this for the paging bar:
$start = (integer) (isset($_POST['start']) ? $_POST['start'] : $_GET['start']);
$end = (integer) (isset($_POST['limit']) ? $_POST['limit'] : $_GET['limit']);
$limit = $query." LIMIT ".$start.",".$end;
$result = mysql_query($limit);

Мы все еще хотим делать первый запрос так, чтбы получить общее количество записей (nbrows), но обратно в Ext будем отсылать массим содержащий 15 или менее записей, которые были запрошены.
Давайте не забудем сделать эти же изменения в функции поиска searchPresidents().

Файл с материалами этой части учебника здесь http://nicolas.bize.free.fr/ext/tutorial/Tutorial%20Part%209.zip