<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>jQuery и другое...</title>
	<atom:link href="http://www.linkexchanger.su/feed" rel="self" type="application/rss+xml" />
	<link>http://www.linkexchanger.su</link>
	<description>css, html, php, javascript, jQuery, ajax ... - решения, примеры, рецепты</description>
	<lastBuildDate>Sun, 08 Jan 2012 13:25:49 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.2</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Что такое этот новый jQuery.Callbacks Object</title>
		<link>http://www.linkexchanger.su/2012/875.html</link>
		<comments>http://www.linkexchanger.su/2012/875.html#comments</comments>
		<pubDate>Sun, 08 Jan 2012 13:12:19 +0000</pubDate>
		<dc:creator>Андрей</dc:creator>
				<category><![CDATA[Остальное]]></category>

		<guid isPermaLink="false">http://www.linkexchanger.su/?p=875</guid>
		<description><![CDATA[Оригинал статьи также доступен в блоге автора.
В не столь давно вышедшей версии jQuery 1.7 появился новый объект Callbacks, о котором сегодня и пойдёт речь.
В официальной документации jQuery.Callbacks описан, как многоцелевой объект, представляющий собой список функций обратного вызова (callbacks &#8211; далее просто колбэков) и мощные инструменты по управлению этим списком.
Я просматривал возможности этого объекта, когда он [...]]]></description>
			<content:encoded><![CDATA[<blockquote><p>Оригинал статьи также доступен в <a href="http://blog.zalab.net/2012/01/jquerycallbacks-object.html">блоге автора</a>.</p></blockquote>
<p>В не столь давно вышедшей версии jQuery 1.7 появился новый объект <b>Callbacks</b>, о котором сегодня и пойдёт речь.<br />
В официальной документации <a href="http://api.jquery.com/jQuery.Callbacks/">jQuery.Callbacks</a> описан, как многоцелевой объект, представляющий собой список функций обратного вызова (callbacks &#8211; далее просто колбэков) и мощные инструменты по управлению этим списком.</p>
<p>Я просматривал возможности этого объекта, когда он был ещё только в разработке, и надо сказать, что возможностей у него изначально было немного больше, чем осталось в релизной версии. Например, сейчас отсутствует возможность создания очереди (queue) колбэков, которые вызываются по одному на каждый вызов <code>fire()</code>. Видимо, команда jQuery, решила немного подсократить код, убрав &laquo;ненужные/редкоиспользуемые&raquo; возможности, чтобы сэкономить в весе библиотеки. Это маленький экскурс в историю Callbacks, но далее я буду описывать только доступные сейчас функции и в конце напишу небольшое возможное улучшение этого объекта.<span id="more-875"></span></p>
<h2>
Назначение</h2>
<p>Прежде чем приступить к подробному изучению этого нового объекта jQuery.Callbacks, хочу остановиться на том, для чего же вообще нужен этот объект. Довольно часто в JavaScript коде используются колбэки &#8211; функции, которые вызываются при наступлении некоторого события, например, после завершения какого-то действия, самым ярким примером может послужить запрос AJAX. И при этом часто возникает потребность вызвать не одну функцию, а сразу несколько (заранее неизвестно сколько, может быть пару, может быть пару десятков, а может вообще ни одной) &#8211; это известный и простой <a href="http://ru.wikipedia.org/wiki/%D0%9D%D0%B0%D0%B1%D0%BB%D1%8E%D0%B4%D0%B0%D1%82%D0%B5%D0%BB%D1%8C_(%D1%88%D0%B0%D0%B1%D0%BB%D0%BE%D0%BD_%D0%BF%D1%80%D0%BE%D0%B5%D0%BA%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F)">паттерн &laquo;Наблюдатель&raquo;</a>. И вот для таких случаев и оказывается полезен рассматриваемый объект jQuery.Callbacks. В самом jQuery этот объект используется (начиная с версии 1.7) внутри jQuery.Deferred и jQuery.ajax. Также авторы jQuery сделали этот объект общедоступным и задокументировала его, чтобы другие разработчики могли его использовать при реализации собственных компонентов.</p>
<h2>
Конструктор: jQuery.Callbacks(flags)</h2>
<p>Вызовом конструктора создаётся объект callbacks, который имеет ряд методов для управления списком колбэков.<br />
Параметр <code>flags</code> необязательный и позволяет задать параметры работы объекта, возможные значения параметра мы рассмотрим ниже.</p>
<pre class="brush: jscript;">
var callbacks = $.Callbacks();
</pre>
<p>К созданному объекту <code>callbacks</code> мы сможем теперь добавлять функции-колбэки в список, удалять их, вызывать, снова вызывать (если это не было запрещено при создании объекта), проверять статус объекта (был ли уже вызов или ещё нет) с помощью таких методов объекта, как <code>add()</code>, <code>remove()</code>, <code>fire()</code> и пр. Выполняются колбэки, кстати, в порядке их добавления в список.</p>
<p>Отмечу, что это не &laquo;настоящий&raquo; конструктор экземпляра класса, поэтому использовать оператор <code>new</code> при его вызове не требуется (и даже бессмысленно).</p>
<p>По этой причине не получится проверить, является ли объект экземпляром Callbacks, способом, стандартным для JS:</p>
<pre class="brush: jscript;">
if (obj instanceof $.Callbacks) {
    obj.add(fn);
}
</pre>
<p>Выражение под if всегда возвращает false. Но можно положиться на один из известных методов этого объекта (или сразу на несколько), например, можно проверить так:</p>
<pre class="brush: jscript;">
if (obj.fire) {
    obj.add(fn);
}
</pre>
<p>На самом деле, внутри этой функции создаётся обычный JS-объект с определённым набором методов, которые опираются на своё замыкание &#8211; это довольно распространённый в JavaScript способ задания приватных (private) переменных, недоступных вне этого псевдоконструктора.</p>
<p>Также, благодаря такому псевдоконструктору, методы этого объекта никак не зависят от контекста вызова &#8211; объекта, которому они принадлежат, а это значит, что их можно смело присваивать в свойства другого объекта, не заботясь о смене контекста, &#8211; всё по прежнему будет работать корректно. Это справедливо для всех методов, кроме <code>fire</code>, он как раз таки зависит от контекста, но использует его в качестве контекста выполнения колбэков из списка, т.е. этот метод в ряде случаев не просто можно, а именно <b>нужно</b> присваивать свойствам другого объекта со сменой контекста. Например:</p>
<pre class="brush: jscript;">
var c = $.Callbacks(), obj = {};
obj.register = c.add;
obj.register(function() { console.log('fired'); });
c.fire();
// output: 'fired'
</pre>
<h2>
Флаги</h2>
<blockquote><p>
<i><b>Примечание</b>: далее по тексту под словами &laquo;вызов метода <code>fire()</code>&raquo; понимается вызов выполнения колбэков из списка в том числе и методом <code>fireWith()</code>.</i></p></blockquote>
<p>Параметр конструктора <code>flags</code> &#8211; это строка, в которой через пробел можно указать флаги &#8211; опции, в соответствии с которыми будет работать созданный объект <code>callbacks</code>. Поддерживаются такие флаги:</p>
<p><b>once</b> &#8211; указывает, что список колбэков  может быть выполнен только единожды, второй и последующие вызовы метода <code>fire()</code> будут безрезультатны (как это сделано в объекте deferred), если этот флаг не указан, то можно несколько раз вызывать метод <code>fire()</code>.</p>
<p><b>memory</b> &#8211; указывает, что необходимо запоминать параметры последнего вызова метода <code>fire()</code> (и выполнения колбэков из списка) и немедленно выполнять добавляемые колбэки с соответствующими параметрами, если они добавляются уже после вызова метода <code>fire()</code> (как это сделано в объекте deferred).</p>
<p><b>unique</b> &#8211; указывает, что каждый колбэк может быть добавлен в список только один раз, повторная попытка добавить колбэк в список ни к чему ни приведёт.</p>
<p><b>stopOnFalse</b> &#8211; указывает, что нужно прекратить выполнение колбэков из списка, если какой-то из них вернул <code>false</code>, в пределах текущей сессии вызова <code>fire()</code>. Следующий вызов метода <code>fire()</code> начинает новую сессию выполнения списка колбэков, и они будут выполняться опять до тех пор, пока один из списка не вернёт <code>false</code> либо пока не закончатся.</p>
<h2>
Методы</h2>
<p>Ниже я приведу список методов с кратким описанием, примеры есть в официальных доках и для некоторых методов в следующем разделе. В общем-то методы довольно просты и ведут себя вполне ожидаемо.</p>
<p><b>callbacks.add(callbacks)</b> <i>returns: callbacks</i> &#8211; добавляет в список колбэки, можно одновременно передавать в аргументах этого метода несколько функций (несколько аргументов) или массивов функций (можно одновременно и то и другое), можно даже вложенные массивы передавать. Все аргументы (или элементы массива), не являющиеся функциями, просто игнорируются. Метод (этот и некоторые далее) возвращает контекст своего вызова, позволяя тем самым организовать цепочку вызовов нескольких методов одного объекта подряд, как это принято в jQuery.</p>
<p><b>callbacks.remove(callbacks)</b> <i>returns: callbacks</i> &#8211; удаляет колбэки из списка, причем даже если колбэк был добавлен дважды, удаление его произойдёт с обеих позиций. Т.о. если вызвать метод удаления некоторого колбэка из списка, то можно быть уверенным, что его в списке больше нет, сколько бы раз его не добавляли. Можно передавать несколько функций одновременно как несколько аргументов, массивы передавать нельзя, все аргументы не функции игнорируются.</p>
<p><b>callbacks.has(callback)</b> <i>returns: boolean</i> &#8211; проверяет, есть ли указанная функция в списке колбэков.</p>
<p><b>callbacks.empty()</b> <i>returns: callbacks</i> &#8211; очищает список колбэков.</p>
<p><b>callbacks.disable()</b> <i>returns: callbacks</i> &#8211; &laquo;отключает&raquo; объект callbacks, все действия с ним будут безрезультатны. При этом перестают работать вообще все методы: <code>add</code> &#8211; ни к чему не приводит, <code>has</code> &#8211; всегда возвращает <code>false</code> и пр.</p>
<p><b>callbacks.disabled()</b> <i>returns: boolean</i> &#8211; проверяет, отключен ли объект callbacks, после вызова <code>disable()</code> будет возвращать <code>true</code>.</p>
<p><b>callbacks.lock()</b> <i>returns: callbacks</i> &#8211; фиксирует текущее состояние объекта callbacks относительно параметров и состояния выполнения списка колбэков. Этот метод актулен при использовании флага <i>memory</i> и предназначен для блокирования только последующих вызовов <code>fire()</code>, в остальных случаях он равносилен вызову <code>disable()</code>.</p>
<blockquote><p>
Детально этот метод работает так: если флаг <i>memory</i> не указан или ещё ни разу не был вызван метод <code>fire()</code> или последняя сессия выполнения колбэков была прервана возвратом <code>false</code> одним из них, то вызов <code>lock()</code> равносилен вызову <code>disable()</code> (именно он и вызывается внутри) и вызов <code>disabled()</code> в таком случае вернёт <code>true</code>, иначе будут заблокированы только последующие вызовы <code>fire()</code> &#8211; они не приведут ни к выполнению колбэков, ни к изменению параметров выполнения добавляемых колбэков (при наличии флага <i>memory</i>).</p></blockquote>
<p><b>callbacks.locked()</b> <i>returns: boolean</i> &#8211; проверяет, зафиксирован ли объект callbacks методом <code>lock()</code>, также верёт <code>true</code> после вызова <code>disable()</code>.</p>
<p><b>callbacks.fireWith( [context] [, args] )</b> <i>returns: callbacks</i> &#8211; запускает выполнение всех колбэков в списке с указанным контекстом и аргументами. <b>context</b> &#8211; указывает контекст выполнения колбэка (объект, доступный через <code>this</code> внутри функции). <b>args</b> &#8211; массив (именно массив) аргументов, передаваемых в колбэк.</p>
<p><b>callbacks.fire( arguments )</b> <i>returns: callbacks</i> &#8211; запускает выполнение всех колбэков в списке с контекстом вызова и аргументами этого метода. <b>arguments</b> &#8211; список аргументов (не массив, как в методе <code>fireWith()</code>). Т.е. контекстом вызова и аргументами колбэков будут контекст и аргументы метода <code>fire()</code>.</p>
<p>Пример, как можно эквивалентно запустить исполнение колбэков с одинаковыми параметрами и контекстом:</p>
<pre class="brush: jscript;">
var callbacks = $.Callbacks(),
    context = { test: 1 };
callbacks.add(function(p, t) { console.log(this.test, p, t); });

callbacks.fireWith(context, [ 2, 3 ]);
// output: 1 2 3

context.fire = callbacks.fire;
context.fire(2, 3);
// output: 1 2 3
</pre>
<p>Колбэки из списка выполняются в том порядке, в котором они в этот список добавлялись. После выполнения колбэков при указанном флаге <i>once</i> список будет очищен, а если при этом не указан флаг <i>memory</i> или выполнение колбэков было прервано возвратом <code>false</code>, то объект callbacks будет отключен методом <code>disable()</code>.</p>
<h2>
Примеры</h2>
<p>Давайте посмотрим как работают флаги на примерах. Во всех примерах используются такие функции:</p>
<pre class="brush: jscript;">
function fn1( value ){
    console.log( value );
}

function fn2( value ){
    fn1(&quot;fn2 says:&quot; + value);
    return false;
}
</pre>
<h3>
$.Callbacks():</h3>
<pre class="brush: jscript;">
var callbacks = $.Callbacks();
callbacks.add( fn1 );
callbacks.fire( &quot;foo&quot; );
callbacks.add( fn2 );
callbacks.add( fn1 );
callbacks.fire( &quot;bar&quot; );
callbacks.remove( fn2 );
callbacks.fire( &quot;foobar&quot; );
console.log(callbacks.disabled());

/*
output:
foo
bar
fn2 says:bar
bar
foobar
foobar
false
*/
</pre>
<p>Никакие флаги не указали &#8211; вполне ожидаемое поведение.</p>
<h3>
$.Callbacks(&#8216;once&#8217;):</h3>
<pre class="brush: jscript;">
var callbacks = $.Callbacks( &quot;once&quot; );
callbacks.add( fn1 );
callbacks.fire( &quot;foo&quot; );
callbacks.add( fn2 );
callbacks.add( fn1 );
callbacks.fire( &quot;bar&quot; );
callbacks.remove( fn2 );
callbacks.fire( &quot;foobar&quot; );
console.log(callbacks.disabled());

/*
output:
foo
true
*/
</pre>
<p>Тут всё понятно &#8211; один раз выполнили, что было, далее ничего не происходит, что ни делали, т.к. список уже отключен.</p>
<h3>
$.Callbacks(&#8216;memory&#8217;):</h3>
<pre class="brush: jscript;">
var callbacks = $.Callbacks( &quot;memory&quot; );
callbacks.add( fn1 );
callbacks.fire( &quot;foo&quot; );
callbacks.add( fn2 );
callbacks.add( fn1 );
callbacks.fire( &quot;bar&quot; );
callbacks.remove( fn2 );
callbacks.fire( &quot;foobar&quot; );
console.log(callbacks.disabled());

/*
output:
foo
fn2 says:foo
foo
bar
fn2 says:bar
bar
foobar
foobar
false
*/
</pre>
<p>Здесь, вроде, тоже ничего сложного &#8211; после первого выполнения каждое добавление колбэка вызывает его немедленное выполнение, а потом снова вызываем выполнение всего списка. При этом одну функцию мы добавили в список дважды &#8211; она дважды и срабатывает.</p>
<h3>
$.Callbacks(&#8216;unique&#8217;):</h3>
<pre class="brush: jscript;">
var callbacks = $.Callbacks( &quot;unique&quot; );
callbacks.add( fn1 );
callbacks.fire( &quot;foo&quot; );
callbacks.add( fn2 );
callbacks.add( fn1 );
callbacks.fire( &quot;bar&quot; );
callbacks.remove( fn2 );
callbacks.fire( &quot;foobar&quot; );
console.log(callbacks.disabled());

/*
output:
foo
bar
fn2 says:bar
foobar
false
*/
</pre>
<p>А в этом случае повторное добавление функции <code>fn1</code> было проигнорировано.</p>
<h3>
$.Callbacks(&#8217;stopOnFalse&#8217;):</h3>
<pre class="brush: jscript;">
var callbacks = $.Callbacks( &quot;stopOnFalse&quot; );
callbacks.add( fn1 );
callbacks.fire( &quot;foo&quot; );
callbacks.add( fn2 );
callbacks.add( fn1 );
callbacks.fire( &quot;bar&quot; );
callbacks.remove( fn2 );
callbacks.fire( &quot;foobar&quot; );
console.log(callbacks.disabled());

/*
output:
foo
bar
fn2 says:bar
foobar
foobar
false
*/
</pre>
<p>Колбэк <code>fn2</code> прерывает цепочку выполнения, т.к. возвращает <code>false</code>.</p>
<p>Это простые примеры, а теперь давайте попробуем поиграться с комбинациями флагов &#8211; будет немного интереснее:</p>
<h3>
$.Callbacks(&#8216;once memory&#8217;):</h3>
<pre class="brush: jscript;">
var callbacks = $.Callbacks( &quot;once memory&quot; );
callbacks.add( fn1 );
callbacks.fire( &quot;foo&quot; );
callbacks.add( fn2 );
callbacks.add( fn1 );
callbacks.fire( &quot;bar&quot; );
callbacks.remove( fn2 );
callbacks.fire( &quot;foobar&quot; );
console.log(callbacks.disabled());

/*
output:
foo
fn2 says:foo
foo
false
*/
</pre>
<p>Видим, что сработали только первый <code>fire()</code> и добавление новых колбэков привело к их немедленному выполнению с параметрами первого <code>fire()</code>.</p>
<h3>
$.Callbacks(&#8216;once memory unique&#8217;):</h3>
<pre class="brush: jscript;">
var callbacks = $.Callbacks( &quot;once memory unique&quot; );
callbacks.add( fn1 );
callbacks.fire( &quot;foo&quot; );
callbacks.add( fn2 );
callbacks.add( fn1 );
callbacks.fire( &quot;bar&quot; );
callbacks.remove( fn2 );
callbacks.fire( &quot;foobar&quot; );
console.log(callbacks.disabled());

/*
output:
foo
fn2 says:foo
foo
false
*/
</pre>
<p>Здесь результат тот же, несмотря на то, что мы указали флаг <i>unique</i> и дважды добавляем <code>fn1</code>, &#8211; второй раз добавление этой функции в список сработало, потому что при указанном флаге <i>once</i> после выполнения колбэков список очищается, а флаг <i>memory</i> указывает, что последующие добавления колбэков будут приводить к их немедленному выполнению без помещения в список, а так как список пуст &#8211; то добавление любой функции всегда уникально. Но этот флаг сыграет свою роль при попытке добавить за раз несколько колбэков, среди которых есть дублирующиеся, если в предыдущем коде изменить 4-ю строку как показано ниже, то <code>fn2</code> всё равно выполнена будет только один раз (а без флага <i>unique</i> была бы выполнена три раза):</p>
<pre class="brush: jscript;">
callbacks.add( fn2, fn2, fn2 );
</pre>
<h3>
$.Callbacks(&#8216;once memory stopOnFalse&#8217;):</h3>
<pre class="brush: jscript;">
var callbacks = $.Callbacks( &quot;once memory stopOnFalse&quot; );
callbacks.add( fn1 );
callbacks.fire( &quot;foo&quot; );
callbacks.add( fn2 );
callbacks.add( fn1 );
callbacks.fire( &quot;bar&quot; );
callbacks.remove( fn2 );
callbacks.fire( &quot;foobar&quot; );
console.log(callbacks.disabled());

/*
output:
foo
fn2 says:foo
true
*/
</pre>
<p>Возврат <code>false</code> заблокировал все дальнейшие выполнения колбэков и при наличии флага <i>once</i> вообще привёл к отключению объекта callbacks.</p>
<p>Я не буду рассматривать все возможные комбинации флагов, я постарался выбрать наиболее интересные (не совсем простые) и объяснить поведение callbacks. Остальные комбинации можно протестировать самостоятельно, например, воспользовавшись заготовкой: <a href="http://jsfiddle.net/zandroid/JXqzB/">http://jsfiddle.net/zandroid/JXqzB/</a></p>
<h2>
Обещанное улучшение</h2>
<p>Улучшение, конечно, совсем не обязательное и даже, может быть, в какой-то степени надуманное, не судите строго.<br />
Идея улучшения в том, чтобы опустить вызов метода <code>fire()</code>, а вместо этого сам объект callbacks использовать как функцию. Для этого пишем такую функцию:</p>
<pre class="brush: jscript;">
(function($, undefined){
    $.FCallbacks = function(flags, fns) {
        var i = $.type(flags) === 'string' ? 1 : 0,
            callbacks = $.Callbacks(i ? flags : undefined);
        callbacks.add(Array.prototype.slice.call(arguments, i))
        return $.extend(callbacks.fire, callbacks, { fcallbacks: true });
    };
})(jQuery);
</pre>
<p>И без лишних слов посмотрим пример использования:</p>
<pre class="brush: jscript;">
function fn1(p1, p2) { console.log('fn1 says:', this, p1, p2); }
function fn2(p1, p2) { console.log('fn2 says:', this, p1, p2); }
var callbacks = $.FCallbacks('once', fn1, fn2);
callbacks.add(fn2);
callbacks(2, 3);
</pre>
<p>Также ещё у нового &laquo;конструктора&raquo; появилась возможность сразу же передавать начальные колбэки в параметрах, без лишнего вызова <code>add()</code>.<br />
Ну и в работе: <a href="http://jsfiddle.net/zandroid/RAVtF/">http://jsfiddle.net/zandroid/RAVtF/</a></p>
<h2>
Реальное использование</h2>
<p>Предвидя некоторые комментарии по поводу реальных примеров использования (и получив их уже в другом месте публикации этой статьи) сразу постараюсь на них ответить.</p>
<p><b>Callbacks</b> на самом деле теперь используется <b>очень многими</b> пользователями jQuery 1.7+ и был реализован командой разработчиков не просто, потому что им захотелось сделать новый фичер.</p>
<p>Давно в библиотеке был реализован метод <code>$.ajax()</code>, который по своей природе не что иное, как надстройка над неким Deferred &#8211; разработчики улучшили код, вынесли его отдельно от основного кода <code>$.ajax()</code> (для возможности повторного использования и упрощения тестирования) и решили, а почему бы не опубликовать этот код (дать доступ пользователям библиотеки к нему и задокументировать его) &#8211; получился <code>$.Deferred</code>.</p>
<p>В свою очередь <code>$.Deferred</code> &#8211; это изначально два (<code>done()</code> и <code>fail()</code>), а теперь три (+ ещё <code>progress()</code>) надстройки над Callbacks, который был сделан как внутренний код <code>$.Deferred</code>. И снова, разработчики улучшили и отделили этот код от <code>$.Deferred</code>, реализовав последний через <code>$.Callbacks</code> (кстати, source-код <code>$.Deferred</code> стал при этом намного понятнее и читабельнее).</p>
<p>Вывод: разработчики не ставят главной целью добавление новых &laquo;никому не нужных&raquo; фичеров, они оптимизируют уже существующий внутренний код, попутно публикуя побочные, но не менее полезные от этого, результаты. И каждый раз, когда вы используете <code>$.ajax()</code> &#8211; знайте, вы используете <code>$.Deferred</code>, а значит и <code>$.Callbacks</code>. Это и есть пример реального использования.</p>
<p>Всех с наступившими праздниками, спасибо за внимание.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.linkexchanger.su/2012/875.html/feed</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>jQuery 1.7 Released</title>
		<link>http://www.linkexchanger.su/2011/865.html</link>
		<comments>http://www.linkexchanger.su/2011/865.html#comments</comments>
		<pubDate>Fri, 04 Nov 2011 21:01:32 +0000</pubDate>
		<dc:creator>Андрей</dc:creator>
				<category><![CDATA[Остальное]]></category>

		<guid isPermaLink="false">http://www.linkexchanger.su/?p=865</guid>
		<description><![CDATA[jQuery 1.7 готов к загрузке! Можно скачать с jQuery CDN:
 http://code.jquery.com/jquery-1.7.js
 http://code.jquery.com/jquery-1.7.min.js
Так же этот релиз будет готов к загрузке с Google и Microsoft CDNs в течении дня или двух.
Команда jQuery благодарит всех, кто принимал участие в тестировании и и поиске багов в бета-версиях и верит в прочность и стабильность релиза. И просит всех, кто найдёт [...]]]></description>
			<content:encoded><![CDATA[<p>jQuery 1.7 готов к загрузке! Можно скачать с jQuery CDN:<br />
<a href="http://code.jquery.com/jquery-1.7.js"> http://code.jquery.com/jquery-1.7.js</a><br />
<a href="http://code.jquery.com/jquery-1.7.min.js"> http://code.jquery.com/jquery-1.7.min.js</a></p>
<p>Так же этот релиз будет готов к загрузке с Google и Microsoft CDNs в течении дня или двух.</p>
<p>Команда jQuery благодарит всех, кто принимал участие в тестировании и и поиске багов в бета-версиях и верит в прочность и стабильность релиза. И просит всех, кто найдёт какие-либо баги, рапортовать о них на <a href="http://bugs.jquery.com/">баг-трекер</a> и по-возможности подкреплять тестами для воспроизведения на <a href="http://jsfiddle.net/">jsFiddle</a> для более быстрого анализа проблем.<br />
<span id="more-865"></span></p>
<h2>Что нового в версии 1.7</h2>
<p>Краткий список нововведений можно быстро получить просмотрев <a href="http://api.jquery.com/category/version/1.7/">описание API с тегом 1.7</a>, а ниже будет описание больших новшеств версии 1.7 и некоторые вещи, которые пока ещё не вошли в документацию по API.</p>
<h3>Новый API событий: .on() и .off()</h3>
<p>Новый <strong>.on() и .off() API</strong> делает универсальным и более удобным привязку к событиям в документе.</p>
<pre class="brush: jscript;">
$(elements).on( events [, selector] [, data] , handler );
$(elements).off( [ events ] [, selector] [, handler] );
</pre>
<p>Когда указан <code>selector</code>, то метод <code>.on()</code> работает так же, как <code>.delegate()</code> &#8211; привязывает обработчики события, фильтруя элементы по селектору. Если же <code>selector</code> не указан или равен <code>null</code>, то вызов работает как обычный <code>.bind()</code>. Существует некоторая неоднозначность: если аргумент <code>data</code> является строкой, требуется явно передавать аргумент <code>selector</code> в виде строки или <code>null</code>, чтобы <code>data</code> не был ошибочно принят за селектор. Передавая в качестве <code>data</code> объект, не требуется специально беспокоится об указании селектора.</p>
<p>Все существующие виды привязок к событиям (и соответствующие им метода отвязки от событий) доступны и в версии 1.7, но рекомендуется использовать именно <code>.on()</code> для новых проектов, где гарантированно будет использоваться версия 1.7 или старше. Ниже приведены небольшие примеры, демонстрирующие аналогичные привязки к событиям с использованием старого и нового API:</p>
<pre class="brush: jscript;">
$('a').bind('click', myHandler);
$('a').on('click', myHandler);

$('form').bind('submit', { val: 42 }, fn);
$('form').on('submit', { val: 42 }, fn);

$(window).unbind('scroll.myPlugin');
$(window).off('scroll.myPlugin');

$('.comment').delegate('a.add', 'click', addNew);
$('.comment').on('click', 'a.add', addNew);

$('.dialog').undelegate('a', 'click.myDlg');
$('.dialog').off('click.myDlg', 'a');

$('a').live('click', fn);
$(document).on('click', 'a', fn);

$('a').die('click');
$(document).off('click', 'a');
</pre>
<h3>Улучшена производительность делегированных событий (Delegated Events)</h3>
<p>Делегирование событий (event delegation) приобретает всё большую важность с ростом размера и сложности страниц. Такие фреймворки, как Backbone и Sproutcore, широко используют делегирование событий. Имея это в виду, обработка событий в jQuery 1.7 была переработана с целью ускорить процесс обработки событий, особенно для наиболее распространённых случаев.</p>
<p>Чтобы оптимизировать код для часто встречающихся видов селекторов, команда jQuery рассмотрела срез с Google Codesearch. Почти две третьих всех селекторов, используемых в <code>.live()</code> и <code>.delegate()</code>, имеют вид <code>tag#id.class</code>, где используются один или несколько тегов, id или классов. В оптимизации разбора этих простых селекторов команда jQuery добилась таких успехов, что смогла обойти даже время нативного <code>matchesSelector</code> браузеров во время обработки событий. Для разбора более сложных селекторов по-прежнему используется Sizzle движок.</p>
<p>В конечном результате имеет почти двукратное ускорение по сравнению с версией 1.6.4:<br />
<img src="http://www.linkexchanger.su/wp-content/uploads/2011/11/QHvsl.png" alt="Delegated Events" title="Улучшена производительность делегированных событий" width="407" height="371" class="alignleft size-full wp-image-870" /></p>
<h3>Улучшение поддержки HTML 5 в IE 6/7/8</h3>
<p>Любой, кто пробовал использовать новые теги из HTML5, например, такие как <code>&lt;section&gt;</code>, гарантировано имели проблемы не только с тем, что IE 6/7/8 не понимают этих тегов, но ещё и совсем удаляют их из документа. В jQuery 1.7 есть встроенная поддержка тегов HTML5 для старых IE в таких методах, как <code>.html()</code>. Этот механизм реализован на том же уровне, что и ранее <a href="http://jdbartlett.com/innershiv/">innerShiv</a>. <strong>По-прежнему требуется подключать <a href="http://code.google.com/p/html5shiv/">HTML5Shiv</a> в секции <code>head</code> документа для поддержки HTML5-тегов в старых IE.</strong> Подробности смотрите на <a href="http://paulirish.com/2011/the-history-of-the-html5-shiv/">The Story of the HTML5 Shiv</a>.</p>
<h3>Интуитивная работа переключения анимации</h3>
<p>В предыдущих версиях jQuery переключение анимации, такое как <code>.slideToggle()</code> или <code>.fadeToggle()</code>, не срабатывало правильно, если несколько анимаций были запущены последовательно и предыдущая была прервана методом <code>.stop()</code>. Это было исправлено в версии 1.7, теперь перед запуском анимации запоминаются начальные значения, и происходит сброс значений, если переключение анимации было прервано преждевременно.</p>
<h3>Asynchronous Module Definition (AMD)</h3>
<p>Теперь jQuery <a href="http://bugs.jquery.com/ticket/7102">поддерживает</a> асинхронное определение модуля &#8211; <a href="https://github.com/amdjs/amdjs-api/wiki/AMD">AMD API</a>. Это не означает, что jQuery является загрузчиком скриптов (script loader); jQuery всего лишь использует AMD-совместимую модель определения модуля, поддерживаемую, например, RequireJS или curl.js, поэтому jQuery может быть подключена динамически таким загрузчиком, и событие <code>ready</code> также является подконтрольным ему. Теперь AMD-совместимые загрузчики могут подключать немодифицированную версию jQuery с CDN (например, от Google или Microsoft). Особая благодарность выражается James Burke (@jrburke) за предоставленные патч и тесты и ожидание включения их сборку.</p>
<h3>jQuery.Deferred</h3>
<p>Объект <a href="http://api.jquery.com/category/deferred-object/">jQuery.Deferred</a> был расширен новыми обработчиками прогресса и методами уведомления, вызывающими этими обработчики. Это позволяет асинхронно уведомлять слушателей о прогрессе исполнения без завершения или отмены запроса. Дополнительно появился метод <code>state()</code>, который возвращает состояние Deferred-объекта, это в первую очередь полезно при отладке.</p>
<p>Теперь Deferreds реализованы через использование новой функциональности &#8211; <a href="http://api.jquery.com/category/callbacks-object/">jQuery.Callbacks</a>, обобщающей функции вызова очереди или серии колбэков. Эта новая функциональность может быть интересно писателям плагинов, хотя Deferreds и система событий обеспечивают более высокий уровень подобной функциональности.</p>
<h3>jQuery.isNumeric()</h3>
<p>Внутри jQuery было найдено несколько ситуаций, когда необходимо знать, является ли аргумент числом или может быть успешно сконвертирован в число.  И было решено написать и задокументировать метод <a href="http://api.jquery.com/jQuery.isNumeric">jQuery.isNumeric()</a> как полезную утилиту. Передайте внутрь него любой аргумент и получите <code>true</code> или <code>false</code> в качестве результата.</p>
<h3>Удаленная функциональность</h3>
<p><strong>event.layerX и event.layerY:</strong> в версии 1.7 удалены эти нестандартные свойства. Хотя ранее они никому не мешали, Chrome 16 показывает множество предупреждений в консоли. Поэтому было решено отказаться от них и удалить. Там, где ещё поддерживаются эти свойства, они будут доступны через <code>event.originalEvent.layerX</code> и <code>event.originalEvent.layerY</code>.</p>
<p><strong>jQuery.isNaN():</strong> эта недокументированная утилита была также удалена. Она имела имя встроенной функции JavaScript, но семантически не соответствовала ей. Новая функция <code>jQuery.isNumeric()</code> позволяет делать тоже самое. Несмотря на то, что функция <code>jQuery.isNaN()</code> была недокументирована, некоторые проекты на Github использовали её. Команда jQuery связалась с ними и предупредили о проблеме и возможных решениях.</p>
<p><strong>jQuery.event.proxy():</strong> этот недокументированный и устаревший метод был удалён. Вместо него следует использовать задокументированный метод <code>jQuery.proxy</code>.</p>
<p><em>Прочие благодарности и change log со списком всех поправленных багов можно прочитать в конце оригинальной статьи <a href="http://blog.jquery.com/2011/11/03/jquery-1-7-released/">http://blog.jquery.com/2011/11/03/jquery-1-7-released/</a></em></p>
]]></content:encoded>
			<wfw:commentRss>http://www.linkexchanger.su/2011/865.html/feed</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>jQuery. Сборник рецептов (+ CD-ROM)</title>
		<link>http://www.linkexchanger.su/2011/842.html</link>
		<comments>http://www.linkexchanger.su/2011/842.html#comments</comments>
		<pubDate>Tue, 02 Aug 2011 09:21:13 +0000</pubDate>
		<dc:creator>Gennady</dc:creator>
				<category><![CDATA[jQuery]]></category>
		<category><![CDATA[jQuery UI]]></category>
		<category><![CDATA[jQuery документация]]></category>
		<category><![CDATA[ajax]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[UI]]></category>
		<category><![CDATA[документация jQuery]]></category>

		<guid isPermaLink="false">http://www.linkexchanger.su/?p=842</guid>
		<description><![CDATA[В августе в интернет-магазинах должна появиться в продаже книга jQuery. Сборник рецептов (+ CD-ROM). Это второе издание книги, которая вышла в издательстве &#171;БХВ-Петербург&#187; чуть более года назад. Второе издание дорабатывалось мной с учетом замечаний и пожеланий читателей и вновь появившихся возможностей.

Геннадий Самков
jQuery. Сборник рецептов (+ CD-ROM)
Издательство: БХВ-Петербург, 2011 г.
Мягкая обложка, 416 стр.
ISBN 978-5-9775-0732-5
Тираж: 1500 экз.
Формат: [...]]]></description>
			<content:encoded><![CDATA[<p>В августе в интернет-магазинах должна появиться в продаже книга <strong>jQuery. Сборник рецептов (+ CD-ROM)</strong>. Это <strong>второе издание</strong> книги, которая вышла в издательстве &laquo;БХВ-Петербург&raquo; чуть более года назад. Второе издание дорабатывалось мной с учетом замечаний и пожеланий читателей и вновь появившихся возможностей.</p>
<ul style="float: right; width: 200px">
<li>Геннадий Самков</li>
<li>jQuery. Сборник рецептов (+ CD-ROM)</li>
<li>Издательство: БХВ-Петербург, 2011 г.</li>
<li>Мягкая обложка, 416 стр.</li>
<li>ISBN 978-5-9775-0732-5</li>
<li>Тираж: 1500 экз.</li>
<li>Формат: 70&#215;100/16</li>
</ul>
<p><a href="http://www.ozon.ru/context/detail/id/6843919/?partner=lexsu&#038;from=bar" title="jQuery. Сборник рецептов" target="_blank"><img style="border:1px solid #333;" src="http://www.linkexchanger.su/wp-content/uploads/2011/07/jquery2.jpg" alt="jQuery. Сборник рецептов" /></a><span id="more-842"></span></p>
<p><strong>Аннотация:</strong><br />
Книга является сборником решений наиболее часто встречающихся задач при веб-программировании пользовательских интерфейсов с использованием библиотеки jQuery. Рассмотрены практически все методы и вспомогательные функции jQuery, в том числе обеспечивающие взаимодействие jQuery и AJAX. Подробно рассказано о надстройке UI jQuery. Приведено большое количество примеров использования плагинов для jQuery &#8211; создание графиков и диаграмм, фотогалерей, меню, работа с таймерами и cookies, обработка табличных данных и др. Во втором издании в примерах используется библиотека jQuery версий 1.4.4 и 1.5.2, а также надстройка UI jQuery 1.8.9.<br />
Компакт-диск содержит примеры из книги, файлы библиотеки jQuery 1.4.4 и 1.5.2, файлы надстройки UI jQuery 1.8.9, а также файлы расширений сторонних разработчиков.</p>
<p>От себя могу добавить, что книга написана в таком же духе, как и <a href="http://www.linkexchanger.su/2010/115.html">первое издание</a>, т.е. примеры, примеры и еще раз примеры плюс подробные разъяснения&#8230;.</p>
<p><a target="_blank" href="http://mmedia.ozon.ru/multimedia/book_file/1003104287.pdf">Отрывок книги в PDF</a></p>
<p><strong>Полное содержание:</strong></p>
<table border="0" width="100%">
<tr>
<td>Введение</td>
<td>7</td>
</tr>
<tr>
<td>Структура книги</td>
<td>7</td>
</tr>
<tr>
<td>Как работать с книгой</td>
<td>8</td>
</tr>
<tr>
<td>Источники информации</td>
<td>9</td>
</tr>
<tr>
<td>Благодарности</td>
<td>10</td>
</tr>
<tr>
<td>
<h3>ЧАСТЬ I. МЕТОДЫ БИБЛИОТЕКИ JQUERY</h3>
</td>
<td>11</td>
</tr>
<tr>
<td>
<h3>Глава 1. Выбор элементов</h3>
</td>
<td>13</td>
</tr>
<tr>
<td>1.1. Базовые правила</td>
<td>13</td>
</tr>
<tr>
<td>1.2. Выбор элементов с учетом иерархии</td>
<td>20</td>
</tr>
<tr>
<td>1.3. Основные фильтры</td>
<td>24</td>
</tr>
<tr>
<td>1.4. Фильтрация по содержимому</td>
<td>30</td>
</tr>
<tr>
<td>1.5. Фильтры видимых и невидимых элементов</td>
<td>34</td>
</tr>
<tr>
<td>1.6. Селекторы атрибутов</td>
<td>36</td>
</tr>
<tr>
<td>1.7. Фильтры элементов форм</td>
<td>40</td>
</tr>
<tr>
<td>1.8. Фильтры состояния элементов форм</td>
<td>43</td>
</tr>
<tr>
<td>1.9. Фильтры элементов-потомков</td>
<td>47</td>
</tr>
<tr>
<td>
<h3>Глава 2. Атрибуты элементов</h3>
</td>
<td>53</td>
</tr>
<tr>
<td>2.1. Управление атрибутами элементов</td>
<td>53</td>
</tr>
<tr>
<td>2.2. Работа с атрибутом class</td>
<td>57</td>
</tr>
<tr>
<td>2.3. Работа с HTML и текстом</td>
<td>59</td>
</tr>
<tr>
<td>2.4. Работа с атрибутом value</td>
<td>61</td>
</tr>
<tr>
<td>
<h3>Глава 3. Визуальные эффекты</h3>
</td>
<td>67</td>
</tr>
<tr>
<td>3.1. Как показывать и скрывать элементы</td>
<td>67</td>
</tr>
<tr>
<td>3.2. Эффекты &laquo;скольжения&raquo; и &laquo;затухания&raquo;</td>
<td>70</td>
</tr>
<tr>
<td>3.3. Создание анимации</td>
<td>74</td>
</tr>
<tr>
<td>3.4. Эффекты UI jQuery</td>
<td>79</td>
</tr>
<tr>
<td>
<h3>Глава 4. Работа с CSS-свойствами</h3>
</td>
<td>84</td>
</tr>
<tr>
<td>4.1. Как получать и устанавливать значения CSS-свойств элементов</td>
<td>84</td>
</tr>
<tr>
<td>4.2. Ширина и высота элементов</td>
<td>88</td>
</tr>
<tr>
<td>4.3. Позиционирование элементов</td>
<td>90</td>
</tr>
<tr>
<td>
<h3>Глава 5. Работа с данными в jQuery</h3>
</td>
<td>94</td>
</tr>
<tr>
<td>5.1. Сохранение и извлечение данных</td>
<td>94</td>
</tr>
<tr>
<td>
<h3>Глава 6. Манипуляции над элементами</h3>
</td>
<td>98</td>
</tr>
<tr>
<td>6.1. Изменение содержимого элементов</td>
<td>98</td>
</tr>
<tr>
<td>6.2. Как вставлять элементы в DOM</td>
<td>101</td>
</tr>
<tr>
<td>6.3. Замена, удаление и копирование элементов</td>
<td>111</td>
</tr>
<tr>
<td>
<h3>Глава 7. Перемещение по элементам</h3>
</td>
<td>118</td>
</tr>
<tr>
<td>7.1. Поиск нужных элементов в DOM</td>
<td>118</td>
</tr>
<tr>
<td>7.2. Фильтрация элементов набора</td>
<td>128</td>
</tr>
<tr>
<td>7.3. Прочие методы</td>
<td>138</td>
</tr>
<tr>
<td>
<h3>Глава 8. События и их обработка</h3>
</td>
<td>144</td>
</tr>
<tr>
<td>8.1. События документа</td>
<td>144</td>
</tr>
<tr>
<td>8.2. Назначение, удаление и вызов событий</td>
<td>146</td>
</tr>
<tr>
<td>8.3. События мыши, клавиатуры, браузера и форм</td>
<td>156</td>
</tr>
<tr>
<td>
<h3>Глава 9. Взаимодействие jQuery и AJAX</h3>
</td>
<td>165</td>
</tr>
<tr>
<td>9.1. Сокращенные методы</td>
<td>165</td>
</tr>
<tr>
<td>9.2. Вспомогательные функции $.ajax() и $ajaxSetup()</td>
<td>178</td>
</tr>
<tr>
<td>9.3. События AJAX</td>
<td>187</td>
</tr>
<tr>
<td>9.4. Полезные вспомогательные функции</td>
<td>192</td>
</tr>
<tr>
<td>
<h3>Глава 10. Полезные вспомогательные функции и методы jQuery</h3>
</td>
<td>195</td>
</tr>
<tr>
<td>10.1. Некоторые операции с массивами и объектами в jQuery</td>
<td>195</td>
</tr>
<tr>
<td>10.2. Некоторые операции с наборами элементов в jQuery</td>
<td>208</td>
</tr>
<tr>
<td>10.3. Другие полезные вспомогательные функции</td>
<td>214</td>
</tr>
<tr>
<td>
<h3>ЧАСТЬ II. РАСШИРЕНИЯ ДЛЯ БИБЛИОТЕКИ JQUERY</h3>
</td>
<td>219</td>
</tr>
<tr>
<td>
<h3>Глава 11. Меню для веб-сайта</h3>
</td>
<td>221</td>
</tr>
<tr>
<td>11.1. Плагин jQuery Superfish</td>
<td>221</td>
</tr>
<tr>
<td>
<h3>Глава 12. Работа с таблицами</h3>
</td>
<td>230</td>
</tr>
<tr>
<td>12.1. Плагин jqGrid</td>
<td>230</td>
</tr>
<tr>
<td>
<h3>Глава 13. Графики и диаграммы</h3>
</td>
<td>250</td>
</tr>
<tr>
<td>13.1. Плагин jqPlot</td>
<td>250</td>
</tr>
<tr>
<td>
<h3>Глава 14. AJAX-формы</h3>
</td>
<td>262</td>
</tr>
<tr>
<td>14.1. Плагин jQuery Form</td>
<td>262</td>
</tr>
<tr>
<td>14.2. Плагин jQuery Validate</td>
<td>267</td>
</tr>
<tr>
<td>14.3. Плагин jQuery Uploadify</td>
<td>273</td>
</tr>
<tr>
<td>
<h3>Глава 15. Фотогалерея для сайта</h3>
</td>
<td>282</td>
</tr>
<tr>
<td>15.1. Фотогалерея FancyBox</td>
<td>282</td>
</tr>
<tr>
<td>
<h3>Глава 16. Несколько полезных плагинов</h3>
</td>
<td>297</td>
</tr>
<tr>
<td>16.1. jQuery Cookie</td>
<td>297</td>
</tr>
<tr>
<td>16.2. jQuery Timers</td>
<td>299</td>
</tr>
<tr>
<td>16.3. jQuery Cluetip</td>
<td>302</td>
</tr>
<tr>
<td>
<h3>Глава 17. UI jQuery — виджеты</h3>
</td>
<td>308</td>
</tr>
<tr>
<td>17.1. Виджет Accordion</td>
<td>308</td>
</tr>
<tr>
<td>17.2. Виджет Datepicker</td>
<td>318</td>
</tr>
<tr>
<td>17.3. Виджет Dialog</td>
<td>329</td>
</tr>
<tr>
<td>17.4. Виджет Progressbar</td>
<td>337</td>
</tr>
<tr>
<td>17.5. Виджет Slider</td>
<td>340</td>
</tr>
<tr>
<td>17.6. Виджет Tabs</td>
<td>346</td>
</tr>
<tr>
<td>17.7. Виджет Button</td>
<td>356</td>
</tr>
<tr>
<td>17.8. Виджет Autocomplete</td>
<td>361</td>
</tr>
<tr>
<td>
<h3>Глава 18. UI jQuery — взаимодействие с элементами страницы</h3>
</td>
<td>370</td>
</tr>
<tr>
<td>18.1. Draggable — перемещение элементов</td>
<td>370</td>
</tr>
<tr>
<td>18.2. Droppable — &laquo;сброс&raquo; элементов</td>
<td>379</td>
</tr>
<tr>
<td>18.3. Resizable — изменение размеров элементов</td>
<td>386</td>
</tr>
<tr>
<td>18.4. Selectable — выбор элементов</td>
<td>392</td>
</tr>
<tr>
<td>18.5. Sortable — сортировка элементов</td>
<td>399</td>
</tr>
<tr>
<td>Приложение. Описание компакт-диска</td>
<td>409</td>
</tr>
<tr>
<td>Литература</td>
<td>411</td>
</tr>
<tr>
<td>Предметный указатель</td>
<td>412</td>
</tr>
</table>
<p>&nbsp;</p>
<p>&nbsp;</p>
]]></content:encoded>
			<wfw:commentRss>http://www.linkexchanger.su/2011/842.html/feed</wfw:commentRss>
		<slash:comments>18</slash:comments>
		</item>
		<item>
		<title>Что такое Web Storage?</title>
		<link>http://www.linkexchanger.su/2011/729.html</link>
		<comments>http://www.linkexchanger.su/2011/729.html#comments</comments>
		<pubDate>Tue, 03 May 2011 09:02:30 +0000</pubDate>
		<dc:creator>Gennady</dc:creator>
				<category><![CDATA[HTML5]]></category>
		<category><![CDATA[Web Storage]]></category>

		<guid isPermaLink="false">http://www.linkexchanger.su/?p=729</guid>
		<description><![CDATA[

Web Storage наверное, одна из самых простых для понимания спецификаций HTML5. Статьи про эту технологию конечно уже существуют, но я, следуя духу блога, попробую объяснить, что такое Web Storage, очень простым языком, с примерами и подробными комментариями к ним.
Сразу оговорюсь, что библиотеку jQuery я использую в примерах скорее по привычке&#8230;

Обычно Web Storage рассматривают как развитие [...]]]></description>
			<content:encoded><![CDATA[<p>
<img style="float:left; margin:0 5px 3px 0;" title="html5" src="http://www.linkexchanger.su/wp-content/uploads/2011/05/html5.png" alt="HTML5" /><br />
Web Storage наверное, одна из самых простых для понимания спецификаций HTML5. Статьи про эту технологию конечно уже существуют, но я, следуя духу блога, попробую объяснить, <strong>что такое Web Storage</strong>, очень простым языком, с примерами и подробными комментариями к ним.<br />
Сразу оговорюсь, что библиотеку jQuery я использую в примерах скорее по привычке&#8230;</p>
<p><span id="more-729"></span><br />
Обычно <strong>Web Storage</strong> рассматривают как развитие технологии <em>cookie</em>*. Действительно, при всей универсальности (поддерживаются производителями браузеров с незапамятных времен), файлам <em>cookie</em> присущи серьезные (с современной точки зрения) недостатки:<br />
- ограниченный, и очень маленький размер файлов. Обычно не более 4 Кбайт;<br />
- передача от браузера к серверу и обратно при каждом запросе;<br />
Это только основные недостатки, углубляться в остальные не будем, чтобы не отвлекаться от темы.</p>
<blockquote><p>Куки (от англ. cookie — печенье) — небольшой фрагмент данных, созданный веб-сервером или веб-страницей и хранимый на компьютере пользователя в виде файла, который веб-клиент (обычно веб-браузер) каждый раз пересылает веб-серверу в HTTP-запросе при попытке открыть страницу соответствующего сайта. Применяется для сохранения данных на стороне пользователя, на практике обычно используется для:<br />
- аутентификации пользователя;<br />
- хранения персональных предпочтений и настроек пользователя;<br />
- отслеживания состояния сессии доступа пользователя;<br />
- ведения статистики о пользователях.</p></blockquote>
<p><strong>Что получаем от использования Web Storage?</strong><br />
Во-первых, общий размер сохраняемых данных до 5 Мбайт (по крайней мере это рекомендации производителям браузеров);<br />
Во-вторых, данные обоих хранилищ (локальное хранилище и хранилище сеанса) не гоняются от клиента к серверу и обратно;<br />
В-третьих, в настоящее время Web Storage уже <a href="http://ru.wikipedia.org/wiki/%D0%A1%D1%80%D0%B0%D0%B2%D0%BD%D0%B5%D0%BD%D0%B8%D0%B5_%D0%B1%D1%80%D0%B0%D1%83%D0%B7%D0%B5%D1%80%D0%BE%D0%B2_%28HTML5%29#.D0.A1.D0.B2.D1.8F.D0.B7.D0.B0.D0.BD.D0.BD.D1.8B.D0.B5_.D1.81.D0.BF.D0.B5.D1.86.D0.B8.D1.84.D0.B8.D0.BA.D0.B0.D1.86.D0.B8.D0.B8" target="_blank">поддерживается</a> последними версиями основных браузеров (к сожалению, отличия в реализации все равно существуют);</p>
<p>Большой минус &#8211; число поклонников древнего программного обеспечения среди пользователей Интернета снижается не слишком быстро и значит разработчик должен взвалить на свои хрупкие плечи обязанность проверить, поддерживает ли браузер эту технологию.</p>
<p>Проверка сводится к проверке существования объектов хранилища сеансов <em>window.sessionStorage</em> и локального хранилища <em>window.localStorage</em>.</p>
<blockquote><p>Вы можете открыть файл <a href="http://www.linkexchanger.su/examples_2011/webstorage/example-a.html" target="_blank">example-a.html</a>, чтобы увидеть пример в действии. Попробуйте открыть пример в разных браузерах.</p></blockquote>
<p>Выполняем проверку <em>window.sessionStorage</em></p>
<pre class="brush: jscript;">if(window.sessionStorage)
{
  alert('Браузер поддерживает хранилище сеанса');
} else {
  alert('Браузер не поддерживает хранилище сеанса');
}</pre>
<p>Аналогичным образом можно выполнить и проверку существования <em>window.localStorage</em>.</p>
<p>Если мы убедились, что используемый браузер поддерживает эту технологию, то настало время &laquo;живьем&raquo; посмотреть на этот самый Web Storage и узнать, чем отличается локальное хранилище от хранилища сеанса.</p>
<p>Возьмем для примера браузер Opera. Если нажать комбинацию клавиш Ctrl+Shift+I, откроется панель &laquo;Dragon Fly&raquo; &#8211; встроенное в браузер средство отладки. Можете попробовать и Google Chrome &#8211; используйте точно такое же сочетание клавиш.<br />
<img class="alignnone size-full wp-image-750" title="Opera Dragon Fly - Web Storage" src="http://www.linkexchanger.su/wp-content/uploads/2011/05/a.png" alt="Просмотр хранилищ в Opera Dragon Fly" width="410" height="253" /></p>
<p>Посмотрите &#8211; здесь доступно для просмотра, изменения и удаления содержимое обоих хранилищ.</p>
<p>Теперь о том, почему хранилищ два и чем они отличаются.</p>
<p>Если приложению требуются данные, которые продолжают существовать после того, как будет закрыта вкладка или окно браузера, тогда следует использовать <em>localStorage</em>. Здесь уместно сохранять данные, содержащие например, пользовательские настройки каких-либо интерфейсов.</p>
<p>Если же данные должны храниться в течение времени, которое необходимо для просмотра нескольких страниц (диалоговые окна, многостраничные формы) &#8211; подойдет <em>sessionStorage</em>. В этом случае данные перестанут существовать, как только пользователь закроет вкладку или окно браузера.</p>
<p>В остальном оба хранилища выглядят братьями-близнецами. По крайней мере установка, получение и удаление данных для обоих типов хранилищ не отличается ничем, кроме названия объекта &#8211; <em>sessionStorage</em> или <em>localStorage</em>.</p>
<p>Попробуем сохранить какие-либо данные в хранилище сеансов:</p>
<pre class="brush: jscript;">
window.sessionStorage.setItem('mySessionKey',
'Какие-то данные в хранилище сеанса');</pre>
<p>Метод <strong>setItem</strong> принимает два аргумента &#8211; строку с именем ключа и строку с данными, которые в дальнейшем можно будет извлечь по соответствующему им ключу. Причем сделать это очень просто:</p>
<pre class="brush: jscript;">
var d = window.sessionStorage.getItem('mySessionKey');
</pre>
<p>Передаем методу <strong>getItem</strong> в качестве аргумента имя ключа, и в переменной <strong>d</strong> получаем строку &#8216;<em>Какие-то данные в хранилище сеанса&#8217;</em>. Так же просто можно удалить ставшие ненужными данные:</p>
<pre class="brush: jscript;">
window.sessionStorage.removeItem('mySessionKey');
</pre>
<p>После этой операции, при попытке получить данные по ключу <strong>mySessionKey</strong> из хранилища сеанса, будем получать <em>null</em>.</p>
<p>Сохранять и получать значения можно и иным способом, без вызовов методов <strong>setItem</strong> и <strong>getItem</strong>. Например:</p>
<pre class="brush: jscript;">
// сохраняем
window.sessionStorage.mySessionKey2 = 'Какие-то другие данные в хранилище сеанса.';
// получаем
alert(window.sessionStorage.mySessionKey2);
</pre>
<p>Двигаемся дальше&#8230; Во-первых, используем в следующем примере локальное хранилище, а во-вторых, попробуем сохранить в нем не просто банальную строку, а объект, значениями свойств которого являются строки, числа, массивы и даже другие объекты.</p>
<blockquote><p>Вообще-то, спецификация Web Storage подразумевает сохранение данных не только строкового типа. Но, по крайней мере в текущих версиях браузеров, эти возможности ограничены. Поэтому без некоторых хитростей тут не обойтись. Вернее, пока не обойтись&#8230;</p>
<p>Вы можете открыть файл <a href="http://www.linkexchanger.su/examples_2011/webstorage/example-b.html" target="_blank">example-b.html</a>, чтобы увидеть пример в действии. Попробуйте открыть пример в разных браузерах.</p></blockquote>
<p>Поставим перед собой задачу сохранить в локальном хранилище под именем <strong>myLocalData</strong> вот такой объект:</p>
<pre class="brush: jscript;">var localData = {
  'name': 'Василий Иванович',
  'age': 47,
  'phones': ['(495) 222-33-44 begin_of_the_skype_highlighting              (495) 222-33-44      end_of_the_skype_highlighting begin_of_the_skype_highlighting              (495) 222-33-44      end_of_the_skype_highlighting', '(903) 987-55-66'],
  'profession': { 'one':'токарь', 'two':'слесарь', 'three':'сантехник' }
}</pre>
<p>Казалось бы нет ничего сложного, проходили&#8230; Но, по причине, указанной выше мы сделаем несколько иначе:</p>
<pre class="brush: jscript;">
var data = JSON.stringify(localData);
window.localStorage.setItem('myLocalData', data);
</pre>
<p>Приведем объект JSON к строке и только потом сохраним эту строку в локальном хранилище под именем <strong>myLocalData</strong>.</p>
<p>Соответственно, при извлечении нужных нам данных проделаем обратную операцию:</p>
<pre class="brush: jscript;">var local = $.parseJSON(window.localStorage.getItem('myLocalData'));
</pre>
<p>[ см. <a href="http://api.jquery.com/jQuery.parseJSON/" target="_blank">$.parseJSON(json)</a> ]<br />
после чего в переменной <strong>local</strong> будет храниться объект, к свойствам которого можно легко обратиться:</p>
<pre class="brush: jscript;">
local.name // Василий Иванович
local.phones[0] // (495) 222-33-44
local.profession.one // токарь
</pre>
<p>Мы познакомились с методами <strong>getItem</strong>, <strong>setItem</strong> и <strong>removeItem</strong> объектов <em>localStorage</em> и <em>sessionStorage</em>, но это еще не все возможности.</p>
<p>Объекты <em>localStorage</em> и <em>sessionStorage</em> имеют еще полезное свойство <strong>length</strong>, в котором как Вы уже догадались, содержится число, которое может сказать о том, сколько пар ключ/значение находится в данный момент в хранилище.</p>
<pre class="brush: jscript;">
var n = window.localStorage.length;
</pre>
<p>Вот так в переменную <strong>n</strong> мы записали количество пар ключ/значение, содержащихся в локальном хранилище.</p>
<p>Есть у обоих объектов метод <strong>clear</strong>, по названию которого тоже можно догадаться о его предназначении:</p>
<pre class="brush: jscript;">
window.localStorage.clear();
</pre>
<p>Таким образом можно удалить все содержимое хранилища (в данном случае локального).</p>
<p>Рассмотрим еще один метод, который обеспечивает извлечение заданного ключа по его индексу &#8211; метод <strong>key(index)</strong>.</p>
<pre class="brush: jscript;">
var myKey = window.localStorage.key(index);
</pre>
<p>Вот так можно получить значение ключа по его индексу. Ключи индексируются начиная с нуля, т.е. первому ключу соответствует индекс 0, а последнему length &#8211; 1. При попытке получить значение ключа по несуществующему индексу будет возвращено значение <em>undefined</em> (в Opera) или <em>null</em> (в Google Chrome).</p>
<p>Теперь немного про ошибки, которые могут возникать при сохранении данных в хранилищах. При превышении памяти хранилища попытка сохранения в нем значения приведет к возникновении ошибки <em>QUOTA_EXCEEDED_ERR</em> (некоторые браузеры могут предложить увеличить размер хранилища). К слову, такая же ошибка возникнет при отключении хранилища пользователем. Соответственно, в своих приложениях с использованием как локального хранилища, так и хранилища сеанса следует предусматривать обработку таких ошибок.</p>
<p>На &laquo;закуску&raquo; осталось самое интересное &#8211; мы рассмотрим механизм событий, реализованный в интерфейсе <strong>HTML5 Web Storage</strong>.</p>
<p>Сначала на простом примере познакомимся с объектом <em>StorageEvent</em>. Пример продемонстрирует возможность обмена данными между двумя окнами, относящимися к одному и тому же источнику(*). Естественно, что в примере будет использовано локальное хранилище, поскольку, как Вы помните, данные хранилища сеансов &laquo;живут&raquo; только в одном окне.</p>
<blockquote><p>* &#8211; ресурс адресуется с использованием одного и того же сочетания схемы, хоста и порта.</p></blockquote>
<blockquote><p>Откройте файлы примеров <a href="http://www.linkexchanger.su/examples_2011/webstorage/example-c.html" target="_blank">example-c.html</a> и <a href="http://www.linkexchanger.su/examples_2011/webstorage/example-d.html" target="_blank">example-d.html</a> в двух разных окнах одного и того же браузера. Попробуйте ввести какие-либо произвольные данные в поле ввода в любом окне и нажмите кнопку <strong>Изменить</strong>. Посмотрите, что произойдет в другом окне.</p></blockquote>
<p>Если посмотрели пример, то попробуем немного расшифровать, что же там происходит.</p>
<p>При нажатии на кнопку мы просто изменяем данные для ключа <strong>myText</strong>, находящиеся в локальном хранилище.</p>
<pre class="brush: jscript;">
$('#btn').bind('click',function(){
  window.localStorage.setItem('myText', $('input').val());
});
</pre>
<p>А вот так зарегистрируем обработчик события <strong>storage</strong>, которое будет вызываться при изменениях в хранилище, вызванных любой страницей.</p>
<pre class="brush: jscript;">
window.addEventListener('storage', function(e){
  $('#result').prepend('&lt;p&gt;Ключ (свойство key): ' + e.key +
  '&lt;br /&gt;Новое значение (свойство newValue): ' + e.newValue +
  '&lt;br /&gt;Старое значение (свойство oldValue): ' + e.oldValue +
  '&lt;br /&gt;Адрес ресурса (свойство url): ' + e.url +
  '&lt;br /&gt;Ссылка на хранилище (свойство storageArea): ' + e.storageArea.length + '&lt;/p&gt;');
}, true);</pre>
<blockquote><p>Конечно, такой обработчик не сработает в IE, но нам сейчас не до кроссбраузерности.</p></blockquote>
<p>Рассмотрим каждое из свойств:</p>
<p><strong>key</strong> &#8211; содержит значение ключа, который был обновлен/удален;<br />
<strong>url</strong> &#8211; адрес источника в котором произошло событие;<br />
<strong>newValue</strong> &#8211; содержит новое/измененное значение (если значение было удалено, то <em>null</em>);<br />
<strong>oldValue</strong> &#8211; содержит старое значение (если значение добавлено впервые, то <em>null</em>);<br />
<strong>storageArea</strong> &#8211; ссылка на хранилище;</p>
<p>В принципе ничего непонятного тут нет. Несколько подробнее можно сказать разве что о <em>storageArea</em>. Его удобно использовать, когда необходимо при изменении каких-либо данных в хранилище производить операции над другими данными из того же хранилища. В примере мы используем свойство <strong>length</strong>, чтобы узнать количество содержащихся в хранилище элементов.</p>
<p>В заключение хочу сказать, что я ни в коем случае не призываю никого сразу же внедрять новые возможности в реальные проекты. Думаю, что когда вы будете испытывать предоставленные файлы примеров, то наверняка заметите отличия (и немалые!) в реализации <strong>спецификации Web Storage</strong> в различных браузерах. Наверное, время для 100% использования новых возможностей пока не пришло. Но оно обязательно придет &#8211; и встретить его надо вооруженным знаниями.</p>
<blockquote><p>Вы можете <a href="http://www.linkexchanger.su/examples_2011/webstorage/webstorage.zip">скачать архив</a>, который включает в себя все примеры, которые рассматривались в статье.</p></blockquote>
]]></content:encoded>
			<wfw:commentRss>http://www.linkexchanger.su/2011/729.html/feed</wfw:commentRss>
		<slash:comments>17</slash:comments>
		</item>
		<item>
		<title>jQuery UI – плагин Autocomplete</title>
		<link>http://www.linkexchanger.su/2011/700.html</link>
		<comments>http://www.linkexchanger.su/2011/700.html#comments</comments>
		<pubDate>Mon, 04 Apr 2011 11:22:46 +0000</pubDate>
		<dc:creator>Gennady</dc:creator>
				<category><![CDATA[jQuery]]></category>
		<category><![CDATA[jQuery UI]]></category>
		<category><![CDATA[autocomplete]]></category>
		<category><![CDATA[UI]]></category>

		<guid isPermaLink="false">http://www.linkexchanger.su/?p=700</guid>
		<description><![CDATA[Очень давно не писал ничего в блоге и вот наконец выбрал немного времени, чтобы рассказать о виджете Autocomplete, который был включен в состав jQuery UI – надстройки над JavaScript-библиотекой jQuery.

Виджет Autocomplete помогает организовать список подходящих значений при заполнении пользователем поля ввода.
Для начала посетим страницу настраиваемой закачки на сайте jQuery UI, чтобы получить необходимые нам файлы. [...]]]></description>
			<content:encoded><![CDATA[<p>Очень давно не писал ничего в блоге и вот наконец выбрал немного времени, чтобы рассказать о виджете Autocomplete, который был включен в состав jQuery UI – надстройки над JavaScript-библиотекой jQuery.</p>
<p><img src="http://www.linkexchanger.su/wp-content/uploads/2011/04/autocomplete.jpg" alt="UI jQuery - виджет Autocomplete" /></p>
<p>Виджет Autocomplete помогает организовать список подходящих значений при заполнении пользователем поля ввода.</p>
<p><span id="more-700"></span>Для начала посетим страницу <a href="http://jqueryui.com/download">настраиваемой закачки</a> на сайте jQuery UI, чтобы получить необходимые нам файлы. Щелкаем на ссылке Deselect all component, чтобы не закачивать лишнее, а затем выбираем только то, что нам потребуется – отмечаем чекбокс Autocomplete и видим, что вместе с ним отметились чекбоксы Core, Widget и Position. Работа виджета Autocomplete зависит от этих файлов.</p>
<p>Кроме этого, справа есть выпадающий список, где можно выбрать понравившуюся тему оформления. Если все готово, кликаем кнопку Download и получаем архив. Возможности виджета можно оценить в <a href="http://jqueryui.com/demos/autocomplete/">демо-галерее</a> на сайте разработчика. Я лишь постараюсь по-русски и как можно более простым языком объяснить, как заставить все это работать.</p>
<p>Итак, сначала в разделе HEAD потребуется подключить несколько файлов, которые есть в архиве.</p>
<pre class="brush: xml;">&lt;link type=&quot;text/css&quot; href=&quot;css/ui-lightness/jquery-ui-1.8.9.custom.css&quot; rel=&quot;stylesheet&quot; /&gt;
&lt;script src=&quot;js/jquery-1.4.4.min.js&quot; type=&quot;text/javascript&quot;&gt;&lt;/script&gt;
&lt;script src=&quot;js/jquery-ui-1.8.9.custom.min.js&quot; type=&quot;text/javascript&quot;&gt;&lt;/script&gt;</pre>
<p>Сначала мы подключили файл стилевого оформления виджета, затем файл библиотеки jQuery. Третий подключенный файл необходим, чтобы реализовать Autocomplete.</p>
<p>Виджет Autocomplete не требует сложной HTML-разметки. Достаточно всего лишь поля для ввода текста, т.е. обычного элемента input, который имеет значение text в атрибуте type. Дополнительную разметку можно добавить, чтобы использовать стили в соответствии с выбранной темой оформления.</p>
<pre class="brush: xml;">&lt;div class=&quot;ui-widget&quot;&gt;
	&lt;label for=&quot;tags&quot;&gt;Tags: &lt;/label&gt;
	&lt;input id=&quot;tags&quot; type=&quot;text&quot; /&gt;
&lt;/div&gt;</pre>
<p>Для начала разберем самый простой случай использования виджета &#8211; когда для формирования списка подсказок используются локальные данные.</p>
<pre class="brush: jscript;">&lt;script type=&quot;text/javascript&quot;&gt;
$(function(){
  var availableTags = [&quot;ActionScript&quot;,&quot;AppleScript&quot;,&quot;Asp&quot;,&quot;BASIC&quot;,
  &quot;C&quot;,&quot;C++&quot;,&quot;Clojure&quot;,&quot;COBOL&quot;,&quot;ColdFusion&quot;,&quot;Erlang&quot;,&quot;Fortran&quot;,
  &quot;Groovy&quot;,&quot;Haskell&quot;,&quot;Java&quot;,&quot;JavaScript&quot;,&quot;Lisp&quot;,&quot;Perl&quot;,&quot;PHP&quot;,
  &quot;Python&quot;,&quot;Ruby&quot;,&quot;Scala&quot;,&quot;Scheme&quot;];

  $(&quot;#tags&quot;).autocomplete({
    source: availableTags
  });
});
&lt;/script&gt;</pre>
<p>В переменной <strong>availableTags</strong> приготовили массив с подсказками. Затем выбрали элемент <strong>input</strong> по его идентификатору и применили метод <strong>autocomplete</strong>, которому сразу передали объект с настройками. Пока этот объект с единственным свойством <strong>source</strong>, которое определяет источник данных. В нашем случае указан массив <strong>availableTags</strong>.</p>
<p>На самом деле мы можем немного поднастроить виджет, используя другие свойства объекта настроек. Вот список этих свойств:</p>
<p><strong>source</strong> &#8211; опция не имеет значения по умолчанию и должна быть обязательно определена. Значением опции может являться строка, массив или функция. В любом случае в этой опции должен быть указан источник данных.<br />
<strong>minLength</strong> &#8211; значение по умолчанию 1. В этой опции указывается количество символов, которое должно быть введено в поле ввода прежде, чем активизируются подсказки. Значение 0 полезно при использовании локальных данных при списках из нескольких позиций. Это значение должно быть увеличено при использовании запросов к серверу для получения данных и при использовании больших списков, где одному введенному символу может соответствовать несколько тысяч наименований.<br />
<strong>delay</strong> &#8211; значение по умолчанию 300. В этой опции указывается количество миллисекунд, которое должно пройти после нажатия очередной клавиши, чтобы активизировался запрос на получение данных. Нулевое значение имеет смысл при использовании локальных данных. При использовании запросов к серверу нулевое значение в этой опции может породить серьезную нагрузку<br />
<strong>appendTo</strong> &#8211; значение по умолчанию ‘body’. В качестве значения этой опции может быть использован селектор jQuery. Определяет, к какому элементу должен быть добавлен выпадающий список подсказок.<br />
<strong>disabled</strong> &#8211; значение по умолчанию false. Если установить значение true, то при инициализации функциональность виджета Autocomplete будет недоступна, однако может быть включена впоследствии, например, при выполнении какого-либо условия.</p>
<p>Дальше посмотрим, как виджет может реагировать на события.</p>
<pre class="brush: jscript;">&lt;script type=&quot;text/javascript&quot;&gt;
$(function(){
  var availableTags = [&quot;ActionScript&quot;,&quot;AppleScript&quot;,&quot;Asp&quot;,&quot;BASIC&quot;,
  &quot;C&quot;,&quot;C++&quot;,&quot;Clojure&quot;,&quot;COBOL&quot;,&quot;ColdFusion&quot;,&quot;Erlang&quot;,&quot;Fortran&quot;,
  &quot;Groovy&quot;,&quot;Haskell&quot;,&quot;Java&quot;,&quot;JavaScript&quot;,&quot;Lisp&quot;,&quot;Perl&quot;,&quot;PHP&quot;,
  &quot;Python&quot;,&quot;Ruby&quot;,&quot;Scala&quot;,&quot;Scheme&quot;];
  $(&quot;#tags&quot;).autocomplete({
    source: availableTags,
    select: function(event, ui) {
      alert('Событие: ' + event.type +
            ',\nзначение: ' + ui.item.value);
    }
  });
});
&lt;/script&gt;</pre>
<p>Здесь мы добавили свойство <strong>select</strong>, где определили функцию, которая будет вызываться в тот момент, когда пользователь выберет какое-либо значение из списка подсказок. функция принимает два аргумента: первый — объект <strong>event</strong>, второй — специальный объект <strong>ui</strong>. Организовав доступ к свойствам этих объектов, можно получить полезную информацию (мы выводим ее в окне предупреждения).</p>
<p>Это не единственное событие на которое умеет реагировать виджет &#8211; вот полный их список, где указано название свойства в объекте с настройками, название события в event.type и описание события:</p>
<p><strong>create</strong> &#8211; Событие <em>autocompletecreate</em> наступает в момент инициализации.<br />
<strong>search</strong> &#8211; событие <em>autocompletesearch</em> наступает перед выполнением запроса. Если функция, определенная в этой опции вернет false, запрос не будет отправлен.<br />
<strong>open</strong> &#8211; событие	<em>autocompleteopen</em> наступает в момент, когда открывается выпадающий список подсказок.<br />
<strong>focus</strong> &#8211; событие <em>autocompletefocus</em> наступает всякий раз, когда один из пунктов списка подсказок получает фокус.<br />
<strong>select</strong> &#8211; событие <em>autocompleteselect</em> наступает, когда выбран один из пунктов списка подсказок.<br />
<strong>close</strong> &#8211; событие <em>autocompleteclose</em> наступает, когда список подсказок закры-вается. Событие наступает независимо от того, был выбран один из пунктов или нет.<br />
<strong>change</strong> &#8211; событие <em>autocompletechange</em> наступает после того, как выбран один из пунктов списка. Событие всегда наступает после close.</p>
<p>Есть у виджета и некоторые методы, с помощью которых можно еще более расширить его функциональность.</p>
<p>Для примера добавьте в HTML-разметку пару кнопок:</p>
<pre class="brush: xml;">&lt;button id=&quot;search&quot;&gt;Искать &quot;as&quot;&lt;/button&gt;
&lt;button id=&quot;close&quot;&gt;Закрыть&lt;/button&gt;
&lt;div class=&quot;ui-widget&quot;&gt;
	&lt;label for=&quot;tags&quot;&gt;Tags: &lt;/label&gt;
	&lt;input id=&quot;tags&quot; type=&quot;text&quot; /&gt;
&lt;/div&gt;</pre>
<p>И вот такой JS-код:</p>
<pre class="brush: jscript;">&lt;script type=&quot;text/javascript&quot;&gt;
$(function(){
  var availableTags = [&quot;ActionScript&quot;,&quot;AppleScript&quot;,&quot;Asp&quot;,&quot;BASIC&quot;,
  &quot;C&quot;,&quot;C++&quot;,&quot;Clojure&quot;,&quot;COBOL&quot;,&quot;ColdFusion&quot;,&quot;Erlang&quot;,&quot;Fortran&quot;,
  &quot;Groovy&quot;,&quot;Haskell&quot;,&quot;Java&quot;,&quot;JavaScript&quot;,&quot;Lisp&quot;,&quot;Perl&quot;,&quot;PHP&quot;,
  &quot;Python&quot;,&quot;Ruby&quot;,&quot;Scala&quot;,&quot;Scheme&quot;];
  $(&quot;#tags&quot;).autocomplete({
    source: availableTags,
    minLength: 0
  });
  $(&quot;#search&quot;).click(function(){
    $(&quot;#tags&quot;).autocomplete(&quot;search&quot;, &quot;as&quot;);
  });
  $(&quot;#close&quot;).click(function(){
    $(&quot;#tags&quot;).autocomplete(&quot;close&quot;);
  });
});
&lt;/script&gt;</pre>
<p>Теперь, щелкая по кнопке <strong>Искать &laquo;as&raquo;</strong> мы можем имитировать действия пользователя, как если бы он ввел символы <strong>&laquo;as&raquo;</strong> в элементе <strong>input</strong>. Щелкнув по кнопке <strong>Закрыть</strong> &#8211; закрываем список подсказок, если он был открыт ранее.</p>
<p>Ниже приведены описания всех доступных методов:</p>
<p><strong>destroy</strong> &#8211; .autocomplete( &#8216;destroy&#8217; ) полностью удаляет всю функциональность виджета Autocomplete. Возвращает элементы в состояние, предшествующее инициализации.<br />
<strong>disable</strong> &#8211; .autocomplete(&#8216;disable&#8217;) временно запрещает использование всей функциональности виджета. Вновь разрешить ее можно с помощью метода enable.<br />
<strong>enable</strong> &#8211; .autocomplete(&#8216;enable&#8217;) разрешает использование всей функциональности виджета, если ранее она была запрещена методом disable.<br />
<strong>option</strong> &#8211; .autocomplete(&#8216;option&#8217;, optionName, [value]) с помощью этого метода можно установить значение любой опции виджета после инициализации.<br />
<strong>option</strong>  &#8211; .autocomplete(&#8216;option&#8217;, optionName) с помощью этого метода можно получить значение любой опции виджета после инициализации.<br />
<strong>widget</strong> &#8211; .autocomplete(&#8216;widget&#8217;) с помощью этого метода можно получить доступ к объекту, который представляет собой элемент с функциональностью Autocomplete.<br />
<strong>search</strong> &#8211; .autocomplete(&#8217;search, [value]&#8216;) с помощью этого метода без передачи второго па-раметра можно открыть весь список. Если передать во втором параметре строку символов, то будет открыт список с подходящими подсказками.<br />
<strong>close</strong> &#8211; .autocomplete(&#8216;close&#8217;) закрывает список подсказок, если он был открыт ранее.</p>
<p>В общем теперь мы знаем как настраивать виджет и управлять им, но пока мы использовали исключительно локальные данные для формирования списка подсказок. Для изучения возможностей &#8211; подойдет, но для реальной работы &#8211; вряд ли. В реальной работе наверняка потребуется формировать подсказки из данных, хранящихся на своем сервере, а может быть и вообще не на своем <img src='http://www.linkexchanger.su/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<p>Опция <strong>source</strong>, которая, напомню, является обязательной и определяет источник данных, может принимать также строку, где содержится url, к которому следует отправлять запрос. А еще в <strong>source</strong> можно определить свою функцию, которая будет делать, то что надо именно Вам. Вот этот, пожалуй самый гибкий способ мы и разберем. Попробуем получить в виде списка подсказок какие-либо данные с сервера <strong>geonames.org</strong></p>
<p>Я приведу полностью рабочий код и попробую разъяснить, как и что работает.</p>
<pre class="brush: xml;">&lt;!DOCTYPE html PUBLIC &quot;-//W3C//DTD XHTML 1.0 Strict//EN&quot; &quot;http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd&quot;&gt;
&lt;html xmlns=&quot;http://www.w3.org/1999/xhtml&quot; lang=&quot;ru&quot; xml:lang=&quot;ru&quot;&gt;
&lt;head&gt;
&lt;title&gt;example-17-8-4&lt;/title&gt;
&lt;meta http-equiv=&quot;Content-Type&quot; content=&quot;text/html; charset=utf-8&quot; /&gt;
&lt;link type=&quot;text/css&quot; href=&quot;css/ui-lightness/jquery-ui-1.8.9.custom.css&quot; rel=&quot;stylesheet&quot; /&gt;
&lt;script src=&quot;js/jquery-1.4.4.min.js&quot; type=&quot;text/javascript&quot;&gt;&lt;/script&gt;
&lt;script src=&quot;js/jquery-ui-1.8.9.custom.min.js&quot; type=&quot;text/javascript&quot;&gt;&lt;/script&gt;
&lt;style type=&quot;text/css&quot;&gt;
.ui-autocomplete-loading {
  background: #FFF url('css/ui-lightness/images/ui-anim_basic_16x16.gif') right center no-repeat;
}
#city { width: 25em; }
#log { height: 200px; width: 600px; overflow: auto; }
&lt;/style&gt;
&lt;script type=&quot;text/javascript&quot;&gt;
$(function() {
  $(&quot;#city&quot;).autocomplete({
    source: function(request,response) {
      $.ajax({
        url: &quot;http://ws.geonames.org/searchJSON&quot;,
        dataType: &quot;jsonp&quot;,
        data: {
          featureClass: &quot;P&quot;,
          style: &quot;full&quot;,
          maxRows: 12,
          name_startsWith: request.term
        },
        success: function(data) {
          response($.map(data.geonames, function(item) {
            return {
              label: item.name + &quot;, &quot; + item.countryName,
              value: item.name + &quot; (&quot; + item.countryName + &quot;)&quot; + &quot; [&quot; + item.lat + &quot;, &quot; + item.lng + &quot;]&quot;
            }
          }));
        }
      });
    },
    minLength: 3,
    select: function(event,ui) {
      $(&quot;&lt;p/&gt;&quot;).text(ui.item ? ui.item.value : &quot;Ничего не выбрано!&quot;).prependTo(&quot;#log&quot;);
      $(&quot;#log&quot;).attr(&quot;scrollTop&quot;, 0);
    }
  });
});
&lt;/script&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;div class=&quot;ui-widget&quot;&gt;
  &lt;label for=&quot;city&quot;&gt;Город: &lt;/label&gt;&lt;input id=&quot;city&quot; /&gt;&lt;br /&gt;
  &lt;span style=&quot;font-size:.8em;&quot;&gt;Поддерживается &lt;a href=&quot;http://geonames.org&quot;&gt;geonames.org&lt;/a&gt;&lt;/span&gt;
&lt;/div&gt;
&lt;div id=&quot;log&quot; class=&quot;ui-widget ui-widget-content&quot;&gt;&lt;/div&gt;
&lt;/body&gt;
&lt;/html&gt;</pre>
<p>Сначала смотрим на HTML-разметку. В первом элементе <strong>div</strong> нас интересует только элемент <strong>input</strong> с идентификатором <strong>city</strong>. Сюда будем вводить начальные буквы (на латинице) населенного пункта, информацию о котором мы хотели бы получить. Элемент <strong>div</strong> с идентификатором <strong>log</strong> используем для занесения в него полученной информации.</p>
<p>Если смотреть JavaScript-код, то увидим, что объект с настройками содержит три свойства — обязательное свойство <strong>source</strong>, а также свойства <strong>minLength</strong> и <strong>select</strong>.<br />
Мы займемся рассмотрением только свойства <strong>source</strong>, где можно написать свою функцию. Эта функция принимает два аргумента. Первый аргумент — <strong>request</strong> — объект, содержащий единственное свойство <strong>term</strong>, в котором хранится строка, введенная пользователем в поле ввода. Второй аргумент — <strong>response</strong> — функция, с помощью которой будет обрабатываться полученный ответ.</p>
<p>Внутри функции, определенной в свойстве <strong>source</strong> мы имеем практически неограниченную свободу действий. А поэтому, не мудрствуя, пишем там ajax-запрос к <strong>url</strong> http://ws.geonames.org/searchJSON, в опции <strong>dataType</strong> указываем, что в ответе ожидаем получить данные в формате JSON. В опции <strong>data</strong> определяем объект с параметрами запроса, который будет отправляться на указанный <strong>url</strong> (<em>почему параметры именно такие &#8211; надо смотреть документацию по API на сервере geonames.org</em>). В последнем параметре передаем <strong>request.term</strong> &#8211; то, что ввел пользователь.</p>
<p>В следующей опции ajax-запроса &#8211; опции <strong>success</strong>, вызываем функцию обработки ответа <strong>response</strong>. В аргументе, который мы передаем этой функции, мы можем обрабатывать данные, полученные в ответе сервера так, как нам будет угодно. Мы используем метод <strong>$.map</strong> чтобы применить некоторую функцию к каждому элементу объекта, переданному в первом аргументе. Внутри функции мы можем обращаться к свойствам объекта — <strong>item.countryName</strong>, <strong>item.lng</strong>, <strong>item.lat</strong> (<em>почему свойства именно такие &#8211; см. документацию по API, которую предоставляет веб-сервис</em>). Функция, которую мы написали, для каждого элемента возвращает объект, содержащий два свойствами, которые мы определили самостоятельно, используя полученные данные. Из получившегося массива таких объектов и строится список подсказок.</p>
<p>В общем, вот так как-то&#8230; В заключение, большая человеческая просьба &#8211; в комментариях пожалуйста, можете похвалить или поругать в принципе. Если же Вы хотите задать технический вопрос, то задавайте его на <a href="http://www.linkexchanger.su/forum/">форуме jQuery</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.linkexchanger.su/2011/700.html/feed</wfw:commentRss>
		<slash:comments>26</slash:comments>
		</item>
		<item>
		<title>jQuery 1.5 Released</title>
		<link>http://www.linkexchanger.su/2011/685.html</link>
		<comments>http://www.linkexchanger.su/2011/685.html#comments</comments>
		<pubDate>Sun, 06 Feb 2011 17:32:21 +0000</pubDate>
		<dc:creator>Андрей</dc:creator>
				<category><![CDATA[jQuery]]></category>

		<guid isPermaLink="false">http://www.linkexchanger.su/?p=685</guid>
		<description><![CDATA[В соответствии с графиком в конце января состоялся релиз очередной версии библиотеки jQuery 1.5.
В новой версии исправлено 83 бага и закрыто 460 тикетов. Дополнена документация jQuery API.
Полностью был переработан модуль AJAX. Наиболее заметные изменения коснулись метода jQuery.ajax (jQuery.get, jQuery.post и пр.), сейчас эти методы возвращают объект jqXHR, который предоставляет доступ к объекту XMLHttpRequest.
Также модуль AJAX [...]]]></description>
			<content:encoded><![CDATA[<p>В соответствии с графиком в конце января состоялся релиз очередной версии библиотеки jQuery 1.5.<span id="more-685"></span></p>
<p>В новой версии исправлено 83 бага и закрыто 460 тикетов. Дополнена документация jQuery API.</p>
<p>Полностью был переработан модуль AJAX. Наиболее заметные изменения коснулись метода jQuery.ajax (jQuery.get, jQuery.post и пр.), сейчас эти методы возвращают объект jqXHR, который предоставляет доступ к объекту XMLHttpRequest.</p>
<p>Также модуль AJAX расширен за счёт возможностей назначать упорядоченные обработчики, фильтры и транспорты данных. Эти новшества должны предоставить больше возможностей для написания новых ajax плагинов.</p>
<div style="margin-left: 20px">
<h2>Extending Ajax &#8211; расширение Ajax</h2>
<p>В jQuery 1.5 $.ajax() предоставляет три способа расширения функциональности отправки, получения и управления ajax-запросами:<br />
<strong>Prefilters</strong><br />
обработка события перед отправкой запроса.<br />
<strong>Converters</strong><br />
обработка событий, которые возникают в ответ на определенный тип полученных данных, отличающийся от ожидаемого.<br />
<strong>Transports</strong><br />
новое в 1.5, они используются при проблемах с ajax-запросами и в крайнем случае.</p>
</div>
<p>Ещё одним нововведением стали deferred objects &#8211; это инструемнт, который позволяет создавать очередь задач, которые будут выполнены позже. Таким образом становится возможно работать со значениями, которые не могут быть получены немедленно (например, при асинхронных запросах ajax), и назначать несколько обработчиков, которые будут вызваны при получении таких &laquo;отложенных&raquo; значений.</p>
<div style="margin-left: 20px">
<h2>Deferred Objects</h2>
<p>Методы создания deferred объекта: <strong>$.Deferred()</strong> &#8211; создаёт новый  deferred объект, <strong>$.when( deferreds )</strong> &#8211; принимает в качестве аргументов deferred объекты и создаёт новый, который будет выполнен после всех переданных аргументами или отменен, как только будет отменен хотя бы один из переданных аргументов.</p>
<p>Методы добавления обработчиков в очередь к deferred объекту: <strong>deferred.done( doneCallbacks )</strong> &#8211; добавить обработчик успешного выполнения, <strong>deferred.fail( failCallbacks )</strong> &#8211; добавить обработчик ошибочного завершения, <strong>deferred.then( doneCallbacks, failCallbacks )</strong> &#8211; добавить обработчики обоих типов. В эти методы аргументом можно передавать как одну функцию, так и массив из нескольких функций.</p>
<p>Методы вызова обработчиков из очереди deferred объекта: <strong>deferred.resolve( args )</strong>, <strong>deferred.resolve( context, args )</strong> &#8211; вызвать обработчики успешного выполнения, <strong>deferred.reject( args )</strong>, <strong>deferred.rejectWith( context, args )</strong> &#8211; вызвать обработчики ошибочного завершения, где <strong>args</strong> &#8211; это аргументы, передаваемые в обработчик, а <strong>context</strong> &#8211; это контекст вызова обработчиков (доступен через this внутри функции обработчика)</p>
</div>
<p>Теперь jQuery предоставляет новую функциональность &#8211; <strong>jQuery.sub()</strong>, благодаря которой можно создавать и изменять дубликат jQuery, поддерживающий весь его API. Например, можно это использовать для переопределения исходных методов jQuery без влияния на на других пользователей, вызывающих эти же методы, или создавать скрытое API для плагинов без коллизий пересечения имён объектов.</p>
<p>В этом релизе была увеличена скорость работы методов обхода DOM: .children(), .prev(), .next()</p>
<p>В заключении, команда jQuery сделала некоторые изменения во внутренней системе сборки библиотеки. Проделана работа в сторону стандартизации всех процессов сборки на серверной среде NodeJS, которая сильно впечатлила команду jQuery. Разработчики особенно ценят то, что им удалось снизить зависимость от устаревшей системы Java/Rhino и перейти на многообещающую JavaScript-среду. Дополнительно к этому также осуществлен переход к использованию UglifyJS взамен Google Closure Compiler, всвязи с чем заметны некоторые улучшения в плане размера файла библиотеки.</p>
<p>Ссылка на официальную новость: <a href="http://blog.jquery.com/2011/01/31/jquery-15-released/">JQUERY 1.5 RELEASED</a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.linkexchanger.su/2011/685.html/feed</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>jQuery Templates plugin: Template Tags</title>
		<link>http://www.linkexchanger.su/2010/644.html</link>
		<comments>http://www.linkexchanger.su/2010/644.html#comments</comments>
		<pubDate>Tue, 09 Nov 2010 12:22:25 +0000</pubDate>
		<dc:creator>Андрей</dc:creator>
				<category><![CDATA[jQuery]]></category>
		<category><![CDATA[jQuery документация]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[jQuery Plugins]]></category>

		<guid isPermaLink="false">http://www.linkexchanger.su/?p=644</guid>
		<description><![CDATA[В прошлой статье я рассказал основы использования плагина jQuery Templates plugin и обещал описать возможности синтаксиса шаблонов, чем сегодня и займусь. В этой статье помимо описания инструкций шаблонов будет много примеров, а в конце есть ссылка на демонстрационную страницу с &#171;живыми&#187; примерами &#8211; там их можно посмотреть, поредактировать, поэкспериментировать, попробовать собственные примеры.
Синтаксис шаблонов довольно функционален. [...]]]></description>
			<content:encoded><![CDATA[<p>В <a href="/2010/619.html">прошлой статье</a> я рассказал основы использования плагина <strong>jQuery Templates plugin</strong> и обещал описать возможности синтаксиса шаблонов, чем сегодня и займусь. В этой статье помимо описания инструкций шаблонов будет много примеров, а <a href="#demoLinks">в конце</a> есть ссылка на демонстрационную страницу с &laquo;живыми&raquo; примерами &#8211; там их можно посмотреть, поредактировать, поэкспериментировать, попробовать собственные примеры.</p>
<p>Синтаксис шаблонов довольно функционален. В теле шаблона разработчик может обращаться к полям объекта, к полям полей объекта, вызывать методы объекта и его полей, обращаться к внешним объектам JavaScript и jQuery, просто писать JS-выражения. Кроме всего этого внутри шаблона можно итеративно выводить коллекции, организовать условный вывод, использовать вложенные шаблоны. Но давайте обо всём по порядку.</p>
<p><span id="more-644"></span></p>
<h2>Примеры в статье</h2>
<p>Все примеры я буду приводить ключевыми кусками, чтобы не загромождать материал повторяющимися частями кода. В примере будут присутствовать <strong>текст шаблона</strong>, <strong>JSON данных</strong>, при необходимости <strong>JSON опций</strong> (параметр <strong><em>options</em></strong><em> </em>метода <strong><em>.tmpl( ) </em></strong>) и <strong>результат</strong>. В общем случае полный код примера будет выглядеть так:</p>
<pre class="brush: xml;">
&lt;script type=&quot;text/x-jquery-tmpl&quot;&gt;
    &lt;li&gt; ЭТО ТЕКСТ ШАБЛОНА &lt;/li&gt;
&lt;/script&gt;
&lt;script type=&quot;text/javascript&quot;&gt;
var data = [
    // ЭТО МАССИВ ДАННЫХ
];
var options = {
    // ЭТО ОПЦИИ
};
$(function() {
    // ПРИМЕНЕНИЕ ШАБЛОНА И ВЫВОД РЕЗУЛЬТАТА:
    $('script[type=&quot;text/x-jquery-tmpl&quot;]').tmpl(data, options).appendTo($('.output').empty());
});
&lt;/script&gt;
&lt;ul class=&quot;output&quot;&gt;&lt;/ul&gt;
</pre>
<p>Так будет полностью выглядеть приведенный ниже <strong>пример 1.</strong>:</p>
<pre class="brush: xml;">
&lt;script type=&quot;text/x-jquery-tmpl&quot;&gt;
    &lt;li&gt;${Name} ({{html Desc}}); вычисления: ${p1 + p2 + 10}&lt;/li&gt;
&lt;/script&gt;
&lt;script type=&quot;text/javascript&quot;&gt;
var data = [
    { Name: &quot;obj1&quot;, Desc: &quot;&lt;em&gt;Object one&lt;/em&gt;&quot;, p1: 1, p2: 10 },
    { Name: &quot;&lt;em&gt;obj2&lt;/em&gt;&quot;, Desc: &quot;&lt;strong&gt;Object two&lt;/strong&gt;&quot;, p1: 3.3, p2: 5 }
];
var options = {
};
$(function() {
    $('script[type=&quot;text/x-jquery-tmpl&quot;]').tmpl(data, options).appendTo($('.output').empty());
});
&lt;/script&gt;
&lt;ul class=&quot;output&quot;&gt;&lt;/ul&gt;
</pre>
<h2>Вставка данных в шаблон ${}, {{= }}, {{html }}</h2>
<p>С помощью этих инструкции можно вставить в шаблон значение поля объекта данных, результат работы метода или результат вычисления некоторого js-выражения, внутри инструкции можно обращаться к глобальным объектам JS, а также к служебным объектам, созданным при обработке шаблона.</p>
<p>Прежде чем перейти к примерам, хочу немного акцентировать внимание на сходстве и разнице этих инструкций. Все три инструкции предназначены для вывода результата в эту точку шаблона. <strong>${<em>expression</em>}</strong> и <strong>{{= <em>expression</em>}}</strong> эквивалентны (первая инструкция &#8211; это сокращённая форма второй), результат вставляется в шаблон в виде текста, если в тексте встречаются символы разметки (&lt;  &gt;  &amp; и пр.), то они будут заменены на соответствующие спецсимволы (&amp;lt;  &amp;gt;  &amp;amp; и пр.). Для вставки же разметки с сохранением html-кода нужно использовать инструкцию <strong>{{html <em>expression</em>}}</strong>.</p>
<h3>Пример 1. Вывод данных с разметкой и без</h3>
<p>Шаблон:</p>
<pre class="brush: xml;">&lt;li&gt;${Name} ({{html Desc}}); вычисления: ${p1 + p2 + 10}&lt;/li&gt;</pre>
<p>Данные:</p>
<pre class="brush: jscript;">[
    { Name: &quot;obj1&quot;, Desc: &quot;&lt;em&gt;Object one&lt;/em&gt;&quot;, p1: 1, p2: 10 },
    { Name: &quot;&lt;em&gt;obj2&lt;/em&gt;&quot;, Desc: &quot;&lt;strong&gt;Object two&lt;/strong&gt;&quot;, p1: 3.3, p2: 5 }
]</pre>
<p>Результат:</p>
<pre class="brush: xml;">&lt;ul class=&quot;output&quot;&gt;
    &lt;li&gt;obj1 (&lt;em&gt;Object one&lt;/em&gt;); вычисления: 21&lt;/li&gt;
    &lt;li&gt;&amp;lt;em&amp;gt;obj2&amp;lt;/em&amp;gt; (&lt;strong&gt;Object two&lt;/strong&gt;); вычисления: 18.3&lt;/li&gt;
&lt;/ul&gt;</pre>
<h2>Доступ к объектам и внутренние параметры шаблона</h2>
<p>Как я уже упоминал, внутри выражения можно обращаться к глобальным объектам и функциям JS, при этом шаблонизатор сам пытается определить, является указанное имя ссылкой на глобальный объект или на часть переданных данных. И конечно же, локальные данные имеют более высокий приоритет, чем глобальные. Давайте определим в документе глобальные объект testObj и функцию testFunc и попробуем их использовать:</p>
<pre class="brush: jscript;">var testObj = {
    Value: 1
};

function testFunc(i) {
    return i % 2 == 1;
}
</pre>
<h3>Пример 2. Использование глобальных объектов</h3>
<p>Шаблон:</p>
<pre class="brush: xml;">&lt;li&gt;${Name}; testObj.Value = ${testObj.Value}; testFunc(${p}) =&gt; ${testFunc(p)}&lt;/li&gt;</pre>
<p>Данные:</p>
<pre class="brush: jscript;">[
    { Name: &quot;obj1&quot;, p: 3, testObj: { Value: 20 } },
    { Name: &quot;obj2&quot;, p: 13, testFunc: function(i) { return i + 7; } }
]</pre>
<p>Результат:</p>
<pre class="brush: xml;">&lt;ul class=&quot;output&quot;&gt;
    &lt;li&gt;obj1; testObj.Value = 20; testFunc(3) =&amp;gt; true&lt;/li&gt;
    &lt;li&gt;obj2; testObj.Value = 1; testFunc(13) =&amp;gt; 20&lt;/li&gt;
&lt;/ul&gt;</pre>
<p>Внутри шаблона есть ещё доступ  к специальным объектам &#8211; внутренним параметрам шаблона: <strong>$item</strong> и <strong>$data</strong>.</p>
<p><strong>$item</strong> &#8211; представляет доступ к обрабатываемой в данный момент единице данных для вставки в шаблон, внутри этого объекта собраны и параметры, переданные пользователем, и дополнительные параметры из аргумента <em><strong>options</strong></em> функции <em><strong>.tmpl( )</strong></em>. Доступ к параметрам из данных осуществляется через вложенный объект <strong>.</strong><strong>data</strong>, а к параметрам аргумента <em><strong>options</strong></em> &#8211; напрямую, т.е. если вызвать <em><strong>$(&#8216;#myTmpl&#8217;).tmpl({ p1: 1 }, { p2: 2 })</strong></em>, то в шаблоне получить доступ к ним можно так: <em><strong>$item.data.p1</strong></em> и <em><strong>$item.p2</strong></em>.</p>
<p><strong>$data</strong> &#8211; это сокращённая версия доступа к данным, по сути это просто синоним для <em><strong>$item.data</strong></em>, в свете этого доступ в шаблоне к p1 и p2 из вызова в абзаце выше может быть таким: <em><strong>$data.p1</strong></em> и <em><strong>$item.p2</strong></em>.</p>
<h3>Пример 3. Параметр options</h3>
<p>Шаблон:</p>
<pre class="brush: xml;">&lt;li&gt;${Name}; p * 100 = ${$item.Method($data.p)}&lt;/li&gt;</pre>
<p>Данные:</p>
<pre class="brush: jscript;">[
    { Name: &quot;obj1&quot;, p: 3.3 },
    { Name: &quot;obj2&quot;, p: 0.13 }
]</pre>
<p>Опции:</p>
<pre class="brush: jscript;">{ Method: function(i) { return i * 100; } }</pre>
<p>Результат:</p>
<pre class="brush: xml;">&lt;ul class=&quot;output&quot;&gt;
    &lt;li&gt;obj1; p * 100 = 330&lt;/li&gt;
    &lt;li&gt;obj2; p * 100 = 13&lt;/li&gt;
&lt;/ul&gt;</pre>
<h2>Условная разметка в шаблоне {{if }} {{else}} {{/if}}</h2>
<p>Внутри шаблона можно отслеживать некие условия и в зависимости от них выводить тот или иной кусок. Для достижения этих целей применяются следующие комбинации инструкций:</p>
<p><strong>{{if <em>expression</em>}} tmplPart {{/if}}</strong> &#8211; простой вывод части шаблона, только если выражение истинно,<br />
<strong>{{if <em>expression</em>}} tmplPart1 {{else}} tmplPart2 {{/if}}</strong> &#8211; вывод части 1 или 2 в зависимости от истинности выражения,<br />
<strong>{{if <em>expr1</em>}} tmplPart1 {{else <em>expr2</em>}} tmplPart2 {{else}} tmplPart3</strong><strong> {{/if}}</strong> &#8211; это уже построение цепочки последовательных проверок (if .. else if .. else if .. else ..), понятное дело, что последний else без выражения необязателен. Думаю, тут всё понятно, давайте посмотрим простенький пример:</p>
<h3>Пример 4. Условный вывод</h3>
<p>Шаблон:</p>
<pre class="brush: xml;">&lt;li&gt;
{{if (
    (typeof(useFirstLastName) != &quot;undefined&quot; ? useFirstLastName : $item.defaultUseFirstLastName) &amp;&amp;
    $data.FirstName &amp;&amp; $data.LastName)}} ${FirstName + &quot; &quot; + LastName}
{{else $data.FullName}} ${FullName}
{{else $data.Name}} ${Name}
{{else}} ${$item.NoName}
{{/if}}
&lt;/li&gt;</pre>
<p>Данные:</p>
<pre class="brush: jscript;">[
    { Name: &quot;obj1&quot;, FullName: &quot;Object 1&quot;, FirstName: &quot;Object&quot;, LastName: &quot;One&quot; },
    { FirstName: &quot;Object&quot;, LastName: &quot;Two&quot;, Name: &quot;obj2&quot;, useFirstLastName: true },
    { Title: &quot;object number 3&quot; }
]</pre>
<p>Опции:</p>
<pre class="brush: jscript;">{ NoName: &quot;[noname]&quot;, defaultUseFirstLastName: false }</pre>
<p>Результат:</p>
<pre class="brush: xml;">&lt;ul class=&quot;output&quot;&gt;
    &lt;li&gt;  Object 1  &lt;/li&gt;
    &lt;li&gt;  Object Two  &lt;/li&gt;
    &lt;li&gt;  [noname]  &lt;/li&gt;
&lt;/ul&gt;</pre>
<p>Поясню, что происходит в шаблоне. Под первым условием мы проверяем, нужно ли выводить имя объекта в виде конкатенации FirstName и LastName &#8211; при этом проверяется условие внутри объекта, если оно не определено, то условие в опциях, и требуется наличие обоих свойств. Обращаясь в условии к этим свойствам через параметр <em><strong>$data</strong></em>, гарантируем их наличие именно в переданных данных, а глобальные объекты при этом игнорируются. Во втором и третьем условиях проверяем последовательно свойства FullName и Name &#8211; выводим первое попавшееся, если нет ни одного из них, то выводим &laquo;заглушку&raquo; из опций.</p>
<h2>Итеративный обход с помощью {{each }}</h2>
<p>Думаю всем знакома функция jQuery <em><strong>.each(func)</strong></em> (а есть ещё и <em><strong>$.map(array, callback)</strong></em> ) и надеюсь многим знакома конструкция JS <em><strong>for (var key in obj)</strong></em> &#8211; эти выражения совершают итеративный обход элементов коллекции. В шаблонах тоже возможен вывод коллекций поэлементно и описывается он следующей инструкцией: <strong>{{each( <em>index</em>, <em>value </em>) <em>data</em>}} <em>tmplText </em>{{/each}}</strong>. <strong>index</strong> и <strong>value</strong> &#8211; это имена параметров (указываются без кавычек) для доступа соответственно к текущему индексу и значению в коллекции. Есть и сокращённая форма этой инструкции: <strong>{{each <em>data</em>}} <em>tmplText</em> {{/each}}</strong> &#8211; при этом в тексте шаблона к текущим индексу и значению можно обратиться по именам по умолчанию: <strong>$index</strong> и <strong>$value</strong>. Выражение <strong>data</strong> должно возвращать в результате массив или объект, объект будет обработан как словарь (коллекция пар имя-значение свойств объекта). Довольно слов, посмотрим на примере:</p>
<h3>Пример 5. Вывод массива</h3>
<p>Шаблон:</p>
<pre class="brush: xml;">&lt;li&gt;
    &lt;table border=&quot;1&quot; cellpadding=&quot;5&quot; cellspacing=&quot;0&quot;&gt;
        &lt;tr&gt;&lt;th align=&quot;left&quot;&gt;n&lt;/th&gt;&lt;th align=&quot;left&quot;&gt;${Expr}&lt;/th&gt;&lt;/tr&gt;
        {{each NumCollection}}
        &lt;tr&gt;&lt;td&gt;${$index}&lt;/td&gt;&lt;td&gt;${$value}&lt;/td&gt;&lt;/tr&gt;
        {{/each}}
        &lt;tr&gt;&lt;td&gt;${$item.EtcName}&lt;/td&gt;&lt;td&gt;${$item.EtcVal}&lt;/td&gt;&lt;/tr&gt;
    &lt;/table&gt;
&lt;/li&gt;</pre>
<p>Данные:</p>
<pre class="brush: jscript;">[
    { Expr: &quot;2^n&quot;, NumCollection: [ 1, 2, 4, 8, 16 ] },
    { Expr: &quot;2n-1&quot;, NumCollection: [ 1, 3, 5, 7, 9 ] },
]</pre>
<p>Опции:</p>
<pre class="brush: jscript;">{ EtcName: &quot;etc.&quot;, EtcVal: &quot;...&quot; }</pre>
<p>Результат:</p>
<pre class="brush: xml;">&lt;ul class=&quot;output&quot;&gt;
&lt;li&gt;
     &lt;table border=&quot;1&quot; cellpadding=&quot;5&quot; cellspacing=&quot;0&quot;&gt;
         &lt;tbody&gt;
             &lt;tr&gt;&lt;th align=&quot;left&quot;&gt;n&lt;/th&gt;&lt;th align=&quot;left&quot;&gt;2^n&lt;/th&gt;&lt;/tr&gt;
             &lt;tr&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;/tr&gt;
             &lt;tr&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;2&lt;/td&gt;&lt;/tr&gt;
             &lt;tr&gt;&lt;td&gt;2&lt;/td&gt;&lt;td&gt;4&lt;/td&gt;&lt;/tr&gt;
             &lt;tr&gt;&lt;td&gt;3&lt;/td&gt;&lt;td&gt;8&lt;/td&gt;&lt;/tr&gt;
             &lt;tr&gt;&lt;td&gt;4&lt;/td&gt;&lt;td&gt;16&lt;/td&gt;&lt;/tr&gt;
             &lt;tr&gt;&lt;td&gt;etc.&lt;/td&gt;&lt;td&gt;...&lt;/td&gt;&lt;/tr&gt;
         &lt;/tbody&gt;
    &lt;/table&gt;
&lt;/li&gt;&lt;li&gt;
     &lt;table border=&quot;1&quot; cellpadding=&quot;5&quot; cellspacing=&quot;0&quot;&gt;
         &lt;tbody&gt;
             &lt;tr&gt;&lt;th align=&quot;left&quot;&gt;n&lt;/th&gt;&lt;th align=&quot;left&quot;&gt;2n-1&lt;/th&gt;&lt;/tr&gt;
             &lt;tr&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;/tr&gt;
             &lt;tr&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;3&lt;/td&gt;&lt;/tr&gt;
             &lt;tr&gt;&lt;td&gt;2&lt;/td&gt;&lt;td&gt;5&lt;/td&gt;&lt;/tr&gt;
             &lt;tr&gt;&lt;td&gt;3&lt;/td&gt;&lt;td&gt;7&lt;/td&gt;&lt;/tr&gt;
             &lt;tr&gt;&lt;td&gt;4&lt;/td&gt;&lt;td&gt;9&lt;/td&gt;&lt;/tr&gt;
             &lt;tr&gt;&lt;td&gt;etc.&lt;/td&gt;&lt;td&gt;...&lt;/td&gt;&lt;/tr&gt;
        &lt;/tbody&gt;
    &lt;/table&gt;
&lt;/li&gt;
&lt;/ul&gt;</pre>
<h3>Пример 6. Вывод объекта-словаря</h3>
<p>Шаблон:</p>
<pre class="brush: xml;">&lt;li&gt;
JSON = { {{each(key, val) $data}}{{if $item.f}},{{/if}} &quot;${key}&quot;: &quot;${val}&quot;{{if $item.f=true}}{{/if}}{{/each}} }
&lt;/li&gt;</pre>
<p>Данные:</p>
<pre class="brush: jscript;">[
    { Name: &quot;obj1&quot;, FullName: &quot;Object 1&quot;, FirstName: &quot;Object&quot;, LastName: &quot;One&quot; },
    { FirstName: &quot;Object&quot;, LastName: &quot;Two&quot;, Name: &quot;obj2&quot; },
    { Title: &quot;object number 3&quot; }
]</pre>
<p>Опции:</p>
<pre class="brush: jscript;">{ f: false }</pre>
<p>Результат:</p>
<pre class="brush: xml;">&lt;ul class=&quot;output&quot;&gt;
    &lt;li&gt; JSON = {  &quot;Name&quot;: &quot;obj1&quot;, &quot;FullName&quot;: &quot;Object 1&quot;, &quot;FirstName&quot;: &quot;Object&quot;, &quot;LastName&quot;: &quot;One&quot; } &lt;/li&gt;
    &lt;li&gt; JSON = {  &quot;FirstName&quot;: &quot;Object&quot;, &quot;LastName&quot;: &quot;Two&quot;, &quot;Name&quot;: &quot;obj2&quot; } &lt;/li&gt;
    &lt;li&gt; JSON = {  &quot;Title&quot;: &quot;object number 3&quot; } &lt;/li&gt;
&lt;/ul&gt;</pre>
<h2>Вывод шаблона в шаблоне {{tmpl }}</h2>
<p>Внутри шаблона можно использовать другие шаблоны, которые могут быть получены извне (селектором, функцией) или в виде разметки сгенерированной на месте (например, когда сами данные определяют в каком виде их надо вставить в шаблон).</p>
<p>Полный вид инструкции: <strong>{{tmpl(<em>data</em>, <em>options</em>) <em>template</em>}}</strong>.</p>
<p><em><span style="color: #ff0000">(На момент написания статьи в оф.документации показана сигнатура инструкции <span style="text-decoration: underline">{{tmpl( [data], [options] ) template}} content {{/tmpl}}</span> &#8211; это <strong>ОШИБКА</strong>, у этой инструкции нет закрывающего тега <span style="text-decoration: underline">{{/tmpl}}</span> и, соответственно, никакого содержания <span style="text-decoration: underline">content</span>)</span></em></p>
<p>Параметры <strong>data</strong> и <strong>options</strong> &#8211; являются необязательными и имеют то же назначение, что и в функции <em><strong>.tmpl(data, options)</strong></em>, если параметры опущены, то в качестве данных используется текущий объект данных <em><strong>$data</strong></em>, а <em><strong>options</strong></em> &#8211; пустой объект.<br />
Параметр <strong>template</strong> может принимать следующие значения: HTML-разметка, HTML- или jQuery-объект, строка, которая является именем скомпилированного шаблона или скомпилированный шаблон, также можно просто передать строку-селектор для jQuery (это недокументированная возможность, но она фигурирует в примерах).</p>
<h3>Пример 7. Использование сгенерированного шаблона</h3>
<p>Шаблон:</p>
<pre class="brush: xml;">&lt;li&gt; {{tmpl $item.lt+tag+$item.gt+$item.tp+&quot;{content}&quot;+$item.lt+&quot;/&quot;+tag+$item.gt}} &lt;/li&gt;</pre>
<p>Данные:</p>
<pre class="brush: jscript;">[
    { tag: &quot;strong&quot;, content: &quot;&lt;em&gt;Hello&lt;/em&gt;, &quot; },
    { tag: &quot;em&quot;, content: &quot;World!&quot; }
]</pre>
<p>Опции:</p>
<pre class="brush: jscript;">{ lt: &quot;&lt;&quot;, gt: &quot;&gt;&quot;, tp: &quot;$&quot; }</pre>
<p>Результат:</p>
<pre class="brush: xml;">&lt;ul class=&quot;output&quot;&gt;
    &lt;li&gt; &lt;strong&gt;&amp;lt;em&amp;gt;Hello&amp;lt;/em&amp;gt;, &lt;/strong&gt; &lt;/li&gt;
    &lt;li&gt; &lt;em&gt;World!&lt;/em&gt; &lt;/li&gt;
&lt;/ul&gt;</pre>
<p>В шаблоне этого примера при генерировании вложенного шаблона используются строковые значение &laquo;&lt;&raquo;, &laquo;&gt;&raquo;, &laquo;$&raquo; в виде переменных, это сделано для того, чтобы они не воспринимались как разметка и инструкции внешним шаблоном.</p>
<h3>Пример 8. Использование шаблона внутри шаблона</h3>
<p>Шаблон:</p>
<pre class="brush: xml;">&lt;li&gt;${Name} {{if Items}}&lt;ul&gt;{{tmpl(Items) '#tmplSelector'}}&lt;/ul&gt;{{/if}}&lt;/li&gt;</pre>
<p>Тут строку &#8216;#tmplSelector&#8217; &#8211; следует заменить на корректный селектор (или функцию) получения текущего шаблона. Для моих &laquo;живых&raquo; примеров можно использовать такой шаблон:</p>
<pre class="brush: xml;">&lt;li&gt;${Name} {{if Items}}&lt;ul&gt;{{tmpl(Items) $('#example8 script[type=&quot;text/x-jquery-tmpl&quot;]')}}&lt;/ul&gt;{{/if}}&lt;/li&gt;</pre>
<p>, в нём будет применен начальный шаблон, или</p>
<pre class="brush: xml;">&lt;li&gt;${Name} {{if Items}}&lt;ul&gt;{{tmpl(Items) $('#example8')[0].DemoData.Tmpl}}&lt;/ul&gt;{{/if}}&lt;/li&gt;</pre>
<p>, в нём будет применён отредактированный шаблон &#8211; полностью &laquo;живой&raquo; пример.</p>
<p>Данные:</p>
<pre class="brush: jscript;">[
    { Name: &quot;obj1&quot;, Items: [ { Name: &quot;obj11&quot;, Items: [ { Name: &quot;obj111&quot; }, { Name: &quot;obj112&quot; } ] }, { Name: &quot;obj12&quot; } ] },
    { Name: &quot;obj2&quot; }
]</pre>
<p>Опции:</p>
<pre class="brush: jscript;">{ }</pre>
<p>Результат:</p>
<pre class="brush: xml;">&lt;ul class=&quot;output&quot;&gt;
    &lt;li&gt;obj1 &lt;ul&gt;
        &lt;li&gt;obj11 &lt;ul&gt;
            &lt;li&gt;obj111 &lt;/li&gt;
            &lt;li&gt;obj112 &lt;/li&gt;
        &lt;/ul&gt;&lt;/li&gt;
        &lt;li&gt;obj12 &lt;/li&gt;
    &lt;/ul&gt;&lt;/li&gt;
    &lt;li&gt;obj2 &lt;/li&gt;
&lt;/ul&gt;</pre>
<p>В этом примере определён рекурсивный шаблон.</p>
<h2>Шаблон в шаблоне с HTML-содержанием внутри {{wrap }}</h2>
<p><strong>{{wrap (<em>data</em>, <em>options</em>) <em>template</em>}} <em>HTMLcontent </em>{{/wrap}}</strong> &#8211; эта инструкция очень похожа на инструкцию <em><strong>{{tmpl }}</strong></em>, её параметры <em><strong>data</strong></em>, <em><strong>options</strong></em> и <em><strong>template</strong></em> имеют то же назначение. Отличие инструкции <em><strong>{{wrap }}</strong></em> от <em><strong>{{tmpl }}</strong></em> в том, что она может включать HTML-содержание и затем это содержание можно использовать в применяемом шаблоне.</p>
<p>В шаблоне, который передаётся параметром <em><strong>template</strong></em>, можно достать HTML-разметку, заключённую между <em><strong>{{wrap }}</strong></em> и <em><strong>{{/wrap}}</strong></em> с помощью метода <em><strong>$item.html(filter, onlyText)</strong></em>. Первым аргументом можно наложить фильтр в виде селектора jQuery на выбираемые из разметки элементы, а вторым аргументом можно указать, что нужно в результате получить только текст верхнего выбранного элемента.</p>
<h3>Пример 9. Использование {{wrap }}</h3>
<pre class="brush: xml;">&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
  &lt;style&gt;
table { border-collapse:collapse; width:380px; background-color:#f8f8f8; border:2px solid blue; margin:5px; } table td { border:1px solid blue; padding:3px; }
&lt;/style&gt;
  &lt;script src=&quot;http://code.jquery.com/jquery-latest.min.js&quot;&gt;&lt;/script&gt;
  &lt;script src=&quot;http://nje.github.com/jquery-tmpl/jquery.tmpl.js&quot;&gt;&lt;/script&gt;
&lt;/head&gt;
&lt;body&gt;

&lt;script id=&quot;myTmpl&quot; type=&quot;text/x-jquery-tmpl&quot;&gt;
    The following wraps and reorders some HTML content:
    {{wrap &quot;#tableWrapper&quot;}}
        &lt;h3&gt;One&lt;/h3&gt;
        &lt;div&gt;
            First &lt;b&gt;content&lt;/b&gt;
        &lt;/div&gt;
        &lt;h3&gt;Two&lt;/h3&gt;
        &lt;div&gt;
            And &lt;em&gt;more&lt;/em&gt; &lt;b&gt;content&lt;/b&gt;...
        &lt;/div&gt;
    {{/wrap}}

    And this wraps different HTML content:
    {{wrap &quot;#tableWrapper&quot;}}
        &lt;div&gt;
            First &lt;b&gt;div&lt;/b&gt;
        &lt;/div&gt;
        &lt;div&gt;
            Second &lt;b&gt;div&lt;/b&gt;
        &lt;/div&gt;
        &lt;div&gt;
            Third &lt;b&gt;div&lt;/b&gt;
        &lt;/div&gt;
        &lt;h3&gt;first h3&lt;/h3&gt;
        &lt;h3&gt;second h3&lt;/h3&gt;
        &lt;h3&gt;third h3&lt;/h3&gt;
    {{/wrap}}
&lt;/script&gt;

&lt;script id=&quot;tableWrapper&quot; type=&quot;text/x-jquery-tmpl&quot;&gt;
    &lt;table&gt;&lt;tbody&gt;
        &lt;tr&gt;
            {{each $item.html(&quot;h3&quot;, true)}}
                &lt;td&gt;
                    ${$value}
                &lt;/td&gt;
            {{/each}}
        &lt;/tr&gt;
        &lt;tr&gt;
            {{each $item.html(&quot;div&quot;)}}
                &lt;td&gt;
                    {{html $value}}
                &lt;/td&gt;
            {{/each}}
        &lt;/tr&gt;
    &lt;/tbody&gt;&lt;/table&gt;
&lt;/script&gt;

&lt;div id=&quot;myWrappedView&quot;&gt;&lt;/div&gt;

&lt;script&gt;
$( &quot;#myTmpl&quot; ).tmpl()
    .appendTo( &quot;#myWrappedView&quot; );
&lt;/script&gt;

&lt;/body&gt;
&lt;/html&gt;</pre>
<p>Этот пример взят со страницы оф. документации по этой инструкции шаблона. В &laquo;живых&raquo; примерах приведена переработанная под их специфику версия.</p>
<h2>Комментарии в шаблонах {{! }}</h2>
<p>В шаблонах можно вставлять комментарии разработчика, которые не будут попадать в результат, делается это очень просто, а именно так:</p>
<pre class="brush: xml;">{{! это текст комментария}}</pre>
<h2><a name="demoLinks"></a>&laquo;Живые&raquo; примеры из статьи</h2>
<p>По этому адресу страница доступна online: <a href="http://zalab.net/projects/jquery/demo/tmpl/" target="_blank">http://zalab.net/projects/jquery/demo/tmpl/</a><br />
А по этой ссылке можно загрузить эту страничку в архиве: <a href="http://www.linkexchanger.su/wp-content/uploads/2010/11/jq.tmpl_.live_.examples.zip">jq.tmpl.live.examples.zip (33 KB)</a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.linkexchanger.su/2010/644.html/feed</wfw:commentRss>
		<slash:comments>14</slash:comments>
		</item>
		<item>
		<title>Официальные плагины jQuery: Templates plugin</title>
		<link>http://www.linkexchanger.su/2010/619.html</link>
		<comments>http://www.linkexchanger.su/2010/619.html#comments</comments>
		<pubDate>Mon, 01 Nov 2010 13:13:03 +0000</pubDate>
		<dc:creator>Андрей</dc:creator>
				<category><![CDATA[jQuery]]></category>
		<category><![CDATA[jQuery документация]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[json]]></category>
		<category><![CDATA[jQuery Plugins]]></category>

		<guid isPermaLink="false">http://www.linkexchanger.su/?p=619</guid>
		<description><![CDATA[4 октября 2010 года была опубликована запись в официальном блоге jQuery об анонсе трёх плагинов jQuery, созданных при поддержке команды Microsoft. Эти плагины &#8211; плагин шаблонов (the jQuery Templates plugin), плагин привязки данных (the jQuery Data Link plugin) и плагин глобализации (the jQuery Globalization plugin) &#8211; получили статус “Официальные плагины проекта jQuery” (officially supported plugins [...]]]></description>
			<content:encoded><![CDATA[<p>4 октября 2010 года была опубликована <a href="http://blog.jquery.com/2010/10/04/new-official-jquery-plugins-provide-templating-data-linking-and-globalization/">запись в официальном блоге jQuery</a> об анонсе трёх плагинов jQuery, созданных при поддержке команды Microsoft. Эти плагины &#8211; <strong>плагин шаблонов (the jQuery Templates plugin)</strong>, <strong>плагин привязки данных (the jQuery Data Link plugin)</strong> и <strong>плагин глобализации (the jQuery Globalization plugin)</strong> &#8211; получили статус “Официальные плагины проекта jQuery” (officially supported plugins of the jQuery project).</p>
<p>Плагин шаблонов используется при необходимости вывести объект данных или массив объектов в разметку страницы. Плагин привязки данных нужен для связи объектов с элементами страницы и синхронного изменения значений. Плагин глобализации позволяет выводить такие данные, как числа, дата и время, сумма денег и т.п., на страницу в соответствии с форматом текущего языка.</p>
<p>Следует отметить, что команда Microsoft использовала свои немалые наработки в этих направлениях, как и команда проекта jQuery, и при хорошо сложившемся взаимодействии получились, на мой взгляд, отличные инструменты для разработчиков. В подтверждение моего мнения могу добавить, что разработчики jQuery анонсировали включение плагинов шаблонов и привязки данных в ядро библиотеки jQuery уже версии 1.5, а плагина глобализации &#8211; в соответствующую версию jQuery UI. Плагины пока ещё не получили статус релиза, но уже активно ведется написание документации на сайте <a href="http://api.jquery.com/">http://api.jquery.com</a>. К слову, команда Microsoft последовала традициям разработки jQuery и разместила материалы по разработке плагинов на github.com, где доступны описание и исходники плагинов.</p>
<p>В этой статье я расскажу немного о плагине шаблонов.<br />
<span id="more-619"></span></p>
<h2>jQuery Templates plugin</h2>
<p>Начнём с простенького примера:</p>
<pre class="brush: xml;">&lt;script id=&quot;movieTemplate&quot; type=&quot;text/x-jquery-tmpl&quot;&gt;
  &lt;li&gt;&lt;b&gt;${Name}&lt;/b&gt; (${ReleaseYear})&lt;/li&gt;
&lt;/script&gt;

&lt;script type=&quot;text/javascript&quot;&gt;
var movies = [
  { Name: &quot;The Red Violin&quot;, ReleaseYear: &quot;1998&quot; },
  { Name: &quot;Eyes Wide Shut&quot;, ReleaseYear: &quot;1999&quot; },
  { Name: &quot;The Inheritance&quot;, ReleaseYear: &quot;1976&quot; }
];

$( &quot;#movieTemplate&quot; ).tmpl( movies )
    .appendTo( &quot;#movieList&quot; );
&lt;/script&gt;

&lt;ul id=&quot;movieList&quot;&gt;&lt;/ul&gt;</pre>
<p>Итак, в примере разработчик страницы описал шаблон для вывода объектов в виде разметки (первый элемент <strong><em>script</em></strong>), потом получил откуда-то массив объектов <strong><em>movies </em></strong>и вызвал инструкцию сгенерировать нужную разметку по шаблону, взяв данные из предоставленного массива объектов, а результат добавить в конец списка <strong><em>#movieList</em></strong>.<br />
В результате работы плагина мы получим такую разметку:</p>
<pre class="brush: xml;">&lt;ul id=&quot;movieList&quot;&gt;
  &lt;li&gt;&lt;b&gt;The Red Violin&lt;/b&gt; (1998)&lt;/li&gt;
  &lt;li&gt;&lt;b&gt;Eyes Wide Shut&lt;/b&gt; (1999)&lt;/li&gt;
  &lt;li&gt;&lt;b&gt;The Inheritance&lt;/b&gt; (1976)&lt;/li&gt;
&lt;/ul&gt;</pre>
<p>А теперь по сути вопроса.<br />
<strong> Что делает плагин?</strong><br />
Плагин получает на вход строку шаблона и множество объектов (или один объект), которые нужно вывести в строку (или разметку) с форматированием.<br />
<strong> Где это применяется?</strong><br />
В основном этот плагин полезен при динамическом выводе объектов JS на страницу, объекты могут быть получены самыми разными способами, например, при разчётах или по результатам каких-то действий пользователя и, конечно самый часто приводимый пример, в виде JSON в ответе сервера на AJAX запрос.</p>
<h3>Методы плагина</h3>
<p><strong><span style="text-decoration: underline">.tmpl( [ data ], [ options ] )</span></strong><br />
Получает содержимое первого выбранного элемента и использует его в качестве шаблона для форматированного вывода указанных данных.<br />
<strong><em> data </em></strong> &#8211; данные для вывода в шаблон (объект или массив объектов).<br />
<strong><em> options </em></strong> &#8211; опционально, определённое пользователем расширение в виде пар ключ-значение для объекта вывода в шаблон.<br />
<strong><span style="text-decoration: underline"> jQuery.tmpl(template, [ data ], [ options ])</span></strong><br />
Использует указанный шаблон для форматированного вывода указанных данных.<br />
<strong><em> template </em></strong> &#8211; шаблон для форматирования данных, может быть одного из следующих типов: строка с разметкой, HTML-элемент (в том числе и в jQuery-обёртке), строка, соответствующая имени ранее скомпилированного шаблона.<br />
<strong><em> data, options</em></strong> &#8211; имеют то же значение, что и выше<br />
<strong><span style="text-decoration: underline"> .tmplItem()</span></strong><br />
Возвращает для первого выбранного элемента структуру (объект) с результатами работы шаблонизатора. Возвращаемый методом объект обеспечивает доступ к:</p>
<ul>
<li> HTML-частям, из которых состоит шаблон</li>
<li> ассоциированной единице переданных данных</li>
<li> родительскому шаблону, если текущий шаблон является вложенным</li>
<li> текущему шаблону, использованному для вывода</li>
<li> определённому пользователем расширению (полям и методам), переданным в параметр options метода tmpl()</li>
</ul>
<p>Этот метод используется, напрмер, когда после проведенного форматирования данных требуется узнать, а какие же данные были использованы при формировании некоторого куска разметки, или для обновления куска разметки с использованием новых данных.<br />
<strong><span style="text-decoration: underline"> jQuery.tmplItem( element )</span></strong><br />
Аналогичен методу .tmplItem, только структура с результатами работы шаблонизатора ищется для элемента <strong><em>element </em></strong> (HTML-элемент, в том числе и в jQuery-обёртке).<br />
<strong><span style="text-decoration: underline"> .template( [ name ] )</span></strong><br />
Метод делает из содержимого первого выбранного элемента скомпилированную версию шаблона форматирования.<br />
<strong><em> name </em></strong> &#8211; необязательно имя шаблона, если имя указано, то потом можно использовать его для обращения к этому шаблону в методе <strong><em>jQuery.tmpl(name, data, options)</em></strong><br />
<strong><span style="text-decoration: underline"> jQuery.template( [ name, ] template )</span></strong><br />
Метод аналогичен описанному выше, только здесь шаблон передаётся в качестве параметра <strong><em>template </em></strong> &#8211; это может быть строка, строка с разметкой, HTML-элемент (в том числе и в jQuery-обёртке).</p>
<h3>Синтаксис шаблона</h3>
<p>Я вкратце приведу описание нескольких самых основных элементов шаблона, остальное надеюсь более подробно описать в следующей статье (если будет положительный отклик на эту статью)<br />
<strong><span style="text-decoration: underline"> ${fieldNameOrExpression} и {{= fieldNameOrExpression}}</span></strong><br />
Позволяет вставить в шаблон значение поля (свойства) объекта данных, может быть также использовано для вставки результата метода или js-выражения. Напрмер, <strong><em>&laquo;${Name}&raquo;</em></strong> &#8211; вставит в шаблон значение поля obj.Name, а при том, что Languages &#8211; это поле объекта, которому присвоен массив, <strong><em>&laquo;${Languages.length}&raquo;</em></strong> &#8211; вставит в шаблон длину этого массива, и наконец, если у объекта есть метод getLanguages с двумя параметрами, то <strong><em>&laquo;${getLanguages(Languages, &#8216; &#8211; &#8216;)}&raquo;</em></strong> &#8211; вставит в шаблон результат работы этого метода.<br />
<strong><span style="text-decoration: underline"> {{html fieldNameOrExpression}}</span></strong><br />
Элемент шаблона ${field} (или {{= field}}) вставляет в результат значение указанного поля в виде текста, т.е. если в строке будут HTML-теги, то они будут закодированы, а не преобразованы в разметку. Если вам требуется вставить данные в шаблон именно в виде HTML-разметки, то надо использовать синтаксис <strong><em>{{html &lt;что нужно вставить&gt;}}</em></strong>.<br />
Для начала работы с плагином уже достаточно рассказано, могу лишь добавить, что синтаксис шаблонов позволяет вставлять вложенные шаблоны, условные инструкции, обращаться к некоторым объектам JS и jQuery и ещё кое-что&#8230; Остальное &#8211; материал будущей статьи.</p>
<h3>Источники</h3>
<p>Статья написана по материалам, найденным во всемирной паутине. В основном это перевод официальной документации. Оригиналы можно посмотреть по следующим ссылкам:</p>
<ul>
<li> <a href="http://blog.jquery.com/2010/10/04/new-official-jquery-plugins-provide-templating-data-linking-and-globalization/"> Сообщение в блоге jQuery</a> и <a href="http://weblogs.asp.net/scottgu/archive/2010/10/04/jquery-templates-data-link-and-globalization-accepted-as-official-jquery-plugins.aspx"> в блогах MS</a></li>
<li> <a href="http://api.jquery.com/category/plugins/templates/"> Страница документации плагина</a> (примеры взяты оттуда)</li>
</ul>
<h3>Об авторе</h3>
<p>Зовут меня Зайцев Андрей, <a href="http://blog.zalab.net/">мой маленький блог</a>, профиль на форуме <a href="http://linkexchanger.su/forum/memberlist.php?mode=viewprofile&amp;u=106">zandroid</a></p>
<p>Это моя первая статья в этом блоге, надеюсь, и не последняя <img src='http://www.linkexchanger.su/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' />  Большое спасибо Геннадию за возможность публикации и за полезные советы по написанию и оформлению материала.</p>
<h3>Примеры</h3>
<p><strong>Пример 1: Динамическое переключение применяемого шаблона</strong></p>
<pre class="brush: xml;">&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
  &lt;style&gt;
    table { cursor:pointer; border-collapse:collapse; border:2px solid blue; width:300px; margin:8px; }
    table tr { border:1px solid blue; color:blue; background-color:#f8f8f8; } table td { padding:3px; } table tr:hover { color:red; }
    .movieDetail { background-color:yellow; } .movieDetail.row1 { border-bottom:none; } .movieDetail.row2 { border-top:none; }
  &lt;/style&gt;
  &lt;script src=&quot;http://code.jquery.com/jquery-latest.min.js&quot;&gt;&lt;/script&gt;
  &lt;script src=&quot;http://nje.github.com/jquery-tmpl/jquery.tmpl.js&quot;&gt;&lt;/script&gt;
&lt;/head&gt;
&lt;body&gt;

&lt;script id=&quot;summaryTemplate&quot; type=&quot;text/x-jquery-tmpl&quot;&gt;
  &lt;tr class='movieSummary'&gt;&lt;td colspan='2'&gt;${Name}&lt;/td&gt;&lt;/tr&gt;
&lt;/script&gt;

&lt;script id=&quot;detailTemplate&quot; type=&quot;text/x-jquery-tmpl&quot;&gt;
  &lt;tr class='movieDetail row1'&gt;&lt;td colspan='2'&gt;${Name}&lt;/td&gt;&lt;/tr&gt;&lt;tr class='movieDetail row2'&gt;&lt;td&gt;${ReleaseYear}&lt;/td&gt;&lt;td&gt;Director: ${Director}&lt;/td&gt;&lt;/tr&gt;
&lt;/script&gt;

Click for details:
&lt;table&gt;&lt;tbody id=&quot;movieList&quot;&gt;&lt;/tbody&gt;&lt;/table&gt;

&lt;script type=&quot;text/javascript&quot;&gt;
var movies = [
  { Name: &quot;The Red Violin&quot;, ReleaseYear: &quot;1998&quot;, Director: &quot;François Girard&quot; },
  { Name: &quot;Eyes Wide Shut&quot;, ReleaseYear: &quot;1999&quot;, Director: &quot;Stanley Kubrick&quot; },
  { Name: &quot;The Inheritance&quot;, ReleaseYear: &quot;1976&quot;, Director: &quot;Mauro Bolognini&quot; }
];
var selectedItem = null;

/* Render the summaryTemplate with the &quot;movies&quot; data */
$( &quot;#summaryTemplate&quot; ).tmpl( movies ).appendTo( &quot;#movieList&quot; );

/* Add onclick handlers for movie template items
using the summary or details template */
$(&quot;#movieList&quot;)
  .delegate( &quot;.movieSummary&quot;, &quot;click&quot;, function () {
    if (selectedItem) {
      // Set the template on the previously selected item
      // back to the summary template
      selectedItem.tmpl = $( &quot;#summaryTemplate&quot; ).template();
      selectedItem.update();
    }
    /* Get the data structure for the template item
       which this clicked element belongs to, and make
       it the selected item */
    selectedItem = $.tmplItem(this);

    /* Set the template on this item to the detail template */
    selectedItem.tmpl = $( &quot;#detailTemplate&quot; ).template();
    selectedItem.update();
  })
  .delegate( &quot;.movieDetail&quot;, &quot;click&quot;, function () {
    /* Set the template on this item to the summary template */
    selectedItem.tmpl = $( &quot;#summaryTemplate&quot; ).template();
    selectedItem.update();
    selectedItem = null;
  });
&lt;/script&gt;

&lt;/body&gt;
&lt;/html&gt;</pre>
<p>Довольно сложный и объемный, в нём используются сразу несколько методов плагина, взят с <a href="http://api.jquery.com/jquery.tmplitem/"> этой страницы</a>.</p>
<p><strong>Пример 2: Вставка данных с разметкой в шаблон</strong></p>
<pre class="brush: xml;">&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
  &lt;style&gt;
    .role {font-weight:bold;font-style: italic;} #movieContainer {padding-left: 8px;}
  &lt;/style&gt;
  &lt;script src=&quot;http://code.jquery.com/jquery-latest.min.js&quot;&gt;&lt;/script&gt;
  &lt;script src=&quot;http://nje.github.com/jquery-tmpl/jquery.tmpl.js&quot;&gt;&lt;/script&gt;
&lt;/head&gt;
&lt;body&gt;

&lt;script id=&quot;movieTemplate&quot; type=&quot;text/x-jquery-tmpl&quot;&gt;
  &lt;h4&gt;${Name}&lt;/h4&gt;
  &lt;p&gt;{{html Synopsis}}&lt;/p&gt;
&lt;/script&gt;

&lt;div id=&quot;movieContainer&quot;&gt;&lt;/div&gt;

&lt;script&gt;
/* The Synopsis data field contains HTML markup. */
var movie = {
  Name: &quot;Meet Joe Black&quot;,
  Synopsis: &quot;The &lt;span class='role'&gt;grim reaper&lt;/span&gt; (&lt;a href='http://www.netflix.com/RoleDisplay/Brad_Pitt/73919'&gt;Brad Pitt&lt;/a&gt;) visits &lt;span class='role'&gt;Bill Parrish&lt;/span&gt; (&lt;a href='http://www.netflix.com/RoleDisplay/Anthony_Hopkins/43014'&gt;Anthony Hopkins&lt;/a&gt;)...&quot;
};

/* Render the template with the movie data.
   The template uses the {{html}} template tag
   to  insert the Synopsis HTML markup data. */
$( &quot;#movieTemplate&quot; ).tmpl( movie )
  .appendTo( &quot;#movieContainer&quot; );
&lt;/script&gt;

&lt;/body&gt;
&lt;/html&gt;</pre>
<p>В этом примере в шаблон добавляются как простые строковые значения полей, так и значения с разметкой, взято вот с <a href="http://api.jquery.com/template-tag-html/"> этой страницы</a>.</p>
<h3>P.S.</h3>
<p>Примеры не стал расписывать, если аудитория поддержит мои начинания, то можно пошагово расписать что, как и почему, и привести ещё несколько примерчиков.</p>
<p>Вопросы по плагину прошу задавать на <a href="/forum/">форуме</a>, если есть комменты именно по статье &#8211; то комментируйте ниже.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.linkexchanger.su/2010/619.html/feed</wfw:commentRss>
		<slash:comments>11</slash:comments>
		</item>
		<item>
		<title>jqGrid Часть III: Расширенные возможности</title>
		<link>http://www.linkexchanger.su/2010/523.html</link>
		<comments>http://www.linkexchanger.su/2010/523.html#comments</comments>
		<pubDate>Sun, 23 May 2010 15:26:04 +0000</pubDate>
		<dc:creator>TRAHOMOTO</dc:creator>
				<category><![CDATA[jQuery]]></category>
		<category><![CDATA[jQuery UI]]></category>

		<guid isPermaLink="false">http://www.linkexchanger.su/?p=523</guid>
		<description><![CDATA[Введение
Я рад приветствовать вас, дорогие читатели в третьей статье цикла, посвященного плагину jqGrid. Прошу прощения за столь долгое отсутствие, сейчас все свободное время отдаю изучению и освоению другого монстра &#8211; ExtJS.
Напомню, что в предыдущей статье (jqGrid Часть II: Базовые возможности) мы перешли от basic к advanced уровню использования этого замечательного плагина. Но прежде чем мы [...]]]></description>
			<content:encoded><![CDATA[<p><strong>Введение</strong><br />
Я рад приветствовать вас, дорогие читатели в третьей статье цикла, посвященного плагину jqGrid. Прошу прощения за столь долгое отсутствие, сейчас все свободное время отдаю изучению и освоению другого монстра &#8211; ExtJS.<br />
Напомню, что в предыдущей статье (<a href="http://www.linkexchanger.su/2010/486.html">jqGrid Часть II: Базовые возможности</a>) мы перешли от basic к advanced уровню использования этого замечательного плагина. Но прежде чем мы начнем я хотел бы сообщить, что jqGrid обновилась до версии 3.6.5 и соответственно и мы будем использовать новую версию.</p>
<p>Итак план работ на эту статью:</p>
<ul>
<li><strong><a href="http://www.linkexchanger.su/2010/523.html#p1">jqGrid и &laquo;деревья&raquo;</a></strong>
<ul>
<li><a href="http://www.linkexchanger.su/2010/523.html#p1.1">Общие сведения о деревьях</a></li>
<li><a href="http://www.linkexchanger.su/2010/523.html#p1.2">jqGrid и SQL деревья</a></li>
<li><a href="http://www.linkexchanger.su/2010/523.html#p1.3">jqGrid и статические деревья</a></li>
</ul>
</li>
<li><strong><a href="http://www.linkexchanger.su/2010/523.html#p2">Связывание данных в jqGrid</a></strong>
<ul>
<li><a href="http://www.linkexchanger.su/2010/523.html#p2.1">Простая подтаблица (subgrid)</a></li>
<li><a href="http://www.linkexchanger.su/2010/523.html#p2.2">jqGrid как subgrid</a></li>
<li><a href="http://www.linkexchanger.su/2010/523.html#p2.3">Ведущая и ведомая jqGrid</a></li>
</ul>
</li>
</ul>
<p><span id="more-523"></span><br />
<a title="p1" name="p1"></a><strong>I.jqGrid и &laquo;деревья&raquo;</strong></p>
<p><a title="p1.1" name="p1.1"></a><strong>1) Краткие сведения о деревьях</strong><br />
Конечно здесь речь пойдет не о садово-огородных работах, а об представлении древовидных структур данных. В очередной раз обратимся за определением к Википедии:</p>
<p><strong>Древовидная структура</strong> является одним из способов представления иерархической структуры в графическом виде. <strong>Древовидной структурой</strong> называется благодаря тому, что граф выглядит как перевернутое дерево. По этой же причине говорят, что корневой узел (корень) находится на самом верху, а листья — внизу.</p>
<p>В принципе более или менее сносное определение, несмотря на то, что оно навивает академическую скуку я все же настоятельно рекомендую ознакомиться с материалом по древовидным структурам. Поверьте моему не большому опыту, абсолютно каждый программист рано или поздно станет перед необходимостью использовать дерево, и тут одним из помошников может стать jqGrid. Плагин может работать с двумя самыми распространенными в вычислительной технике типами деревьев: на основе &laquo;вложенных множеств&raquo; (Nested Sets) и &laquo;матрицы смежностей&raquo; (adjacency matrix). Вообще говоря тема деревьев очень серьезна, это один из разделов теории графов на изучение которых отводится не одна лекция в ВУЗах, но от себя посоветую те материалы которые мне в свое время очень помогли. Столкнувшись впервые с проблемой хранения деревьев я наткнулся на материал по подходу вложенных множеств (<a href="http://phpclub.ru/detail/article/db_tree">Хранение древовидных структур в Базах данных</a>) и сразу же остановился на нем, наверное потому что для работы с таким деревом существует готовый PHP класс. Но в процессе написания статьи я все же прочел статью <a href="http://phpclub.ru/detail/article/2002-06-03">Работа с &laquo;MySQL. Деревья&raquo;</a> в которой рассматривается подход матрицы смежностей.</p>
<p>Думаю здесь общие сведения следует закончить и перейти к практическому рассмотрению. </p>
<p><a title="p1.2" name="p1.2"></a><strong>2) jqGrid и SQL деревья</strong><br />
Для того, чтобы jqGrid могла работать с древовидными структурами, она должна быть загружена с модулем <strong>Tree Grid</strong>. Плагин может работать с обоими типами деревьев, упомянутых в предыдущем пункте. При этом jqGrid может работать с деревьями подгрузив все дерево за один раз или же подгружая ветви и листья по мере необходимости. Собственно загрузку дерева за один раз я рассматривать не буду, не интересно, а вот автоподгрузку мы разберем детально. И на этой ноте давайте перейдем к просмотру <a href="http://trahomoto.dlinkddns.com/works/linkexchanger/articles/jqgrid/p3e1.html">ДЕМО3.1</a>, <a href="http://trahomoto.dlinkddns.com/works/linkexchanger/articles/jqgrid/files/demo3.1.zip">ссылка для скачивания</a> </p>
<pre class="brush: jscript;">
$(function(){
     $('#table_nested').jqGrid({
                     treeGrid: true,
                     treeGridModel: 'nested',
                     ExpandColumn: 'title',
                     ExpandColClick: true,
                     url: 'p3e1_n.php',
                     datatype: &quot;json&quot;,
                     mtype: &quot;POST&quot;,
                     colNames:[&quot;id&quot;,&quot;Раздел&quot;],
                     colModel:[
                                   {name:'cid',index:'cid', width:1, hidden:true, key:true},
                                   {name:'title',index:'title', width:180}
                                  ],
                     treeIcons: {plus:'ui-icon-folder-collapsed',minus:'ui-icon-folder-open',leaf:'ui-icon-document'},
                     height: 'auto',
                     caption: 'вложенныe множества',
                     hidegrid: false
     });

     $('#table_adjacency').jqGrid({
                     treeGrid: true,
                     treeGridModel: 'adjacency',
                     ExpandColumn: 'title',
                     ExpandColClick: true,
                     url: 'p3e1_a.php',
                     datatype: &quot;json&quot;,
                     mtype: &quot;POST&quot;,
                     colNames:[&quot;cid&quot;,&quot;Раздел&quot;],
                     colModel:[
                                   {name:'cid',index:'cid', width:1, hidden:true, key:true},
                                   {name:'title',index:'title', width:180}
                                   ],
                     treeIcons: {plus:'ui-icon-circle-plus',minus:'ui-icon-circle-minus',leaf:'ui-icon-person'},
                     height: 'auto',
                     caption:'матрица смежностей',
                     hidegrid: false
    });
});
</pre>
<p>Настройки обоих таблиц идентичны, но имеется ряд свойств, отличающих эти экземпляры от рассмотренных в предыдущих статьях.</p>
<ul>
<li><strong>treeGrid: true</strong> &#8211; самый главный параметр, указывает что jqGrig будет работать с древовидной структурой, а не с простыми табличными данными</li>
<li><strong>treeGridModel: &#8216;adjacency&#8217; / &#8216;nested&#8217;</strong> &#8211; это свойство определяет какой алгоритм дерева использовать</li>
<li><strong>ExpandColumn: &#8216;title&#8217;</strong> &#8211; определяет при клике по какому столбцу происходит открытие и(или) загрузка ветви</li>
<li><strong>ExpandColClick: true</strong> &#8211; необходимо, чтобы открытие ветви происходило при клике на колонку, а не на иконку</li>
<li><strong>colModel:[... key:true},...]</strong> &#8211; свойство <strong>key</strong> в массиве colModel позволяет указать данные какого столбца следует использовать как уникальный идентификатор записи при запросе данных с сервера. Т.е. благодаря этому свойству, можно переопределить идентификатор. Но только в одном столбце может быть указан <strong>key</strong>. Плагин будет использовать первый, найденный столбец как ключевой.</li>
<li><strong>treeIcons:</strong> &#8211; позволяет переопределить стандартные (<em>{plus:&#8217;ui-icon-triangle-1-e&#8217;,minus:&#8217;ui-icon-triangle-1-s&#8217;,leaf:&#8217;ui-icon-radio-off&#8217;}</em>) иконки листьев и ветвей.</li>
</ul>
<p>С не описанными свойства мы знакомы из предыдущих статей, а другие свойства и методы, характерные только для работы с jqGrid в качестве дерева можно посмотреть на странице <a href="http://www.trirand.com/jqgridwiki/doku.php?id=wiki:treegrid">TreeGrid</a>. </p>
<p>Прежде чем перейдем к рассмотрению серверных скриптов давайте немного разберемся как работает плагин. Сразу после инициализации, таблица отправляет запрос к серверу, при этом передает следующие данные и ждет ответа сервера:</p>
<blockquote><p><strong>В режиме &#8216;nested&#8217; (на примере метода POST)</strong></p>
<ul>
<li><strong>POST['nodeid']</strong> &#8211; идентификатор узла (ветви)</li>
<li><strong>POST['n_left']</strong> &#8211; позиция узла &laquo;слева&raquo;</li>
<li><strong>POST['n_right']</strong> &#8211; позиция узла &laquo;справа&raquo;</li>
<li><strong>POST['n_level']</strong> &#8211; уровень вложенности узла</li>
</ul>
</blockquote>
<p></p>
<blockquote><p><strong>В режиме &#8216;adjacency&#8217; (на примере метода POST)</strong></p>
<ul>
<li><strong>POST['nodeid']</strong> &#8211; идентификатор узла (ветви)</li>
<li><strong>POST['parentid']</strong> &#8211; идентификатор родительского узла</li>
<li><strong>POST['n_level']</strong> &#8211; уровень вложенности узла</li>
</ul>
</blockquote>
<p></p>
<blockquote><p><strong>кроме этого, на сервер также передаются стандартные для jqGrid параметры (на примере метода POST)</strong></p>
<li><strong>POST['page']</strong> &#8211; номер страницы, для &laquo;листалки&raquo;</li>
<li><strong>POST['rows']</strong> &#8211; количество записей, для ограничения при выборке данных</li>
<li><strong>POST['sidx']</strong> &#8211; значение <strong>index:</strong> в<strong> colModel</strong>, имя столбца по которому необходимо отсортировать результат</li>
<li><strong>POST['sord']</strong> &#8211; направление сортировки</li>
</blockquote>
<p>
После получения данных, серверный скрипт выполняет запрос данных из БД и сроит и  выводит результат в следующих форматах. </p>
<blockquote><p><strong>В режиме &#8216;nested&#8217; (на примере XML ответа)</strong></p>
<ul>
<li><strong>пользовательские столбцы</strong></li>
<li><strong>level</strong> &#8211; уровень узла в иерархии</li>
<li><strong>lft</strong> &#8211; позиция узла &laquo;слева&raquo;</li>
<li><strong>rgt</strong> &#8211; позиция узла &laquo;справа&raquo;</li>
<li><strong>isLeaf</strong> &#8211; логический признак (true/false), определяет этот узел является &laquo;веткой&raquo;(есть подузлы) или &laquo;листом&raquo;(нет подузлов)</li>
<li><strong>expanded</strong> &#8211; логический признак (true/false), нужно ли показывать подузлы если этот узел является веткой, т.е. загрузить &laquo;открытым&raquo; или &laquo;закрытым&raquo;</li>
</ul>
</blockquote>
<p></p>
<blockquote><p><strong>В режиме &#8216;adjacency&#8217; (на примере XML ответа)</strong></p>
<ul>
<li><strong>пользовательские столбцы</strong></li>
<li><strong>level</strong> &#8211; уровень узла в иерархии</li>
<li><strong>parent</strong> &#8211; идентификатор родительского узла</li>
<li><strong>isLeaf</strong> &#8211; логический признак (true/false), определяет этот узел является &laquo;веткой&raquo;(есть подузлы) или &laquo;листом&raquo;(нет подузлов)</li>
<li><strong>expanded</strong> &#8211; логический признак (true/false), нужно ли показывать подузлы если этот узел является веткой, т.е. загрузить &laquo;открытым&raquo; или &laquo;закрытым&raquo;</li>
</ul>
</blockquote>
<p>
Ядро (<a href="http://www.trirand.com/jqgridwiki/doku.php?id=wiki:retrieving_data">ридеры</a>) jqGrid разбирают ответ и вставляют данные в таблицу. Вот теперь серверные скрипты будут более-менее понятны. </p>
<p><strong>Вложенные множества (<em>p3e1_n.php</em>)</strong>(опущены подключение к БД и вывод результата)</p>
<pre class="brush: php;">
// Получаем параметры от таблицы
    # ВНИМАНИЕ!!!
    # Данный код не имеет проверок запрашиваемых данных
    # что может стать причиной взлома! Обязательно проверяйте все данные
    # поступающие от клиента

    $node  = intval($_POST['nodeid']);
    $left  = intval($_POST['n_left']);
    $right = intval($_POST['n_right']);
    $level = intval($_POST['n_level']);

// Настраиваем условие для выбора ветки
    $WHERE = '';
    if($node &gt; 0) {
       $level = $level + 1;  // нужно выбрать следующий уровень
       $WHERE = &quot; AND cleft &gt; &quot;.$left.&quot; AND cright &lt; &quot;.$right.&quot; AND clevel = &quot;.$level;
    }else{
       $level = 0;
    }

// Выбираем подразделы
    $query = &quot;SELECT cid, title, clevel, cleft, cright FROM tree_nested_sets WHERE cleft BETWEEN cleft AND cright &quot;.$WHERE.&quot; GROUP BY title ORDER BY cleft;&quot;;
    $result = mysql_query($query);

// Формируем результат
    $response-&gt;page = 1;
    $response-&gt;total = 1;
    $response-&gt;records = 1;

    $i = 0;
    while($row = mysql_fetch_assoc($result)) {
        //-------------------------------------
        // Определяем является ли выбранный узел &quot;листом&quot;
        if($row['cright'] == $row['cleft']+1)
            $leaf = 'true';
        else
            $leaf='false';
        //-------------------------------------

        if($level == $row['clevel']){
            $response-&gt;rows[$i]['cell'] = array($row['cid'], $row['title'], $row['clevel'], $row['cleft'], $row['cright'], $leaf, 'false');
        }
        $i++;
    }
</pre>
<p><strong>Матрица смежностей(<em>p3e1_a.php</em>)</strong>(опущены подключение к БД и вывод результата)</p>
<pre class="brush: php;">
// Выбираем все разделы без подразделов (листья)
    $query_feafs = &quot;SELECT t1.cid FROM tree_adjacency_matrix AS t1 LEFT JOIN tree_adjacency_matrix AS t2 ON t1.cid = t2.id_parent WHERE t2.cid IS NULL&quot;;
    $sql_leafs = mysql_query($query_feafs);

    while($row = mysql_fetch_assoc($sql_leafs)) {
       $leafs[] = $row['cid']; // Массив для хранения &quot;листьев&quot;
    }
    unset($row);

// Получаем параметры от таблицы
    # ВНИМАНИЕ!!!
    # Данный код не имеет проверок запрашиваемых данных
    # что может стать причиной взлома! Обязательно проверяйте все данные
    # поступающие от клиента

    $level  = intval($_POST['n_level']);
    $node   = intval($_POST['nodeid']);

// Настраиваем условие для выбора ветки
    if($node &gt; 0) {
       $level = $level + 1;            // нужно выбрать следующий уровень
       $WHERE = 'id_parent ='.$node;
    } else {
       $WHERE = 'id_parent IS NULL';   // выбрать корень(ни)
    } 

// Выбираем подразделы
    $query  = &quot;SELECT cid, title, id_parent FROM tree_adjacency_matrix WHERE &quot;.$WHERE;
    $result = mysql_query($query);

// Формируем результат
    $response-&gt;page      = 1;
    $response-&gt;total     = 1;
    $response-&gt;records   = 1;

    $i = 0;
    while($row = mysql_fetch_assoc($result)) {
       //-------------------------------------
       // Определяем ID родителя этого узла
       if(!$row['id_parent'])
           $parent = 'NULL';
       else
          $parent = $row['id_parent'];
       //-------------------------------------
       // Определяем является ли выбранный узел &quot;листом&quot;
       if(in_array($row['cid'], $leafs))
           $is_leaf = 'true';
       else
           $is_leaf = 'false';
       //--------------------------------------

       $response-&gt;rows[$i]['cell'] = array($row['cid'], $row['title'], $level, $parent, $is_leaf, FALSE);

       $i++;
    }</pre>
<p>Я считаю комментировать в этих листингах особо нечего, кроме того что данные поступающие с сервера не ограничиваются по кол-ву записей и не сортируются. А практически все остальное является повторением примеров из предыдущих статей или специфическими вещами при работе с деревьями, описанными в статьях приложенных выше. Также обращу ваше внимание на то, что пример работы с <a href="http://www.trirand.com/jqgridwiki/doku.php?id=wiki:nested_set_model">Nested Set Model</a> описанный в документации разработчиком не хотел работать, пришлось его доводить, за что мне большое спасибо <img src='http://www.linkexchanger.su/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> .</p>
<p><a title="p1.3" name="p1.3"></a><strong>3)jqGrid и статические деревья</strong><br />
Название <em>статические деревья</em> оносительно, того что мы делали немного ранее. Безусловно данные &laquo;загруженные&raquo; таким способом могут быть построены и скриптом динамически, но все же бывают такие задачи, когда дерево уже есть необходимо лишь вывести его в дружественной для пользователя форме как в <a href="http://trahomoto.dlinkddns.com/works/linkexchanger/articles/jqgrid/p3e2.html">ДЕМО3.2</a> . Не правда ли что-то напоминает? Да! это меню из демо jqGrid от разработчика. </p>
<p>Листинг клиентской части:</p>
<pre class="brush: jscript;">
  $('#table').jqGrid({
            url: &quot;tree.xml&quot;,
            datatype: &quot;xml&quot;,
            height: &quot;auto&quot;,
            pager: false,
            loadui: &quot;disable&quot;,
            colNames: [&quot;id&quot;,&quot;Items&quot;,&quot;url&quot;],
            colModel: [
                {name: &quot;id&quot;,width:1,hidden:true, key:true},
                {name: &quot;menu&quot;, width:150, resizable: false, sortable:false},
                {name: &quot;url&quot;,width:1,hidden:true}
            ],
            treeGrid: true,
                    caption: &quot;jqGrid Demos&quot;,
            ExpandColumn: &quot;menu&quot;,
            width: 200,
            rowNum: 200,
            ExpandColClick: true,
            treeIcons: {leaf:'ui-icon-document-b'}
 });
</pre>
<p>Полностью идентичные параметры плагина, за исключением того, что я отключил обработчик события <strong>onSelectRow</strong>.<br />
Из листига должно стать понятно, что таблица запрашивает статичный(оносительно) файл <em>tree.xml</em>, который выглядит так</p>
<pre class="brush: xml;">&lt;?xml version='1.0' encoding=&quot;utf-8&quot;?&gt;
&lt;rows&gt;
    &lt;page&gt;1&lt;/page&gt;
    &lt;total&gt;1&lt;/total&gt;
    &lt;records&gt;1&lt;/records&gt;
    &lt;row&gt;&lt;cell&gt;1&lt;/cell&gt;&lt;cell&gt;Loading Data&lt;/cell&gt;&lt;cell&gt;&lt;/cell&gt;&lt;cell&gt;0&lt;/cell&gt;&lt;cell&gt;1&lt;/cell&gt;&lt;cell&gt;10&lt;/cell&gt;&lt;cell&gt;false&lt;/cell&gt;&lt;cell&gt;false&lt;/cell&gt;&lt;/row&gt;
    &lt;row&gt;&lt;cell&gt;2&lt;/cell&gt;&lt;cell&gt;XML Data&lt;/cell&gt;&lt;cell&gt;xmlex.html&lt;/cell&gt;&lt;cell&gt;1&lt;/cell&gt;&lt;cell&gt;2&lt;/cell&gt;&lt;cell&gt;3&lt;/cell&gt;&lt;cell&gt;true&lt;/cell&gt;&lt;cell&gt;true&lt;/cell&gt;&lt;/row&gt;
    &lt;row&gt;&lt;cell&gt;3&lt;/cell&gt;&lt;cell&gt;JSON Data&lt;/cell&gt;&lt;cell&gt;jsonex.html&lt;/cell&gt;&lt;cell&gt;1&lt;/cell&gt;&lt;cell&gt;4&lt;/cell&gt;&lt;cell&gt;5&lt;/cell&gt;&lt;cell&gt;true&lt;/cell&gt;&lt;cell&gt;true&lt;/cell&gt;&lt;/row&gt;

...

&lt;/rows&gt;
</pre>
<p>Т.е. jqGrid просто разбирает данные из этого файла и строит TreeGrid.</p>
<p><a title="p2" name="p2"></a><strong>II. Связывание данных в jqGrid</strong></p>
<p>Иногда при построении какого либо приложения необходимо как то сгруппировать данные для экономии визуального пространства на странице и при этом иметь возможность как можно быстрее и комфортнее получить доступ к более детальной информации или связанной с текущей. Немного сумбурно получилось <img src='http://www.linkexchanger.su/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> , но надеюсь вы меня поняли, а если не поняли то примеры разложат все по полочкам.</p>
<p><a title="p2.1" name="p2.1"></a><strong>1)Простая подтаблица (subgrid)</strong></p>
<p>Это весьма полезная опция у этого плагина. На <a href="http://trahomoto.dlinkddns.com/works/linkexchanger/articles/jqgrid/p3e3.html">ДЕМО3.3</a> обратите внимание на крестик в крайнем правом столбце таблицы. При клике на нем под выбранной строкой распахивается &laquo;карман&raquo; в котором находится небольшая табличка с названиями городов и прочей &laquo;рыбой&raquo;.  Вот пример группировки. Теперь давайте перейдем к листингам.</p>
<pre class="brush: jscript;">
$('#table').jqGrid({
       url:'p3e3.php',
       datatype: 'json',
       mtype: 'POST',
       colNames:['Код страны','Cтрана',''],
       colModel :[
          {name:'country_code', index:'country_code', width:80, hidden: true},
          {name:'country_name', index:'country_name', width:60},
          {name:'description', width:200, sortable: false}
          ],
       pager: $('#tablePager'),
       rowNum: 100,
       scroll: true,
       viewrecords: true,
       sortname: 'country_name',
       sortorder: 'asc',
       width: 700,
       height: 400,
       subGrid: true,
         subGridUrl: 'p3e3.php?get=subgrid',
         subGridModel:[{
                     name :  ['&amp;nbsp;','Город', 'Широта', 'Долгота'],
                     width:  [40, 200, 150, 150],
                     align:  ['center','left','right','right'],
                     params: ['country_code','country_name']
                 }]
});
</pre>
<p>Как видите, абсолютно обыкновенный jqGrid, но в &laquo;настройках&raquo; есть несколько интересных моментов:</p>
<ul>
<li><strong>subGrid:</strong> &#8211; в строке 19. Эта опция позволяет использовать подтаблицу.</li>
<li><strong>subGridUrl:</strong> &#8211; указывает путь к скрипту, который должен вернуть данные для построения подтаблицы. В данном случае происходит запрос к тому же скрипту, который &laquo;обслуживает&raquo; и таблицу-родителя при этом передав ему параметр <em>get</em> со значением <em>subgrid</em></li>
<li><strong>subGridModel:</strong> &#8211; свойство которое задает параметры подтаблицы. Аналогично <strong>colModel</strong> таблицы-родителя</li>
</ul>
<p><strong>subGridModel:</strong> &#8211; представляет из себя массив с объектом, у которого есть свои параметры. <strong>name</strong>, <strong>width</strong> и <strong>align</strong> уверен их имена говорят сами за себя, это тексты заголовков столбцов, ширины соответствующих столбцов и выравнивание в них соответственно. Более интересным будет свойство <strong>params</strong>. Оно позволяет передать значения из каких то ячеек родительской таблицы на сервер при запросе подтаблицы. Иными словами, когда вы нажмете на крестик , открывающий подтаблицу, происходит запрос к серверу которому передается  клоч (id) записи (в нашем случае это <em>country_code</em>) и плос к этим данным можно передать дополнительные (в нашем случае серверу будет передан еще и <strong>country_name</strong>). А вот на сервере получив значения нужных параметров (в этом примере серверный скрипт просто игнорирует значение параметра <strong>country_name</strong>) вы выполняем запрос к БД и выводим JSON объект с данными.</p>
<pre class="brush: php;">
...
$get = $_REQUEST['get'];  // Параметр указывающий на то
                                    //  что мы запросили информацию для подтаблицы
...
switch ($get){
    case 'subgrid':
        // Выбрать города запрошенной страны
        $id =$_REQUEST['id'];

        // Запрос данных
        $query = &quot;SELECT id, country_code, region_code, city, latitude, longitude, nbip FROM cities  WHERE country_code LIKE '&quot;.$id.&quot;'&quot;;

        $result = mysql_query($query);
        if(mysql_num_rows($result) &gt;= 1){
            // Строки данных для таблицы
            $i = 0;
            while($row = mysql_fetch_assoc($result)) {
                $data-&gt;rows[$i]['cell'] = array($i+1,'&lt;strong&gt;&lt;em&gt;'.$row['city'].'&lt;/em&gt;&lt;/strong&gt;',$row['latitude'],$row['longitude']);
                $i++;
            }
        }
        break;

    default:
        // Выбрать названия стран и сосчетать города
        ...

}
// Вывести результат
...
</pre>
<p>Серверный скрипт особо ничем не отличается от рассмотренных в предыдущих примерах. При этом он разбит на две части оператором <strong>switch</strong>. Который по умолчанию выполняет &laquo;стандартную&raquo; обработку и вывод данных для главной таблицы. А вот если этот скрипт запустить передав ему  параметр <em>get</em> (я выбрал такое имя только ради наглядности) тогда будет выполнена выборка данных для подтаблицы. Ну и если у вас чешутся руки поковырять пример, вы можете <a href="http://trahomoto.dlinkddns.com/works/linkexchanger/articles/jqgrid/files/demo3.3.zip">скачать его</a>.</p>
<p>Подтаблицы это замечательный инструмент, который позволяет очень элегантно организавать данные, но при этом имеет один существенный недостаток &#8211; подтаблица &laquo;не живая&raquo;. Т.е. данные в подтаблице не могут сортироваться, нет возморжности реализовать поиск и т.д. А это очень критично если в подтаблице будет выводиться много записей. Однако это ограничение отпадает само собой при использовании jqGrid как подтаблицы.</p>
<p><a title="p2.2" name="p2.2"></a><strong>2)jqGrid как subgrid</strong><br />
Итак начнем с промотра <a href="http://trahomoto.dlinkddns.com/works/linkexchanger/articles/jqgrid/p3e4.html">ДЕМО3.4</a>. Как видите все тот же &laquo;родной&raquo; jqGrid с названиями стран, и кол-вом городов в этой стране, но при этом подтаблица теперь представляет из себя самую настоящую jqGrid со всеми свойствами и методами. Согласитесь, что так работать намного приятнее и удобнее. </p>
<p>А вот здесь самое интересное &#8211; клиентский код (в листинге опущены все настройки родительской таблицы, кроме одного свойства):</p>
<pre class="brush: jscript;">
...
subGrid: true,
subGridRowExpanded: function(subgrid_id, row_id) {
    var subgrid_table_id;
    subgrid_table_id = subgrid_id+'_t';

    $('#'+subgrid_id).html('&lt;table id=&quot;'+subgrid_table_id+'&quot;&gt;&lt;/table&gt;&lt;div id=&quot;'+subgrid_table_id+'_pager&quot;&gt;&lt;/div&gt;');
    $('#'+subgrid_table_id).jqGrid({
        url: 'p3e4.php',
        datatype: 'json',
        mtype: 'POST',
        postData: {'get':'subgrid', 'id':row_id},
        colNames: ['Город', 'Широта', 'Долгота'],
        colModel: [
            {name: 'city', index: 'city', width:130},
            {name: 'latitude', index: 'latitude', width:80, align: 'right'},
            {name: 'longitude', index: 'longitude', width:80, align: 'right'}
            ],
        height: 'auto',
        autowidth: true,
        rownumbers: true,
        rownumWidth: 40,
        rowNum: 10,
        sortname: 'city',
        sortorder: 'asc',
        pager: $('#'+subgrid_table_id+'_pager'),
        rowNum:10,
        rowList:[10,20,50,100]
    });
}
...
</pre>
<p>Настройки таблицы-родителя идентичные предыдущему примеру, но при этом появилось новое свойство <strong>subGridRowExpanded</strong>. Думаю самые догадливые поняли, а самые любопытные прочли в документации в секции <a href="http://www.trirand.com/jqgridwiki/doku.php?id=wiki:subgrid">Subgrid</a>, что <strong>subGridRowExpanded</strong> не просто свойство, а событие. </p>
<blockquote><p>
<strong>subGridRowExpanded</strong> &#8211; это событие возникает когда пользователь кликает на крестик раскрытия подтаблицы и выполняет определенную функцию при распахивании &laquo;кармана&raquo; для подтаблицы. При этом в функцию обработчик будут переданы 2 параметра:</p>
<ul>
<li><strong>pID</strong> &#8211; уникальный идентификатор &laquo;кармана&raquo; (контейнера, если хотите) который открывается под выбранной строкой</li>
<li><strong>id</strong> &#8211; идентификатор записи под которой открылся &laquo;карман&raquo;</li>
</ul>
</blockquote>
<p>Безусловно существуют еще события, о которых вы можете узнать из документации. А мы пока пробежимся по листингу. Итак когда пользователь &laquo;тыцнул&raquo; на крестик, открывается контейнер и выполняется функция. Которая получает 2 параметра <em>subgrid_id</em> и <em>row_id</em>. Далее в строке 5 создаем строковое значение для атрибута id и в 6й строке используем метод jQuery, который вставляет в &laquo;карман&raquo; (кстати сказать что это обыкновенный DIV) html разметку таблицы и DIV&#8217;a. А далее идет бональная инициализация jqGrid для только что созданной разметки. Т.е. фактически мы просто напросто динамически создали разметку и создали jqGrid! В этот момент я воскликнул &laquo;Тааак!&raquo; и потер руки. Почему? Да потому, что зная id объекта (&lt;div id=&quot;someid&quot;&gt;&#8230;&lt;/div&gt;) с помощью jQuery можно сотворить с ним все что угодно. Но прежде чем мы перейдем к следующему примеру я приведу <a href="http://trahomoto.dlinkddns.com/works/linkexchanger/articles/jqgrid/files/demo3.4.zip">ссылку на раздаточный материал</a> по теме &#8211; это пример который только что расмотрели.<br />
А теперь зная id контейнера давайте, еще добавим наглядности нашему примеру со странами и городами, например как на <a href="http://trahomoto.dlinkddns.com/works/linkexchanger/articles/jqgrid/p3e5.html">ДЕМО3.5</a>. Как видите все эта же, скучная таблица со странами, но теперь при клике на крестик открывается подробная информация и флаг выбранной строки. Под которой свернутая jqGrid таблица с перечнем городов. Этот пример вы можете скачать перейдя по <a href="http://trahomoto.dlinkddns.com/works/linkexchanger/articles/jqgrid/files/demo3.5.zip">этой ссылке</a>. Внутри архива все скрипты, но наибольший интерес представляет клиентский код. Я условно разделю его на 2 части, первая (нижняя) часть:</p>
<pre class="brush: jscript;">
$('#table').jqGrid({
          ...
          subGrid: true,
          subGridRowExpanded: function(subgrid_id, row_id) {
                       getCountryDetails(subgrid_id, row_id);
                   }

});
</pre>
<p>Это таблица из предыдущего примера, но теперь при возникновении события <strong>subGridRowExpanded</strong> будет выполнена пользовательская функция <strong>getCountryDetails()</strong>, которая и является 2 половиной клиентского скрипта:</p>
<pre class="brush: jscript;">
function getCountryDetails(container, countryCode){
    $.ajax({
            type: 'post',
            url: 'p3e5.php',
            data: ({'id': countryCode, 'get': 'details'}),
            dataType: 'json',
            success: function(server){
                $('#'+container).html('&lt;div class=&quot;c_detail&quot;&gt;&lt;img src=&quot;files/images/'+server.flag+'&quot;&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Столица:&lt;/strong&gt; '+server.capital+'&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Дата независимости:&lt;/strong&gt; '+server.independence_date+'&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Официальные языки:&lt;/strong&gt; '+server.off_lng+'&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Форма правления:&lt;/strong&gt; '+server.government+'&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Глава правительства:&lt;/strong&gt; '+server.president+'&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Валюта:&lt;/strong&gt; '+server.currency+'&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Интернет-домены:&lt;/strong&gt; '+server.domains+'&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Телефонный код:&lt;/strong&gt; '+server.dialing_code+'&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Часовой пояс:&lt;/strong&gt; '+server.time_zone+'&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;');

                var subgrid_table_id = container+'_t';

                $('#'+container).append('&lt;table id=&quot;'+subgrid_table_id+'&quot;&gt;&lt;/table&gt;&lt;div id=&quot;'+subgrid_table_id+'_pager&quot;&gt;&lt;/div&gt;');
                $('#'+subgrid_table_id).jqGrid({
                          caption: 'Города',
                          hiddengrid: true,
                          url: 'p3e4.php',
                          datatype: 'json',
                          mtype: 'POST',
                          postData: {'get':'subgrid', 'id':countryCode},
                          colNames: ['Город', 'Широта', 'Долгота'],
                          colModel: [
                            {name: 'city', index: 'city', width:130},
                            {name: 'latitude', index: 'latitude', width:80, align: 'right'},
                            {name: 'longitude', index: 'longitude', width:80, align: 'right'}
                          ],
                          height: 'auto',
                          autowidth: true,
                          rownumbers: true,
                          rownumWidth: 40,
                          rowNum: 10,
                          sortname: 'city',
                          sortorder: 'asc',
                          pager: $('#'+subgrid_table_id+'_pager'),
                          rowNum:10,
                          rowList:[10,20,50,100]
                });
            }
    });
}
</pre>
<p>В теле этой функции выполняется AJAX запрос, передающий серверному скрипту 2 параметра методом POST:</p>
<ul>
<li><strong>id</strong> &#8211; идентификатор записи, в нашем случае это сокращенный код страны</li>
<li><strong>get</strong> &#8211; этот параметр указывает, что мы хотим получить с сервера детальную информацию по выбранной стране</li>
</ul>
<p>После того как запрос будет успешно выполнен (см. строку 8), в открывшийся под выбранной строкой контейнер будет помещена какая-то HTML разметка с полученными с сервера данными. В данном примере такая разметка представлена не нумированным списком в картинкой &#8211; флагом страны. После этого в этот же контейнер помещается разметка для дочерней таблицы и производится инициализация дочерней таблицы.</p>
<p>В принципе логика работы достаточно проста, главное не забывать что работать с полученными от сервера данными можно только после окончания AJAX запроса или производя синхронные запросы. </p>
<p>Как вы понимаете в контейнер для подтаблицы можно помещать не только какую-то примитивную разметку, но и формы, флеш, другие &laquo;вкусные&raquo; и полезные вещи.</p>
<p><a title="p2.3" name="p2.3"></a><strong>3)Ведущая и ведомая jqGrid</strong></p>
<p>Однако не всегда можно сгруппировать данные и засунуть их в подтаблицу. Иногда данные могут быть полностью самостоятельными и при этом иметь некую связь. Например однажды передо мной стала задача вывести пользователю перечень счетов и платежей по этим счетам, и при этом обеспечить возможность просмотра счетов, платежей и платежей по выбранному счету. И тут то мне очень помогла методика &laquo;ведущий-ведомый&raquo;. Работа которой проиллюстрирована в <a href="http://trahomoto.dlinkddns.com/works/linkexchanger/articles/jqgrid/p3e6.html">ДЕМО5.6</a>.</p>
<p>Имеется две jqGrid, отображающие страны и города, которые можно менять местами благодаря плагину <em>sortable</em>. Также в верхней части документа есть checkbox который включает функцию фильтрации содержимого ведомой (нижней) таблицы, на основе выбранной записи в ведущей (верхней) таблице. При этом вся фильтровка осуществляется на стороне сервера, с этого и начнем разбор примера. Для удобства две таблицы запрашивают данные у двух отдельных серверных скриптов, один для городов, другой для стран.<br />
<strong>Скрипт для таблицы &laquo;Города&raquo;</strong> (т.к. основная структура скрипта &laquo;стандартна&raquo; я привожу лишь основные моменты):</p>
<pre class="brush: php;">
// Получение параметров page, rows и т.д.
...
if($_REQUEST['filterBy'] != 'null')  // Фильтр
    $WHERE = &quot; WHERE country_code = '&quot;.$_REQUEST['filterBy'].&quot;' &quot;;
else
    $WHERE = '';
...
// Запрос подсчета суммарного кол-ва записей
$result = mysql_query(&quot;SELECT COUNT(*)AS count FROM cities&quot;.$WHERE);
...
// Запрос выборки данных
$result = mysql_query(&quot;SELECT city, latitude, longitude FROM cities &quot;.$WHERE.&quot; ORDER BY &quot;.$sidx.&quot; &quot;.$sord.&quot; LIMIT &quot;.$start.&quot;, &quot;.$limit);
</pre>
<p>В данном случае клиентский код при запросе данных с сервера помимо &laquo;стандартных&raquo; параметров, отправляет еще и дополнительный параметр <strong>filterBy</strong>, который по сути и определяет критерий по которому нужно выбрать записи из БД и вернуть клиенту. Что и проиллюстрировано в последующих запросах.</p>
<p><strong>Скрипт для таблицы &laquo;Страны&raquo;:</strong></p>
<pre class="brush: php;">
// Получение параметров page, rows и т.д.
...
if($_REQUEST['filterBy'] != 'null'){
     $country = @mysql_fetch_array(mysql_query(&quot;SELECT country_code FROM cities WHERE id = &quot;.$_REQUEST['filterBy']));
     $WHERE = &quot; WHERE country_code = '&quot;.$country['country_code'].&quot;' &quot;;
}else{
     $WHERE = '';
}
...
// Запрос подсчета суммарного кол-ва записей
$result = mysql_query(&quot;SELECT COUNT(*)AS count FROM countries &quot;.$WHERE);
...
// Запрос выборки данных
$result = mysql_query(&quot;SELECT * FROM countries &quot;.$WHERE.&quot; ORDER BY &quot;.$sidx.&quot; &quot;.$sord.&quot; LIMIT &quot;.$start.&quot;, &quot;.$limit);
</pre>
<p>Как видите полная аналогия предыдущего листинга, но теперь если передано определенное значение фильтра, происходит дополнительный запрос для уточнения кода страны.<br />
На этом все премудрости серверной стороны заканчиваются и начинается клиентский код, но прежде чем перейти к его рассмотрению <strong>пара слов о разметке страницы</strong>:</p>
<pre class="brush: xml;">
&lt;input type=&quot;checkbox&quot; id=&quot;link&quot;&gt;
&lt;label for=&quot;link&quot;&gt;Связать таблицы&lt;/label&gt;

&lt;div id=&quot;interfaceBody&quot; style=&quot;margin: 10px 10px 10px 10px; padding: 0px 10px 10px 10px; border: 2px solid gray&quot;&gt;
&lt;div class=&quot;grid&quot; id=&quot;countries&quot; style=&quot;padding: 10px 10px 10px 10px; margin-top: 10px; border: 2px solid red&quot;&gt;
    &lt;table id=&quot;table_countries&quot;&gt;&lt;/table&gt;
    &lt;div id=&quot;table_countriesPager&quot;&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;grid&quot; id=&quot;cities&quot; style=&quot;padding: 10px 10px 10px 10px; margin-top: 10px; border: 2px solid green&quot;&gt;
    &lt;table id=&quot;table_cities&quot;&gt;&lt;/table&gt;
    &lt;div id=&quot;table_citiesPager&quot;&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
</pre>
<p>Если не обращать внимание на ужасно длинные inline-стили, то разметка достаточно проста. Сначала идет код checkbox&#8217;a с меткой, а следом контейнер всего интерфейса, внутри которого два дочерних контейнера, по одному для каждой таблицы. </p>
<p>И наконец <strong>клиентский скрипт:</strong></p>
<pre class="brush: jscript;">
//-------------------------------------------------------------------
// Функция-обработчик
function stickTogether(){
    if($('#link').is(':checked')){
        // Флаг &quot;Связать таблицы&quot; утановлен
        var masterId        = $('#interfaceBody &gt; DIV:first').attr('id');                //Определяем ID контейнера &quot;ведущей таблицы&quot;
        var masterPostData  = $('#table_'+masterId).jqGrid('getGridParam','postData');   //Получаем весь объект postData &quot;ведущей таблицы&quot;
        var selId    = $('#table_'+masterId).jqGrid('getGridParam','selrow');            //Получаем id записи в выделенной строке
                                                                                         //не путайте с rowIndex
        // Проверяем какое текущее значение фильтра ведущей таблицы, он должен быть сброшен (например null)
        if(masterPostData.filterBy){
            $('#table_'+masterId).jqGrid('setGridParam',{'postData':{'filterBy':null}}); //Сбрасываем значение фильтра
            $('#table_'+masterId).trigger('reloadGrid');                                 //Перезагружаем данные в таблицу
        }

        var slaveId  = $('#interfaceBody &gt; DIV:last').attr('id');                        //Определяем ID контейнера &quot;ведомой таблицы&quot;
        $('#table_'+slaveId).jqGrid('setGridParam',{'postData':{'filterBy':selId}});     //Применяем в качестве фильтра ведомой таблицы
                                                                                         //id выбранной записи в ведущей таблице
        $('#table_'+slaveId).trigger('reloadGrid');                                      //Перезагружаем данные в таблицу
    }else{
        // Флаг &quot;Связать таблицы&quot; не утановлен
        var coutriesT       = $('#table_countries');
        var countriesPost   = coutriesT.jqGrid('getGridParam','postData');

        if(countriesPost.filterBy){ //Сбрасываем фильтр т.к. ничего не нужно фильтровать
            coutriesT.jqGrid('setGridParam',{'postData':{'filterBy':null}})
            coutriesT.trigger('reloadGrid');
        }
        //-----------------------
        var citiesT       = $('#table_cities');
        var citiesPost    = citiesT.jqGrid('getGridParam','postData');

        if(citiesPost.filterBy){ //Сбрасываем фильтр т.к. ничего не нужно фильтровать
            citiesT.jqGrid('setGridParam',{'postData':{'filterBy':null}})
            citiesT.trigger('reloadGrid');
        }
    }
}
//-------------------------------------------------------------------
// Настройка плагинов
var tcountries = $('#table_countries').jqGrid({
                          caption: 'Страны',
                          hidegrid: false,
                          url:'p3e6_countries.php',
                          postData: {'filterBy':null},
                          datatype: 'json',
                          mtype: 'POST',
                          colNames:['Код страны','Cтрана','Столица','Домены'],
                          colModel :[
                                    {name:'country_code', index:'country_code', width:80, hidden: true},
                                    {name:'country_name', index:'country_name', width:60},
                                    {name:'capital', index:'capital', width:60},
                                    {name:'domains', index:'domains', width:60}
                                    ],
                          pager: $('#table_countriesPager'),
                          rowNum: 100,
                          scroll: true,
                          viewrecords: true,
                          sortname: 'country_name',
                          sortorder: 'asc',
                          height: 200,
                          autowidth: true,
                          onSelectRow: stickTogether
                });

var tcities = $('#table_cities').jqGrid({
                          caption: 'Города',
                          hidegrid: false,
                          url: 'p3e6_cities.php',
                          postData: {'filterBy':null},
                          datatype: 'json',
                          mtype: 'POST',
                          colNames: ['Город', 'Широта', 'Долгота'],
                          colModel: [
                            {name: 'city', index: 'city', width:130},
                            {name: 'latitude', index: 'latitude', width:80, align: 'right'},
                            {name: 'longitude', index: 'longitude', width:80, align: 'right'}
                          ],
                          height: 200,
                          autowidth: true,
                          rownumbers: true,
                          rownumWidth: 40,
                          rowNum: 100,
                          sortname: 'city',
                          sortorder: 'asc',
                          pager: $('#table_citiesPager'),
                          scroll: true,
                          viewrecords: true,
                          onSelectRow: stickTogether
                });

$(&quot;#interfaceBody&quot;).sortable({
        connectWith: '.grid',
        placeholder: 'ui-state-error',
        opacity: 0.7,
        stop: stickTogether
});

//-------------------------------------------------------------------
// Назначаем обработчик на событие
$('#link').change(stickTogether);
</pre>
<p>При всем своем объеме код достаточно груб и прост. Начнем пожалуй с назначения обработчика на изменение checkbox&#8217;a, самая последняя строка в листинге. Как видите при изменении checkbox&#8217;a будет вызвана функция <strong>stickTogether</strong>. В теле которой происходит проверка состояния флажка и если он не установлен, то произойдет сброс всех &laquo;фильтров&raquo; таблиц и перезагрузка данных в таблице.<br />
Но самое интересное это код в &laquo;положительной&raquo; ветке if&#8217;а, когда флажок установлен. Мы знаем, что ведущая таблица расположена вверху, значит для начала узнаем какая же таблица в данный момент находится там. Для этого получаем значение атрибута <em>id</em> первого контейнера (строка 6).  Теперь зная какая таблица ведущая &#8211; получаем объект <strong>postData</strong>, который, напомню, содержит все параметры и их значения для отправки серверу. В 7й строке получим Id записи в выделенной строке ведущей таблицы. Далее проверив значение фильтра ведущей таблицы, при необходимости сбросим его. Это необходимо если пользователь поменял таблицы местами.<br />
В строке 16 узнаем id контейнера ведомой таблицы. И применяем фильтр, записав id выделенной строки в ведущей таблице, и перезагружаем данные.<br />
У данного скрипта есть один недостаток &#8211; расплата за универсальность. Из-за того что на событие <strong>onSelectRow:</strong> в обоих таблицах определен один и тот же обработчик <strong>stickTogether</strong>, при выделении строки в ведомой таблице, происходит постоянное обновление ее записей. Но уверен что те кому потребуется такой функционал jqGrid без труда смогут это исправить. </p>
<p><strong>Заключение</strong><br />
Подведя итоги проделанной работы можно сказать, что в данной статье мы рассмотрели практически все варианты расширенного использования плагина. Научились работать с древовидными данными и подтаблицами, а также попробовали управлять содержимым ведомой таблицы посредством данных из ведущей. В следующей части я рассмотрю еще одну экзотическую интересную опцию jqGrid &#8211; &laquo;drag&#8217;n'drop записей между jqGrid&#8217;ами&raquo;.<br />
Ну и как всегда если у вас возникли вопросы задавайте их на <a href="http://www.linkexchanger.su/forum/">форуме</a> ну а комментарии используйте для предложений и пожеланий.</p>
<p>Итак приблизительный план работ на следующий выпуск:</p>
<ul>
<li><strong>Расширенные возможности jqGrid</strong>
<ul>
<li>drag&#8217;n'drop записей между jqGrid&#8217;ами</li>
</ul>
</li>
<li><strong>Поиск данных в jqGrid</strong>
<ul>
<li>&laquo;inline&raquo; поиск</li>
<li>поиск с использованием встроенной формы</li>
<li>расширенный поиск с использованием встроенной формы</li>
<li>поиск с использованием внешней формы</li>
</ul>
</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://www.linkexchanger.su/2010/523.html/feed</wfw:commentRss>
		<slash:comments>34</slash:comments>
		</item>
		<item>
		<title>Yii framework. Часть 1 : Введение</title>
		<link>http://www.linkexchanger.su/2010/418.html</link>
		<comments>http://www.linkexchanger.su/2010/418.html#comments</comments>
		<pubDate>Sat, 06 Mar 2010 04:44:29 +0000</pubDate>
		<dc:creator>M4V23</dc:creator>
				<category><![CDATA[php]]></category>
		<category><![CDATA[mvc]]></category>
		<category><![CDATA[yii]]></category>

		<guid isPermaLink="false">http://www.linkexchanger.su/?p=418</guid>
		<description><![CDATA[Предисловие
В этом цикле статей я хочу рассказать о таком замечательном фреймворке, как yii. Предполагается, что у читателя есть некоторый навык работы с php5, особенно с его объектно-ориентированной частью. Я буду стараться охватить как можно больше разных аспектов создания веб-приложений на базе yii, добавляя от себя разные тонкости и хитрости; но не ждите от меня копипаста [...]]]></description>
			<content:encoded><![CDATA[<h2><strong>Предисловие</strong></h2>
<p>В этом цикле статей я хочу рассказать о таком замечательном фреймворке, как yii. Предполагается, что у читателя есть некоторый навык работы с <a href="http://php.net">php5</a>, особенно с его объектно-ориентированной частью. Я буду стараться охватить как можно больше разных аспектов создания веб-приложений на базе yii, добавляя от себя разные тонкости и хитрости; но не ждите от меня копипаста документации или api. И то и другое Вы можете найти на официальном сайте.</p>
<h2><strong>Введение</strong></h2>
<p>Что такое Yii?</p>
<p>Yii это свободный(распространяется под <a href="http://yiiframework.com/license/">new BSD licence</a>) высокопроизводительный объектно-ориентированный расширяемый php-фрэймворк для разработки веб-приложений.<br />
<span id="more-418"></span><br />
Зачем он нужен?</p>
<p>Для удобной разработки веб-приложений и чтобы не изобретать сто раз изобретенный велосипед.</p>
<h3>Основные особенности :</h3>
<ul>
<li>MVC-архитектура</li>
<li>Работа с базами данных</li>
<li>Поддержка кэширования на разных уровнях</li>
<li>Большое количество встроенных компонентов</li>
<li>Возможность простого подключения сторонних библиотек</li>
<li>Интеграция с jQuery</li>
</ul>
<h2><strong>Подготовка:</strong></h2>
<p>Системные требования:</p>
<ul>
<li>Веб-сервер(рекомендуется Apache)</li>
<li>PHP версии не ниже 5.1.0</li>
<li>PDO-совместимая СУБД (MySql, PostgreSQL, SqLite, Microsoft SQL, Oracle8) и соответствующее php-расширение для её поддержки.</li>
</ul>
<p>Рекомендуемые расширения для php:</p>
<ul>
<li>gd – для работы с графикой.</li>
<li>mcrypt – для функций кодирования.</li>
<li>MemCache  или APC – для кэширования, ускорения работы.</li>
</ul>
<p>Такой нехитрый набор в наше время можно найти почти на любом хостинге.</p>
<p>Для домашнего тестирования на linux можно установить пакеты из стандартных репозитариев.Если же Вы предпочитаете на windows, могу порекомендовать в качестве готового решения <a href="http://denwer.ru">denwer</a>.</p>
<p>Для пользователей денвера:</p>
<p>(В данном случае, Z: это диск, монтируемый денвером)<br />
1. Для начала прописываем в переменные окружения путь  Z:\usr\local\php5\<br />
2. Открываем /usr/local/php5/php.ini, ищем строчку extension_dir = &laquo;/usr/local/php5/ext&raquo;, заменяем на extension_dir = &laquo;Z:\usr\local\php5\ext&raquo;<br />
3. session.save_path = &laquo;/tmp&raquo; на session.save_path = &laquo;Z:\tmp&raquo;</p>
<h2><strong>Установка:</strong></h2>
<p>На момент написания статьи, последняя стабильная версия была 1.1.0. Скачать её можно в виде архива по адресу  <a title="http://yiiframework.com/download" href="http://yiiframework.com/download" target="_blank">http://yiiframework.com/download</a><br />
Вся установка сводится к распаковке содержимого архива.Для начала рекомендую распаковать всё в директорию доступную из веб и открыть папку requirments в браузере, чтобы убедиться в наличии всех необходимых компонентов.</p>
<p>Для создания приложения необходимо использовать коммандную строку(ssh при удалённом доступе). Нужно запустить файл yiic.bat(для windows) или yiic(для linux и других unix-based систем) с параметрами webapp /full/path/to/new/wbebapp/.<br />
Например :</p>
<blockquote><p><em>$ /var/www/framework/yiic webapp /var/www/testing</em></p></blockquote>
<p>Убедитесь, что у вас есть права записи в  директорию, где Вы хотите создать веб-приложение и она доступна из web. Можно зайти в нее через браузер и увидеть возможности, предоставлямые yii «из коробки».</p>
<p>Практикой мы займемся в следующей статье, а теперь немного теории.</p>
<p><strong>Модель – Представление – Контроллер(MVC)</strong> – это модульная архитектура, применяющийся в веб-программировании(в частности и в yii). Она направлена на разделение логики(контроллер), управления данными(модель) и пользовательского интерфейса(представление) с целью возможности изменять одни части не внося изменения в другие.</p>
<h3>Основные части, из которых состоит yii:</h3>
<blockquote><p><span style="color: #000000;">В yii имена всех системных классов начинается с перфикса C , с целью недопущения коллизии имён. По этому, пожалуйста, воздержитесь от использования этого префикса в своих классах.</span></p></blockquote>
<p><strong>Входной файл</strong><br />
Входной файл(обычно index.php) это единственный скрипт, с которым контактирует пользователю. Вобщем-то, всё что он делает, это читает конфигурацию и создаёт экземпляр класса CWebApplication.<br />
<strong>Приложение(application)</strong><br />
Приложение(экземпляр класса CWebApplication) обрабатывает запрос пользователя, выполняет роутинг и передаёт его на исполнение соответствующему контроллеру.<br />
<strong>Контроллер(controller)</strong><br />
Контроллер(экзэмпляр класса CController или производного от него) определяет основную логику приложения, взаимодействует с моделями и отображениями. Контроллеры обычно находятся в папке /protected/controllers. По соглашению, класс контроллера и имя файла с ним : NameController.php<br />
<strong>Действие(action)</strong><br />
Действие это тип действия, выполняемого пользователем, например просмотр статьи в блоге или отправка комментария. Обычно является методом контроллера(вида actionName), но может быть вынесенно в отдельный класс.<br />
<strong>Модель (model)</strong><br />
Модель представляет собой сущность данных. Например таблица в базе данных(CActiveRecord) или форма на веб-странице(CForm). Она занимается непосредственной обработкой данных: созданием, получением, изменением. Также представляет интерфейс доступа к валидаторам(validator), проверяющим корректность введённых пользователем данных. Модели находятся в папке /protected/models. Модель обычно имеет имя, соответствующее таблице в бд, которую она представляет.<br />
<strong>Представление(view)</strong><br />
Представление генерирует готовые части страницы, отдаваемой пользователю.Оно не использует логики, кроме как условий и циклов. Представления для каждого контроллера обычно раздельны и хранятся в /protected/views/controllername/.<br />
<strong>Макет (layout)</strong><br />
Макет это специальное представление для вставки других представлений. Обычно он содержит части пользовательского интерфейса, используемого другими представлениями. Например, основной макет может содержать в себе шапку и подвал страницы, места для подключения других представлений и виджетов. Макеты храняться в /protected/views/layouts. По умолчанию используется макет main.php.<br />
<strong>Виджет(widget)</strong><br />
Виджет это компонент для генерации самодостаточного элемента пользовательского интерфейса(например, верхнее меню в тестовом приложении). В zii (библиотеке расширений, поставляемых вместе с yii) имеется большое количество готовых виджетов.<br />
<strong>Компонент(component)</strong><br />
Представляет сущность какого-либо аспекта веб-приложения. Например, авторизация пользователей. В yii много готовых компонентов, которые могут использоваться как в готовом виде, так и расширяться веб-разработчиками.<br />
<strong>Модуль(module)</strong><br />
Модуль, это самодостаточная единица, состоящая из контроллеров, моделей, отображений, и.т.д. Модули используются для разделения сайта на логические части, например форум и блог.</p>
<h2><strong>Заключение</strong></h2>
<p>В первой статье мы узнали, что такое yii, познакомились с базовыми понятиями и подготовили полигон для дальнейшего изучения.</p>
<p>И на последок хочу дать несколько полезных ссылок:</p>
<p><a href="http://yiiframework.com" target="_blank">Оффициальный сайт (en)</a></p>
<p><a href="http://yiiframework.ru" target="_blank">Русскоязычное сообщество (ru)</a></p>
<p><a href="http://dbhelp.ru/" target="_blank">Блог, посвящённый yii (ru) </a></p>
<h2><strong>В следующей статье:</strong></h2>
<p>В следующей статье мы познокомимся со внутренним устройством фреймворка, научимся делать собственные контроллеры, модели и отображения, изучим основы ActiveRecord.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.linkexchanger.su/2010/418.html/feed</wfw:commentRss>
		<slash:comments>32</slash:comments>
		</item>
	</channel>
</rss>

