Вышел новый релиз jQuery - 1.3
Отмечается значительное увеличение производительности и скорости работы библиотеки.
Подробнее
мой блог по JavaScript, React, NodeJS, jQuery, Ext и подобным,html5 and etc.
вторник, 20 января 2009 г.
суббота, 3 января 2009 г.
Приложения в ExtJS
До того как начать
Это пособие подразумевает, что вы уже установили ExtJS - JavaScript библиотеку и что она установлена в директории extjs в вышестоящей от текущей директории, где будет создаваться наше приложение. Если вы установили библиотеку в другом месте вам необходимо будет изменять пути которые мы употребляем в примерах.
Что нам понадобится?
Помимо ExtJS нам понадобится создать два файла:
* applayout.html
* applayout.js
Давайте начнем с файлов с минимумом содержимого. Вот они с детальным описанием:
applayout.html
Первые две строчки указывают тип документа. Наше приложение будет работать и без указания типа, однако в этом случае броузер перейдет в режим Quirks и приложение не станет совместимым с другими броузерами.
Мы будем использовать HTML 4.0.1 спецификацию, потому что она поддерживается большинством броузеров. Конечно, вы можете использовать другой тип документа если захотите, однако важно помнить, что вы всегда должны указывать тип документа.
ОБНОВЛЕНИЕ: Если вы будете указывать тип документа, то в броузерах IE (всех версиях) есть баги, которые могут вызвать непредсказуемые результаты выполнения. Поэтому больше не рекомундуется указывать тип документа в приложениях.
После начала файла и заголовка head идет указание типа содержимого Content-Type. Ваше приложение будет работать и без него, но лучше указать его.
Затем мы подключаем Ext стили, адаптер и ExtJS. Есть две версии ExtJS:
* ext-all.js - плохо-читаемый, загружается быстрее, используется для готовых приложений
* ext-all-debug.js - легко-читаемый, используется для разработки
Мы собираемся разрабатывать, поэтому включаем вторую версию.
Затем идет наше приложение applayout.js, а после него файлы локализации которые перекрывают английский текст в приложении applayout.js и ExtJS своим переводом, до того как приложение стартует.
Теперь мы готовы установить обработчик событий, который инициализирует (стартует) наше приложение после того, как документ полностью загрузится.
Строка
говорит: Когда документ полностью загрузился, запустить init метод myNameSpace.app в области myNameSpace.app
Затем идет текст заголовка, конец head (пустой) и закрывающиеся тэги.
Это все, что сейчас находится в html файле.
applayout.js
Первые строки этого файла это комментарии, объясняющие содержимое файла, автора и другую связанную информацию. Уверен, ваши приложения будут работать и без комментариев, но запомните: всегда пишите ваше приложение так, как будто пишите для другого человека. Когда вы когда-нибудь снова вернетесь к этому коду через пару месяцев и не сможете понять чтоже такое написано здесь, потому как нет комментариев, вы измените вашу практику кодирования.
Затем идет ссылка на пустое изображение, указывающее на ваш собственный сервер взамен сервера по-умолчанию extjs.com. Вам ведь не надо загружать что-либо с extjs.com при каждой загрузке приложения, не так ли?
Теперь мы создаем собственное пространство имен. Причина этого в инкапсуляции (изолированиии) всех переменных и методов в одном глобальном объекте чтоб избежать конфликтов имен переменных и избежать изменнений их значений извне.
Ядро приложения в конце.. Мы создаем свойство app в myNameSpace как возвращаемое значение функции которая запускается немедленно.
Если мы запустим следующий код
Мы по-сути создаем анонимную функцию (функцию без имени) которая запускается сразу же после обработки (помните () полсе определения функции). Функция возвращает объект который привязывается к переменной о. Наше приложение будет использовать туже логику.
Вы можете разместить инициализацию, частные переменные и\или определения частных функций между началом объявление функции и моментом возврата результата, но помните: Код запускается как часть заголовка и никакие html элементы еще не созданы поэтому любые попытки доступа к ним вызовут ошибку.
С другой стороны, init функция это просто один из методов возвращаемых этой анонимной функцией, запускается только после того, как весь документ полностью загружен и вся структура DOM уже доступна.
Дальше - больше. Если вы не сделали никаких ошибок вы получите сообщение об успехе когда запустите ваше приложение.
Теперь идет самая сложная часть. Это написать что-то работающее внутри этого пустого шаблона.
Public, Private, Privileged?
Давайте добавим некоторую жизнь нашему приложению. Добавьте следующие строки в applayout.html внутрь тэга body:
Это пустой div который послужит как контейнер для кнопки. Затем добавим парочку строк в applayout.js и вот что получится:
Переменные btn1 и privVar1 - закрытые, private. Это значит что к ним можно получить доступ только из приложения а вне его они невидимы. Тоже самое справедливо для частной функции btn1Handler.
С другой стороны, btn1Text это публичная public переменная которая видима и может быть доступна или изменена извне приложения (мы продемонстрируем это позже).
Функция init привелигирована в том что может работать как с public так и с private переменными и функциями. В нашем примере она работает с публичной переменной this.btn1Text, а также с private функцией btn1Handler. Функция init публичная (public) и может быть вызвана откуда угодно.
Ок, давайте запустим его. Видите кнопку в верхнем левом углу? Хорошо. Нажмите ее. Вы получите сообщение privVar1=11, так? Частная private функция может работать с частными private переменными.
Однако что то не так при втором показе сообщения и мы видим что this.btn1Text=undefined; это не то, что мы ожидали. Причина в том, что переменная this внутри обработчика теперь указывает на кнопку а не на наше приложение. Добавьте scope:this в конфигурацию кнопки вот так:
и перезагрузите страницу. Работает теперь?
Перезагрузка (перекрытие) публичных переменных.
Добавьте следующий код в конце applayout.js чтобы было так:
Что теперь делает весь этот код? Сначала он создает объект нашего приложения и затем изменяет (перезагружает) значение публичной переменной btn1Text. Запустите его, чтобы увидеть что текст на кнопке изменился.
Хорошая практика кодирования это хранить весь текст используемый приложением в публичных переменных, так они могут быть легко переведены на другие языки без изменения оригинального кода приложения.
Конечно, следует хранить также другие значения, такие как размеры по-умолчанию, стили и другие настраиваемые опции в публичных переменных. Тогда вам не понадобится исследовать тысячи строк кода приложения чтобы изменить цвет какого нибудь элемента например.
Перегрузка (перекрытие) публичных функций
Давайте изменим наш код на следующий:
Что мы здесь сделали? Просто переопределили функцию init тем же кодом что и до того, только заключенным в блок try/catch чтобы мы могли обрабатывать любые ошибки. Должны мы ожидать прежнего результата или другого? Запустите и увидите.
Да, будет ошибка о том что btn1Handler не определен. Это потому, что обновленная функция пытается получить доступ к частной переменной но не может. Вы видите, оригинальный init привелоигирован и имеет доступ к частному пространству но новый не имеет. В этом есть аспект безопасности: Никакой внешний код не должен получать доступ к частному пространству даже если пытается перегрузить привелигированные методы.
Это пособие подразумевает, что вы уже установили ExtJS - JavaScript библиотеку и что она установлена в директории extjs в вышестоящей от текущей директории, где будет создаваться наше приложение. Если вы установили библиотеку в другом месте вам необходимо будет изменять пути которые мы употребляем в примерах.
Что нам понадобится?
Помимо ExtJS нам понадобится создать два файла:
* applayout.html
* applayout.js
Давайте начнем с файлов с минимумом содержимого. Вот они с детальным описанием:
applayout.html
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<link rel="stylesheet" type="text/css" href="../extjs/resources/css/ext-all.css">
<script type="text/javascript" src="../extjs/adapter/ext/ext-base.js"></script>
<script type="text/javascript" src="../extjs/ext-all-debug.js"></script>
<script type="text/javascript" src="applayout.js"></script>
<!-- A Localization Script File comes here -->
<script type="text/javascript">
Ext.onReady(myNameSpace.app.init, myNameSpace.app);
</script>
<title>Application Layout Tutorial</title>
</head>
<body>
<div id="btn1-ct"></div>
</body>
</html>
Первые две строчки указывают тип документа. Наше приложение будет работать и без указания типа, однако в этом случае броузер перейдет в режим Quirks и приложение не станет совместимым с другими броузерами.
Мы будем использовать HTML 4.0.1 спецификацию, потому что она поддерживается большинством броузеров. Конечно, вы можете использовать другой тип документа если захотите, однако важно помнить, что вы всегда должны указывать тип документа.
ОБНОВЛЕНИЕ: Если вы будете указывать тип документа, то в броузерах IE (всех версиях) есть баги, которые могут вызвать непредсказуемые результаты выполнения. Поэтому больше не рекомундуется указывать тип документа в приложениях.
После начала файла и заголовка head идет указание типа содержимого Content-Type. Ваше приложение будет работать и без него, но лучше указать его.
Затем мы подключаем Ext стили, адаптер и ExtJS. Есть две версии ExtJS:
* ext-all.js - плохо-читаемый, загружается быстрее, используется для готовых приложений
* ext-all-debug.js - легко-читаемый, используется для разработки
Мы собираемся разрабатывать, поэтому включаем вторую версию.
Затем идет наше приложение applayout.js, а после него файлы локализации которые перекрывают английский текст в приложении applayout.js и ExtJS своим переводом, до того как приложение стартует.
Теперь мы готовы установить обработчик событий, который инициализирует (стартует) наше приложение после того, как документ полностью загрузится.
Строка
Ext.onReady(myNameSpace.app.init, myNameSpace.app);
говорит: Когда документ полностью загрузился, запустить init метод myNameSpace.app в области myNameSpace.app
Затем идет текст заголовка, конец head (пустой) и закрывающиеся тэги.
Это все, что сейчас находится в html файле.
applayout.js
/**
* Application Layout
* by Jozef Sakalos, aka Saki
* http://extjs.com/learn/Tutorial:Application_Layout_for_Beginners
*/
// reference local blank image
Ext.BLANK_IMAGE_URL = '../extjs/resources/images/default/s.gif';
// create namespace
Ext.namespace('myNameSpace');
// create application
myNameSpace.app = function() {
// do NOT access DOM from here; elements don't exist yet
// private variables
// private functions
// public space
return {
// public properties, e.g. strings to translate
// public methods
init: function() {
alert('Application successfully initialized');
}
};
}(); // end of app
// end of file
Первые строки этого файла это комментарии, объясняющие содержимое файла, автора и другую связанную информацию. Уверен, ваши приложения будут работать и без комментариев, но запомните: всегда пишите ваше приложение так, как будто пишите для другого человека. Когда вы когда-нибудь снова вернетесь к этому коду через пару месяцев и не сможете понять чтоже такое написано здесь, потому как нет комментариев, вы измените вашу практику кодирования.
Затем идет ссылка на пустое изображение, указывающее на ваш собственный сервер взамен сервера по-умолчанию extjs.com. Вам ведь не надо загружать что-либо с extjs.com при каждой загрузке приложения, не так ли?
Теперь мы создаем собственное пространство имен. Причина этого в инкапсуляции (изолированиии) всех переменных и методов в одном глобальном объекте чтоб избежать конфликтов имен переменных и избежать изменнений их значений извне.
Ядро приложения в конце.. Мы создаем свойство app в myNameSpace как возвращаемое значение функции которая запускается немедленно.
Если мы запустим следующий код
var o = function() {
return {p1:11, p2:22};
}();
Мы по-сути создаем анонимную функцию (функцию без имени) которая запускается сразу же после обработки (помните () полсе определения функции). Функция возвращает объект который привязывается к переменной о. Наше приложение будет использовать туже логику.
Вы можете разместить инициализацию, частные переменные и\или определения частных функций между началом объявление функции и моментом возврата результата, но помните: Код запускается как часть заголовка и никакие html элементы еще не созданы поэтому любые попытки доступа к ним вызовут ошибку.
С другой стороны, init функция это просто один из методов возвращаемых этой анонимной функцией, запускается только после того, как весь документ полностью загружен и вся структура DOM уже доступна.
Дальше - больше. Если вы не сделали никаких ошибок вы получите сообщение об успехе когда запустите ваше приложение.
Теперь идет самая сложная часть. Это написать что-то работающее внутри этого пустого шаблона.
Public, Private, Privileged?
Давайте добавим некоторую жизнь нашему приложению. Добавьте следующие строки в applayout.html внутрь тэга body:
<div id="btn1-ct"></div>
Это пустой div который послужит как контейнер для кнопки. Затем добавим парочку строк в applayout.js и вот что получится:
/**
* Application Layout
* by Jozef Sakalos, aka Saki
* http://extjs.com/learn/Tutorial:Application_Layout_for_Beginners
*/
// reference local blank image
Ext.BLANK_IMAGE_URL = '../extjs/resources/images/default/s.gif';
// create namespace
Ext.namespace('myNameSpace');
// Just to allow this tutorial to work for 1.1 and 2.
Ext.Ext2 = (Ext.version && (Ext.version.indexOf("2") == 0));
// create application
myNameSpace.app = function() {
// do NOT access DOM from here; elements don't exist yet
// private variables
var btn1;
var privVar1 = 11;
// private functions
var btn1Handler = function(button, event) {
alert('privVar1=' + privVar1);
alert('this.btn1Text=' + this.btn1Text);
};
// public space
return {
// public properties, e.g. strings to translate
btn1Text: 'Button 1',
// public methods
init: function() {
if (Ext.Ext2) {
btn1 = new Ext.Button({
renderTo: 'btn1-ct',
text: this.btn1Text,
handler: btn1Handler
});
} else {
btn1 = new Ext.Button('btn1-ct', {
text: this.btn1Text,
handler: btn1Handler
});
}
}
};
}(); // end of app
// end of file
Переменные btn1 и privVar1 - закрытые, private. Это значит что к ним можно получить доступ только из приложения а вне его они невидимы. Тоже самое справедливо для частной функции btn1Handler.
С другой стороны, btn1Text это публичная public переменная которая видима и может быть доступна или изменена извне приложения (мы продемонстрируем это позже).
Функция init привелигирована в том что может работать как с public так и с private переменными и функциями. В нашем примере она работает с публичной переменной this.btn1Text, а также с private функцией btn1Handler. Функция init публичная (public) и может быть вызвана откуда угодно.
Ок, давайте запустим его. Видите кнопку в верхнем левом углу? Хорошо. Нажмите ее. Вы получите сообщение privVar1=11, так? Частная private функция может работать с частными private переменными.
Однако что то не так при втором показе сообщения и мы видим что this.btn1Text=undefined; это не то, что мы ожидали. Причина в том, что переменная this внутри обработчика теперь указывает на кнопку а не на наше приложение. Добавьте scope:this в конфигурацию кнопки вот так:
if (Ext.Ext2) {
btn1 = new Ext.Button({
renderTo: 'btn1-ct',
text: this.btn1Text,
handler: btn1Handler,
scope:this
});
} else {
btn1 = new Ext.Button('btn1-ct', {
text: this.btn1Text,
handler: btn1Handler,
scope:this
});
}
и перезагрузите страницу. Работает теперь?
Перезагрузка (перекрытие) публичных переменных.
Добавьте следующий код в конце applayout.js чтобы было так:
Ext.apply(myNameSpace.app, {
btn1Text:'Taste 1'
});
// end of file
Что теперь делает весь этот код? Сначала он создает объект нашего приложения и затем изменяет (перезагружает) значение публичной переменной btn1Text. Запустите его, чтобы увидеть что текст на кнопке изменился.
Хорошая практика кодирования это хранить весь текст используемый приложением в публичных переменных, так они могут быть легко переведены на другие языки без изменения оригинального кода приложения.
Конечно, следует хранить также другие значения, такие как размеры по-умолчанию, стили и другие настраиваемые опции в публичных переменных. Тогда вам не понадобится исследовать тысячи строк кода приложения чтобы изменить цвет какого нибудь элемента например.
Перегрузка (перекрытие) публичных функций
Давайте изменим наш код на следующий:
Ext.apply(myNameSpace.app, {
btn1Text:'Taste 1',
init: function() {
try {
btn1 = new Ext.Button('btn1-ct', {
text: this.btn1Text,
handler: btn1Handler,
scope: this
});
}
catch(e) {
alert('Error: "' + e.message + '" at line: ' + e.lineNumber);
}
}
});
// end of file
Что мы здесь сделали? Просто переопределили функцию init тем же кодом что и до того, только заключенным в блок try/catch чтобы мы могли обрабатывать любые ошибки. Должны мы ожидать прежнего результата или другого? Запустите и увидите.
Да, будет ошибка о том что btn1Handler не определен. Это потому, что обновленная функция пытается получить доступ к частной переменной но не может. Вы видите, оригинальный init привелоигирован и имеет доступ к частному пространству но новый не имеет. В этом есть аспект безопасности: Никакой внешний код не должен получать доступ к частному пространству даже если пытается перегрузить привелигированные методы.
понедельник, 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
- 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
Обычно вы хотите привнести мощь объектно-ориентированного проектирования с помощью классов. Вы делаете это по нескольким причинам как объяснено в этом учебнике.
Классы, используемые многократно
Иногда вам нужны будут компоненты, конфигурационные опции которых вы хотите использовать многократно. Например, у вас может быть набор панелей с одной и той же шириной и высотой, но с разными заголовками. Это назывется предопределенные (предварительно-сконфигурированные) классы.
Модель Конструктора
Один вариант решения это использование функции конструктора, как например здесь:
Фабрика шаблонов (палитр)
Другой путь для создания предопределенных объектов это использование фабрики шаблонов дизайна. Фабричная функция (предопределенная функция) возвращает новый экземпляр объекта. Этот подход не требует расширения классов (краткий обзор не позволяет обсудить это подробно). Фабрика шаблонов это альтернативный вышеупомянутому метод который особенно полезен когда все, что вы хотите сделать, это задать конфигурирование свойств "по-умолчанию", не расширяя/переопрделяя базовый класс.
Расширение функциональности
Другая причина, по которой вы хотите использовать ОО классы это когда вы хотите расширить функциональность другого класса. Допустим вы хотите добавить функцию в вышеупомянутую панель и переопределить существующую функцию. Есть один путь сделать это:
Другой путь для создания конструктора выше показан @Condor :
Вышеупомянутый способ расширения 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.
В случае фабричного метода можно добавить обработчки событий вне фабричного метода, как здесь:
Есть и другие пути обработки событий. Но они более для продвинутых пользователей
Обычно вы хотите привнести мощь объектно-ориентированного проектирования с помощью классов. Вы делаете это по нескольким причинам как объяснено в этом учебнике.
Классы, используемые многократно
Иногда вам нужны будут компоненты, конфигурационные опции которых вы хотите использовать многократно. Например, у вас может быть набор панелей с одной и той же шириной и высотой, но с разными заголовками. Это назывется предопределенные (предварительно-сконфигурированные) классы.
Модель Конструктора
Один вариант решения это использование функции конструктора, как например здесь:
// функция - конструктор
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 сервере, так что все поддиректории которые я опишу позже расположены относительно нее.
Рекомендуемая структура директорий такова:
Ссылка в вышеупомянутой структуре означает мягкую ссылку, указывающую на реальное месторасположение файлов. Это нужно для того, когда вы например, скачаете новую версию Ext в директорию, то просто измените ссылку на нее, без изменений кода, ссылающегося на нее и можете испытать что все работает правильно в новой версии. Если да, можно оставить все как есть, если нет, просто изменить ссылку на прежнюю.
* css содержит все таблицы стилей. Если у вас есть глобальные стили с цветами компании или шрифтами вы можете создать и эту директорию как ссылку.
* ext ссылается на библиотеку Ext JS как было описано выше
* img ссылается на ваши изображения. Также она может иметь поддиректорию с иконками.
* js будет содержать все JavaScript файлы приложения.
* index.html HTML файл, который является стартовой точкой приложения. Вы можете назвать его как захотите и можете добавить другие файлы, например файлы для процесса аутентификации. В любом случает должна быть одна стартовая точка - файл.
* опционально вы можете создать директорию или ссылку на серверную часть приложения (у меня например ./classes). Вы можете называть их как пожелаете, но придерживаться целостности для приложений, которые пишите (./server, ./php хорошие примеры)
index.html
Самое минимальное содержание файла index.html такое
Чтобы я рекомендовал сделать в файле выше, добавить описательный заголовок не только в этот файл но и во все создаваемые файлы. Также маркер End of file имеет значение. Смотрите File Patterns как пример таких заголовков.
js/application.js
Нам нужен файл, где будет располагаться функция onReady, давайте назовем его application.js. Его минимальное содержимое такое:
Ваш заголовок и подвал может изменяться но удостоверьтесь что установили Ext.BLANK_IMAGE_URL на ваш сервер. Это путь на прозрачное изображение 1х1 пикселей которое используется Ext как указатель расположения ихображения, и если он указывает на неверное месторасположение вы можете получить проблемы при отрисовке, такие как потеря изображений в элементах, отсутствие иконок или что-то подобное.
Вам может также понадобится новой глобальной переменной объекта для приложения ( здесь это Application).
В чем вы должны быть уверены то что Ext.onReady это главная начальная точка приложения.
css/application.css
Здесь будут храниться ваши таблицы стилей, в этом файле. Если вам нужно всего лишь пару правил, то создавать отдельный файл нет необходимости, и будет лучше просто включить его в заголовок страницы.
Обратное тоже истина, помните, вы пишите большое приложение, поэтому все должно быть на своем месте. Если вы напишите таблицу стилей прямо в заголовке файла, то рано или поздно у вас могут возникнуть проблемы при отображении и вы забудете где вы сохранили эти стили.
Ошибочный путь
Что обычно следует когда мы начинаем создавать что то? Давайте напишем. Итак, мы сидим и начинаем писать:
Подождите минутку. С таким подходом у нас будет 10000 строк в нашем application.js очень скоро и это то, чего мы хотим в последнюю очередь. Очевидно, мы пропустили какой-то шаг, как будто мы не могли написать это все сразу в index.html?
Правильный путь: Разбиваем все на части
Даже самое сложное состоит из маленьких подсистем, а те состоят из еще более мелких частей, которые тоже состоят из каких-либо элементов. И при написании больших приложений нет исключения. Теперь настало время определить эти части, компоненты и взаимосвязи между ними.
Итак, сядьте, обдумайте все, набросайте схему, сделайте список, но в результате вы должны знать компоненты - хотя бы самые основные, из которых состоит ваше приложение.
Предопределенные классы
Теперь, когда вы проанализировали приложение и определили его компоненты вы можете начать писать. Но как? Самый лучший подход - написать расширение для классов Ext компонент, у которых есть все конфигурационные опции, или они передаются встроенными в конфигурационный объект. Я использую такие предопределенные классы расширения так как они удобнее и более функциональны чем базовые Ext классы, но их главное назначение - это их настройка для своих нужд. Например, чтобы создать таблицу Персонал со специфичной моделью колонок, хранилищем, опциями сортировки, редакторами и тд.
Если у нас такой подход, наше конфигурационное Окно могло мы выглядеть так:
Создание предопределенных классов.
(Запомните: Следующая структура может работать не во всех ситуациях, есть исключения)
Давайте обсудим один пример:
Что мы здесь делаем? Мы расширяем класс Ext.grid.GridPanel создавая новый класс (расширение) Application.PersonelGrid и мы регистрируем его как новый xtype с именем personnelgrid.
Мы передаем главноей табличной панели все конфигурационные опции необходимые для специфичной таблицы персонала. С этой точки у нас есть новый компонент, строительный блок для нашего приложения, который мы сможем использовать где-либо (окна, регион, отдельно-стоящий) где нам понадобится список персонала. Мы можем создать его так:
или с помощью xtype (для ленивых)
Организация предопределенных классов
Код выше не должен и не нужно запускать его внутри функции onReady, потому что он ничего не делает с DOM структурой документа; он только создает JavaScript объект. Вот почему он может и должен быть написан в отдельном файле (js/Application.PersonnelGrid.js) и он может и должен быть включен в заголовок index.html файла как:
Шаг за шагом мы продолжаем создавать наши предопределенные классы, складывать их в /js директорию, включать их в index.html и строить наше приложение из них, как пазл, собирая из кусочков.
Выглядит хорошо, так?
В любом случае, есть еще что добавить.
Межкомпонентное взаимодействие
Представьте что нам нужен экран с каймой со списком ссылок на западе и таб-панелью в центре. Нажатие ссылки в списке должно создавать новую закладку в центре. Теперь, куда мы должны положить логику этого события и процедуру обработки? На запад или в центр?
Ни в одно из них. Почему? Если у нас есть предопределенные классы которые создают и показывают ссылки на западе (слева) и мы положили всю логику в них, то они больше не могут существовать без центрального региона. Мы можем просто не сможем их использовать без центра, так как не сможем создавать закладки в центре.
Если положить логику в центр - результат будет тот же: центр не сможет существовать без запада.
Единственный компонент, который будет независим в этом случае это их контейнер - border layout, и это единственно правильное место куда следует поместить межкомпонентную связь.
Что мы затем должны сделать? Контейнер (с border layout) должен прослушивать все события с запада и должен создавать закладки в центре как ответ на эти клики. Пример такого межкомпонентного взаимодействия можно найти по ссылке http://examples.extjs.eu/?ex=compcomm
Система представлений
По мере создания приложения число файлов растет, число включений JavaScript файлов тоже (у меня в одном приложении включается около 80 файлов и их количество каждый день растет). Это може уменьшить производительность и продуктивность системы.
Лучший путь решения - это слияние всех javascript файлов и создание одного большого файла, и минимизирование его с помощью JavaScript или других инструментов сжатия. Также вам не понадобится отладка библиотеки Ext для готовой системы.
На продуктивной системе нам надо включить:
Для дополнительной информации о минимизации исходных кодов и создания комбинированных файлов у нас есть другой учебник, который раскрывает эту тему более подробно (http://extjs.com/learn/Tutorial:Building_Ext_From_Source).
Заключение
Вот и все почти... Есть специфичные техники для специальных Ext классов, есть множество других ноу-хау сервер-клиентских приложений, но выше изложена практически основная концепция.
Удачного кодинга!
Источник: http://extjs.com/learn/Tutorial:Writing_a_Big_Application_in_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
Создание контекстного меню (правой кнопки)
Это будет последняя часть учебника!!! Знаю, знаю... было очень весело читать! Ок, достаточно! Теперь мы поговорим о контекстных меню или как их обычно называют меню открывающиеся по правому клику мышки.
Во-первых мы записываем номер ряда, на котором мы нажали:
У нас будет простое меню с тремя пунктами: изменение текущей записи, удаление и печать таблицы.
Ок, давайте определим четыре функции. Первая будет вызываться каждый раз при нажатии правой кнопки мыши на таблице:
Ок, как вы видите мы показываем переменную названную PresidentListingContextMenu. Мы ее еще не создали, мы сделаем это сразу после этого. Давайте определим три функции которые будут вызываться для каждого пункта меню:
Как видите, это те функции, которые мы уже определили. Ок, давайте определим ContextMenu:
Последнее, что нам понадобится это перехватчик событий (actionlistener) который будет вызывать функцию 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
Это будет последняя часть учебника!!! Знаю, знаю... было очень весело читать! Ок, достаточно! Теперь мы поговорим о контекстных меню или как их обычно называют меню открывающиеся по правому клику мышки.
Во-первых мы записываем номер ряда, на котором мы нажали:
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 на нашу панель инструментов:
Давайте теперь определим функцию printListing(). Эта функция вызовет php функцию которая создаст HTML файл таблицы:
Ок, вы можете удивится почему мы должны передавать так много параметров... Дело в том что... мы хотим распечатать всю нашу таблицу а наш DataStore содержит лишь 15 записей. Поэтому наш PHP файл собирается сделать запросы заново еще раз. не ограничивая поиск частичными запросами. Затем наш database.php сложит все это в файл listpresidents.html.
Вот наш database.php файл (сначала выбор)
А затем функция печати:
На самом деле это очень просто. Мы просто вставляем запросы без ограничения количества записей и сохраняет полученный массив в HTML файл. Затем мы возвращаем 1.
Как будут выглядеть результаты на самом деле зависит от вас!
Некоторые предпочитают решения на основе экспорта в PDF формат. Вы можете найти эту информацию в топиках форумов.
Что запомнить:
Этот способ нельзя применить везде
Он не показывает колонки в нужном выбранном порядке и видимости
Это не самое лучшее решение.
Файлы этой части можно скачать здесь http://nicolas.bize.free.fr/ext/tutorial/Tutorial%20Part%2011.zip
Отлично... Я собираюсь сказать одну вещь до того, как буду заспамлен тоннами писем с критикой... Метод, описанный здесь для печати вашей таблицы не единственно возможный и не самый оригинальный. Это просто быстрый пример того, как можно распечатать таблицу.
Наш метод - это просто создание 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
Подписаться на:
Сообщения (Atom)