<?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>CTAPbIu_MABP&#039;s BLOG &#187; combobox</title>
	<atom:link href="http://mabp.kiev.ua/tag/combobox/feed/" rel="self" type="application/rss+xml" />
	<link>http://mabp.kiev.ua</link>
	<description>Не вижу проблем, кроме лени! &#169; Старый Мавр</description>
	<lastBuildDate>Tue, 07 Sep 2010 20:05:40 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.1</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>JComboBox Demo</title>
		<link>http://mabp.kiev.ua/2009/12/04/jcombobox-demo/</link>
		<comments>http://mabp.kiev.ua/2009/12/04/jcombobox-demo/#comments</comments>
		<pubDate>Fri, 04 Dec 2009 18:00:10 +0000</pubDate>
		<dc:creator>CTAPbIu_MABP</dc:creator>
				<category><![CDATA[JAVA]]></category>
		<category><![CDATA[Программирование]]></category>
		<category><![CDATA[autocomplete]]></category>
		<category><![CDATA[combobox]]></category>
		<category><![CDATA[swing]]></category>

		<guid isPermaLink="false">http://mabp.kiev.ua/?p=1142</guid>
		<description><![CDATA[Пока я сижу без реального проекта вдохновение брать неоткуда :( Поэтому постить тут буду что есть, а есть первый толковый вопрос по Java с форума

Задание было дизейблить кнопку если в комбике ничего не введено Swing конечно не jQuery но на нем тоже можно делать UI





package ua.kiev.mabp;


import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.GridBagLayout;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.Vector;


/**
 [...]]]></description>
			<content:encoded><![CDATA[<p>Пока я сижу без реального проекта вдохновение брать неоткуда :( Поэтому постить тут буду что есть, а есть первый толковый вопрос по <a href="http://mabp.kiev.ua/category/programming/java/">Java</a> с <a href="http://pyha.ru/forum/" rel="nofollow external">форума</a>
</p>
<p>Задание было дизейблить кнопку если в комбике ничего не введено Swing конечно не jQuery но на нем тоже можно делать UI</p>
<span id="more-1142">
</span>
<pre>
<code class="java">

package ua.kiev.mabp;


import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.GridBagLayout;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.Vector;


/**
 * Created by IntelliJ IDEA.
 * User: CTAPbIu_MABP
 * Date: 30.11.2009
 * Time: 18:48:00
 */


public class ComboBoxEventDemo extends JFrame {


    public ComboBoxEventDemo(String name) {
        super(name);
    }


    public void addComponentsToPane(final Container pane) {


        String comboPrefix = "ComboBox Item #";
        int numItems = 15;
        Vector&lt;String&gt; vector = new Vector&lt;String&gt;(numItems);
        for (int i = 0; i &lt; numItems; i++) {
            vector.addElement(comboPrefix + i);
        }


        final JComboBox searchCombo = new JComboBox(vector);
        searchCombo.setEditable(true);
        final JButton okButton = new JButton("OK");


        searchCombo.getEditor().getEditorComponent().addKeyListener(new KeyListener() {
            public void keyTyped(KeyEvent e) {
                System.out.print("keyTyped");
            }


            public void keyPressed(KeyEvent e) {
                System.out.print("keyPressed");
            }


            public void keyReleased(KeyEvent e) {
                System.out.print("keyReleased");
                try {
                    String s = (String) searchCombo.getSelectedItem();
                    okButton.setEnabled(s != null &#038;&#038; !s.isEmpty());
                }
                catch (NullPointerException npe) {
                    okButton.setEnabled(false);
                }
            }
        });


        add(searchCombo);
        add(okButton);


        pane.setLayout(new GridBagLayout());
        setPreferredSize(new Dimension(450, 450));
    }


    public static void main(String[] args) {
        javax.swing.SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                FocusEventDemo frame = new ComboBoxEventDemo("JComboBox Demo");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.addComponentsToPane(frame.getContentPane());
                frame.pack();
                frame.setVisible(true);
            }
        });
    }
}

</code>
</pre>
<p>Я прекрасно понимаю что это гавно никому не надо не мне ни вам, хотя кто знает может кому-то когда-то пригодится, а пишу я это для того чтоб вы не забывали что я еще есть и жив, зашли на мой сайт оставить камент типа "ну и дерьмо же ты постишь, чувак, совсем хуевый стал, отпишусь ка я от твоего RSS" и тем самым накрутили бы мне счетчик посещаймости :D</p>
<p>Ладно заспойлю вам кое-что, может не отпишитесь от моего RSS. В ближайшее время будет статья о векторной графике с красивым примером.</p>]]></content:encoded>
			<wfw:commentRss>http://mabp.kiev.ua/2009/12/04/jcombobox-demo/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Cascading comboboxes on GWT, ExtGWT, ExtJS и MySQL</title>
		<link>http://mabp.kiev.ua/2009/06/12/cascading-comboboxes-on-gwt-extgwt-extjs-and-mysql/</link>
		<comments>http://mabp.kiev.ua/2009/06/12/cascading-comboboxes-on-gwt-extgwt-extjs-and-mysql/#comments</comments>
		<pubDate>Fri, 12 Jun 2009 12:27:44 +0000</pubDate>
		<dc:creator>CTAPbIu_MABP</dc:creator>
				<category><![CDATA[JAVA]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[SQL]]></category>
		<category><![CDATA[Программирование]]></category>
		<category><![CDATA[combobox]]></category>
		<category><![CDATA[ExtJS]]></category>
		<category><![CDATA[GWT]]></category>

		<guid isPermaLink="false">http://mabp.kiev.ua/?p=1078</guid>
		<description><![CDATA[Я уже давно грозился выложить свежую версию каскадных выпадающих списков на GWT 1.5.3, ExtGWT 1.2, ExtJS 2.2.1 и MySQL 5.1. Код был написан еще в конце апреля, но меня все ломало написать к нему текстовку. А сейчас когда на подходе выход ExtGWT 2.0 и ExtJS 3.0 я решил пошевелиться и накрапать небольшое описалово. Действительно небольшое [...]]]></description>
			<content:encoded><![CDATA[<p>Я уже давно грозился выложить свежую версию каскадных выпадающих списков на GWT 1.5.3, ExtGWT 1.2, ExtJS 2.2.1 и MySQL 5.1. Код был написан еще в конце апреля, но меня все ломало написать к нему текстовку. А сейчас когда на подходе выход ExtGWT 2.0 и ExtJS 3.0 я решил пошевелиться и накрапать небольшое описалово. Действительно небольшое потому что все настройки я уже описал в <a href="http://mabp.kiev.ua/2009/04/12/gwt-extjs-connecting-to-mysql/">предыдущей статье</a>, так что тут будут описаны только изменения.</p>
<span id="more-1078">
</span>
<p>Для начала опишу изменения в БД, я ее полностью поменял. Теперь у нас вместо машинок будут города и страны, но смысл тот же.</p>
<pre>
<code class="sql">

DROP TABLE IF EXISTS `city`;
CREATE TABLE IF NOT EXISTS `city` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(255) NOT NULL,
  `parent` int(10) unsigned NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=latin1 AUTO_INCREMENT=50 ;


INSERT INTO `city` (`id`, `name`, `parent`) VALUES
(1, 'Kiev', 1),
(2, 'Lviv', 1),
(3, 'Odesa', 1),
(4, 'Kharkov', 1),
(5, 'Lugansk', 1),
(6, 'Zaporizhzhia', 1),
(7, 'Mariupol', 1),
(8, 'Moskow', 2),
(9, 'Volgograd', 2),
(10, 'Samara', 2),
(11, 'Kazan', 2),
(12, 'Perm', 2),
(13, 'Eburg', 2),
(14, 'Chelyabinsk', 2),
(15, 'Minsk', 3),
(16, 'Homel', 3),
(17, 'Grodno', 3),
(18, 'Vitebsk', 3),
(19, 'Le Havre', 4),
(20, 'Rennes', 4),
(21, 'Nantes', 4),
(22, 'Berdeaux', 4),
(23, 'Toulouse', 4),
(24, 'Montpellier', 4),
(25, 'Marseille', 4),
(26, 'Lyon', 4),
(27, 'Paris', 4),
(28, 'Reims am Main', 5),
(29, 'Berlin', 5),
(30, 'Bremen', 5),
(31, 'Hannover', 5),
(32, 'Hamburg', 5),
(33, 'Frankfurt', 5),
(34, 'Munchen', 5),
(35, 'Strasbourg', 5),
(36, 'Milano', 6),
(37, 'Bologna', 6),
(38, 'Genova', 6),
(39, 'Roma', 6),
(40, 'Napoli', 6),
(41, 'Palermo', 6),
(42, 'London', 7),
(43, 'Bristol', 7),
(44, 'Birmingham', 7),
(45, 'Liverpool', 7),
(46, 'Manchester', 7),
(47, 'Edinburg', 7),
(48, 'Glasgow', 7),
(49, 'Belfast', 7);


DROP TABLE IF EXISTS `country`;
CREATE TABLE IF NOT EXISTS `country` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(255) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=latin1 AUTO_INCREMENT=8 ;


INSERT INTO `country` (`id`, `name`) VALUES
(1, 'Ukrain'),
(2, 'Russia'),
(3, 'Belarus'),
(4, 'France'),
(5, 'German'),
(6, 'Italia'),
(7, 'Uniited Kingdom');

</code>
</pre>
<p>И EntryPoint у нас на этот раз будет по проще. В нем не будет кучи виджетов, а только один.</p>
<pre>
<code class="java">

package ua.kiev.mabp.client;


import com.extjs.gxt.ui.client.Events;
import com.extjs.gxt.ui.client.Style.Scroll;
import com.extjs.gxt.ui.client.event.BaseEvent;
import com.extjs.gxt.ui.client.event.Listener;
import com.extjs.gxt.ui.client.widget.ContentPanel;
import com.extjs.gxt.ui.client.widget.Viewport;
import com.extjs.gxt.ui.client.widget.form.FormPanel;
import com.extjs.gxt.ui.client.widget.layout.FitLayout;
import com.extjs.gxt.ui.client.widget.layout.FlowLayout;
import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.user.client.ui.RootPanel;
import ua.kiev.mabp.client.utility.CityToCountryUtility;
import ua.kiev.mabp.client.widget.CitySuggestBox;
import ua.kiev.mabp.client.widget.CountrySuggestBox;


public class HelloWorld implements EntryPoint {


    public void onModuleLoad() {


        // создаем рабочую область
        ContentPanel contentPanel = new ContentPanel();
        contentPanel.setLayout(new FitLayout());
        contentPanel.setHeading("Cascading combobox");


        // и новый комбобокс для стран
        CountrySuggestBox countrySuggestBox = new CountrySuggestBox();
        countrySuggestBox.setDisplayField("name");
        countrySuggestBox.setFieldLabel("Country");


        // вот тут вся магия
        CityToCountryUtility.getInstance().setCountryComboBox(countrySuggestBox);


        // ну и листенер конечно
        countrySuggestBox.addListener(Events.SelectionChange, new Listener&lt;BaseEvent&gt;(){
            public void handleEvent(BaseEvent be) {
                CityToCountryUtility.getInstance().eraseCity();
            }
        });


        // новый комбобокс для городов
        CitySuggestBox citySuggestBox = new CitySuggestBox();
        citySuggestBox.setDisplayField("name");
        citySuggestBox.setFieldLabel("City");
        citySuggestBox.setForceSelection(false);


        // еще немного магии
        CityToCountryUtility.getInstance().setCityComboBox(citySuggestBox);


        // дальше манипуляции с отображением 
        FormPanel formPanel = new FormPanel();
        formPanel.setHeaderVisible(false);
        formPanel.add(countrySuggestBox);
        formPanel.add(citySuggestBox);


        contentPanel.add(formPanel);


        Viewport viewport = new Viewport();
        viewport.setLayout(new FlowLayout());
        viewport.setScrollMode(Scroll.AUTO);
        viewport.add(contentPanel);
        RootPanel.get().add(viewport);
    }
}

</code>
</pre>
<p>Тут интересны два, точнее три объекта, CountrySuggestBox, CitySuggestBox и CityToCountryUtility, но два практически одинаковые, поэтому я буду рассказывать только про больший.</p>
<pre>
<code class="java">

package ua.kiev.mabp.client.widget;


import com.extjs.gxt.ui.client.data.BasePagingLoadConfig;
import com.extjs.gxt.ui.client.data.BasePagingLoadResult;
import com.extjs.gxt.ui.client.data.ModelData;
import com.extjs.gxt.ui.client.data.RpcProxy;
import ua.kiev.mabp.client.proxy.CitySuggestBoxRpcProxy;


/**
 * Created by IntelliJ IDEA.
 * User: CTAPbIu_MABP
 * Date: 16.04.2009
 * Time: 22:46:56
 */
public class CitySuggestBox&lt;D extends ModelData&gt; extends AbstractSuggestBox&lt;D&gt; {


    @Override
    protected RpcProxy&lt;BasePagingLoadConfig, BasePagingLoadResult&lt;D&gt;&gt; getRpcProxy() {
        return new CitySuggestBoxRpcProxy&lt;BasePagingLoadConfig, BasePagingLoadResult&lt;D&gt;&gt;();
    }
}

</code>
</pre>
<p>CitySuggestBox и CountrySuggestBox всеголишь обертки, поэтому показываю AbstractSuggestBox.</p>
<pre>
<code class="java">

package ua.kiev.mabp.client.widget;


import com.extjs.gxt.ui.client.data.*;
import com.extjs.gxt.ui.client.store.ListStore;
import com.extjs.gxt.ui.client.widget.form.ComboBox;
import com.google.gwt.user.client.Element;


/**
 * Created by IntelliJ IDEA.
 * User: CTAPbIu_MABP
 * Date: 16.04.2009
 * Time: 22:46:09
 */


public abstract class AbstractSuggestBox&lt;D extends ModelData&gt; extends ComboBox&lt;D&gt; {


    public AbstractSuggestBox() {
        RpcProxy&lt;BasePagingLoadConfig, BasePagingLoadResult&lt;D&gt;&gt; proxy = getRpcProxy();
        PagingLoader&lt;BasePagingLoadConfig&gt; bpl = new BasePagingLoader&lt;BasePagingLoadConfig, BasePagingLoadResult&lt;D&gt;&gt;(proxy);
        ListStore&lt;D&gt; store = new ListStore&lt;D&gt;(bpl);
        setStore(store);
        setTypeAhead(true);
        setForceSelection(true);
        setPageSize(5);
        setWidth("500px");
    }


    protected abstract RpcProxy&lt;BasePagingLoadConfig, BasePagingLoadResult&lt;D&gt;&gt; getRpcProxy();


    @Override
    protected void onRender(Element parent, int index) {
        setEditable(true);
        setTriggerAction(TriggerAction.QUERY);
        super.onRender(parent, index);
    }
}

</code>
</pre>
<p>Тут тоже практически ничего интересного - главное получить правильно параметризированый RpcProxy. Дальше дело техники. Прокси на самом деле тоже только обертка.</p>
<pre>
<code class="java">

package ua.kiev.mabp.client.proxy;


import com.extjs.gxt.ui.client.data.BasePagingLoadConfig;
import com.extjs.gxt.ui.client.data.RpcProxy;
import com.extjs.gxt.ui.client.widget.Info;
import com.google.gwt.user.client.rpc.AsyncCallback;
import ua.kiev.mabp.client.HelloWorldService;
import ua.kiev.mabp.client.HelloWorldServiceAsync;
import ua.kiev.mabp.client.utility.CityToCountryUtility;


/**
 * Created by IntelliJ IDEA.
 * User: CTAPbIu_MABP
 * Date: 16.04.2009
 * Time: 22:51:48
 */
public class CitySuggestBoxRpcProxy&lt;C extends BasePagingLoadConfig, D&gt; extends RpcProxy&lt;C, D&gt; {


    final HelloWorldServiceAsync service = HelloWorldService.App.getInstance();


    @Override
    protected final void load(C loadConfig, final AsyncCallback&lt;D&gt; asyncCallback) {
        String parernt = CityToCountryUtility.getInstance().getParentForCity();
        loadConfig.getParams().put("parent", parernt);
        service.getCity(loadConfig, new AsyncCallback&lt;D&gt;() {
            public void onSuccess(D result) {
                Info.display("Success", "success");
                asyncCallback.onSuccess(result);
            }


            public void onFailure(Throwable caught) {
                Info.display("Failure", "fail");
                asyncCallback.onFailure(caught);
            }


        });
    }
}

</code>
</pre>
<p>Но только эта обертка для городов имеет две лишних строки (по сравнению с оберткой для стран) в которых происходит получение выбранной страны и подставления этого значения в параметры запроса. Для этого я использую класс CityToCountryUtility, который реализует паттерн Registry.</p>
<pre>
<code class="java">

package ua.kiev.mabp.client.utility;


import com.extjs.gxt.ui.client.data.ModelData;
import ua.kiev.mabp.client.widget.CitySuggestBox;
import ua.kiev.mabp.client.widget.CountrySuggestBox;


/**
 * Created by IntelliJ IDEA.
 * User: CTAPbIu_MABP
 * Date: 28.04.2009
 * Time: 15:20:29
 */
public class CityToCountryUtility {


    private static CityToCountryUtility manager;
    private CountrySuggestBox country;
    private CitySuggestBox city;


    private CityToCountryUtility() {


    }


    public static CityToCountryUtility getInstance() {
        if (manager == null) {
            manager = new CityToCountryUtility();
        }
        return manager;
    }


    public void setCountryComboBox(CountrySuggestBox country) {
        this.country = country;
    }


    public void eraseCity() {
        //city.clearSelections();
        city.getStore().removeAll();
    }


    public void setCityComboBox(CitySuggestBox city) {
        this.city = city;
    }


    public String getParentForCity() {
        ModelData value = country.getValue();
        return value != null ? (String) value.get("abbr") : "";
    }
}

</code>
</pre>
<p>Пожалуй это все что надо для того, чтобы сделать каскадные комбобоксы. Повторюсь что как настроить мавен, какие либы подключать и как замапить комбобокс к базе данных описано в <a href="http://mabp.kiev.ua/2009/04/12/gwt-extjs-connecting-to-mysql/">предыдущей статье</a>. Осталось только прикрепить <a href="http://mabp.kiev.ua/content/source/cascading_combobox.rar">проект</a>.</p>
<br />
]]></content:encoded>
			<wfw:commentRss>http://mabp.kiev.ua/2009/06/12/cascading-comboboxes-on-gwt-extgwt-extjs-and-mysql/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Обновил автокомплит</title>
		<link>http://mabp.kiev.ua/2009/05/16/autocomplete-update-2/</link>
		<comments>http://mabp.kiev.ua/2009/05/16/autocomplete-update-2/#comments</comments>
		<pubDate>Sat, 16 May 2009 13:49:11 +0000</pubDate>
		<dc:creator>CTAPbIu_MABP</dc:creator>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[Программирование]]></category>
		<category><![CDATA[autocomplete]]></category>
		<category><![CDATA[combobox]]></category>

		<guid isPermaLink="false">http://mabp.kiev.ua/?p=1069</guid>
		<description><![CDATA[Решил поправить несколько надоедливых багов в автокомплите. Обновления прошли под принципом KISS, чем меньше кода добавлено для получения максимальной гибкости, тем лучше!



Поправлена кнопка таб, теперь при ее нажатии происходит выбор текущего элемента и переход курсора в следующее поле ввода.
Добавлена возможность использовать кастомный формат данных переданных с бэкенда. Например можно использовать вложенные массивы [{a:'a'},{b:'b'}]. Для этого [...]]]></description>
			<content:encoded><![CDATA[<p>Решил поправить несколько надоедливых багов в <a href="http://mabp.kiev.ua/2008/04/08/autocomplete/">автокомплите</a>. Обновления прошли под принципом KISS, чем меньше кода добавлено для получения максимальной гибкости, тем лучше!</p>
<span id="more-1069">
</span>
<ul>
<li>Поправлена кнопка таб, теперь при ее нажатии происходит выбор текущего элемента и переход курсора в следующее поле ввода.</li>
<li>Добавлена возможность использовать кастомный формат данных переданных с бэкенда. Например можно использовать вложенные массивы [{a:'a'},{b:'b'}]. Для этого надо использовать параметр dataHandler передавая в него функцию для обработки данных, пример функции смотрите в <a href="http://mabp.kiev.ua/2008/04/08/autocomplete/">example 12</a>
</li>
<li>Добавлена поддержка каскадных списков, для этого в параметр url теперь можно передавать функцию, которая будет возвращать новую ссылку, следите за тем чтобы функция очищала кеш.</li>
<li>Для неполных списков, добавлен флаг partial который будет заставлять автокомплит делать запрос на сервер каждый раз как введена следующая буква слова.</li>
</ul>]]></content:encoded>
			<wfw:commentRss>http://mabp.kiev.ua/2009/05/16/autocomplete-update-2/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>GWT+EXTJS подключаем MySQL</title>
		<link>http://mabp.kiev.ua/2009/04/12/gwt-extjs-connecting-to-mysql/</link>
		<comments>http://mabp.kiev.ua/2009/04/12/gwt-extjs-connecting-to-mysql/#comments</comments>
		<pubDate>Sun, 12 Apr 2009 14:22:40 +0000</pubDate>
		<dc:creator>CTAPbIu_MABP</dc:creator>
				<category><![CDATA[JAVA]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[SQL]]></category>
		<category><![CDATA[Программирование]]></category>
		<category><![CDATA[combobox]]></category>
		<category><![CDATA[ExtJS]]></category>
		<category><![CDATA[GWT]]></category>

		<guid isPermaLink="false">http://mabp.kiev.ua/?p=1051</guid>
		<description><![CDATA[Очень долго, чуть больше недели готовил эту статью, начал с того что написал по статье об установке каждого используемого компонента, а теперь не знаю с чего начать :)


Начну с описания того, что буду делать. Я попробую создать тонкий клиент используя GWT и GWT-Ext. Кто уже на этом месте начал кричать "GWT-Ext устарела надо использовать GXT" [...]]]></description>
			<content:encoded><![CDATA[<p>Очень долго, чуть больше недели готовил эту статью, начал с того что написал по статье об установке каждого используемого компонента, а теперь не знаю с чего начать :)</p>
<span id="more-1051">
</span>
<p>Начну с описания того, что буду делать. Я попробую создать тонкий клиент используя GWT и GWT-Ext. Кто уже на этом месте начал кричать "GWT-Ext устарела надо использовать GXT" может смело закрывать вкладку браузера [x]. Остальные если не знают что такое и как установить <a href="http://mabp.kiev.ua/2009/04/09/install-intellij-idea/">IntelliJ IDEA</a> и <a href="http://mabp.kiev.ua/2009/04/09/install-maven-2/">Maven</a> отвлекаются от статьи на 20 минут на прочтение инструкций и установку софта. Инструкция об установке <a href="http://mabp.kiev.ua/2009/04/10/install-google-web-toolkit/">Google Web Toolkit</a> тоже имеется, а вот по установке MySQL мне было лень писать.</p>
<p>Теперь собственно о тонком клиенте, он будет немного не доделан. Дело в том что в одну статью все не впихнуть и в этой будут продемонстрированы только некоторые части интерфейса, потому что основной темой будет взаимодействие с базой данных MySQL. Все известные мне мануалы рассказываю как передавать данные в GWT используя XML или JSON сгенерированные PHP, а я хочу чтобы GWT получал данные напрямую из базы и передавал на клиент в сериализированом виде.</p>
<p>Если вы хотите попробовать запустить пример вот ссылка для скачивания <a href="http://mabp.kiev.ua/content/source/HelloWorld.zip" rel="nofollow">проекта</a> и исходников <a href="http://yogurtearl.com/ext-2.0.2.zip" rel="nofollow external">ext-js 2.0.2</a>
</p>
<p>Теперь об используемых компанентах: помимо всего прочего я буду использовать Spring Framefork, Hibernate, Log4j, JUnit и базу данных MySQL. Для того чтобы не разруливать немыслимое количество зависимостей в этих компонентах я и предложил вам поставить Maven, а еще он удобен для сборки билда. Я написал конфигурационный файл в который включил все необходимые зависимости и плагины для сборки. Я, если честно, не очень силен в конфигурации Maven'а, поэтому большая часть настроек была скопирована из мануала по GWT. Конфиг очень большой и его нет смысла выкладывать здесь, поэтому чтоб его посмотреть придется скачать проект.</p>
<p>Когда загрузите проект откроете закладку Maven'а и нажмете на кнопку закачать все артефакты</p>
<div style="text-align:center">
<img src="/content/img/gwt_extjs_mysql/maven.jpg" alt="Control Panel" width="260" height="600"/>
</div>
<p>Если у вас медленное соединение или еще какие-то проблемы Maven может закачать не все и отказаться скачивать повторно. В общем я советую сразу через командную строку зайти в папку с проектом и выполнить команду:</p>
<pre>
<code class="bat">

mvn eclipse:eclipse -Declipse.downloadSources=true -DdownloadJavadocs=true

</code>
</pre>
<p>Эта команда скачает плагин для Eclipse, который умеет качать исходники и доки, правда он попутно закачивает еще какие-то свои файлы, а после работы создает файлы проекта Eclipse которые можно смело удалять по завершению загрузки. В общей сложности Maven скачает примерно 120 метров кода и документации.</p>
<p>В начале статьи была ссылка на EXT-JS, нужно скачать именно версию 2.0.2 (или 2.0.5 но тогда вы нарушите лицензию) c более поздними GWT-EXT работать не хочет. EXT-JS нужно распаковать в D:ideaHelloWorldsrcmainjavauakievmabppublicjsext , позже я объясню почему именно сюда.</p>
<p>Теперь когда у вас есть все исходники, нужно настроить базу данных, откройте файл D:ideaHelloWorldsrcmainresourcesdb.properties и начинайте править настройки подключения.</p>
<pre>
<code class="ini">

jdbc.driverClassName=com.mysql.jdbc.Driver			<- не менять
jdbc.url=jdbc:mysql://localhost:3306/helloworld		<- helloworld это имя базы
jdbc.username=root						<- пользователь
jdbc.password=qwerty					<- пароль
hibernate.dialect=org.hibernate.dialect.MySQL5Dialect	<- диалект
hibernate.default_schema=helloworld			<- схема, можно не трогать

</code>
</pre>
<br />
Первую строку строгать не надо, во второй, после последнего слеша надо написать название БД, в третей - имя пользователя, в четвертой - пароль, разрабатывал на MySQL 5.1 если у вас 4 то в пятой надо убрать цифру 5, шестую можно не трогать. Структура таблицы и дамп данных наже:</p>
<pre>
<code class="sql">

SET SQL_MODE="NO_AUTO_VALUE_ON_ZERO";


CREATE TABLE IF NOT EXISTS `car` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `model` varchar(255) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 AUTO_INCREMENT=43 ;


INSERT INTO `car` (`id`, `model`) VALUES
(1, 'Audi'),
(2, 'BMW'),
(3, 'Lexus'),
(4, 'Ford'),
(5, 'Honda'),
(6, 'Chevrolet'),
(7, 'KIA'),
(8, 'Mercedes'),
(9, 'Mitsubishi'),
(10, 'Nissan'),
(11, 'Opel'),
(12, 'Rover'),
(13, 'Subaru'),
(14, 'Toyota'),
(15, 'Volkswagen'),
(16, 'Volvo'),
(17, 'Mazda'),
(18, 'Skoda'),
(19, 'Lincoln'),
(20, 'Infiniti'),
(21, 'Cadillac'),
(22, 'Renault'),
(23, 'Daevoo'),
(24, 'Jeep'),
(25, 'Pontiac'),
(26, 'Hummer'),
(27, 'Bentley'),
(28, 'Isuzu'),
(29, 'Seat'),
(30, 'Fiat'),
(31, 'GMC'),
(32, 'Land Rover'),
(33, 'Porsche'),
(34, 'Jaguar'),
(35, 'Hyundai'),
(36, 'Peugeot'),
(37, 'Aston Martin'),
(38, 'Alfa Romeo'),
(39, 'Dodge'),
(40, 'Ferrari'),
(41, 'Saab'),
(42, 'Chrysler');

</code>
</pre>
<p>Все с базой почти закончили. Теперь надо зайти в Tools->Data Source (в восьмой идеи это панелька сбоку а в седьмой новое окно) и добавить новый источник данных как показано на скриншоте:</p>
<div style="text-align:center">
<img src="/content/img/gwt_extjs_mysql/datasouce.jpg" alt="Control Panel" width="554" height="694"/>
</div>
<p>И наконец надо закачать данные о структуре при помощи кнопки Refresh Tables. Вот теперь с базой все.</p>
<p>Предполагается что вы все делали как я описывал в статьях по установке и все находиться на диске D:Program Files , если нет заходите в Project Structure и настраивайте </p>
<p>В закладке GWT указываете путь к GWT, после этого нажимать на кнопку FIX не нужно, потому что у вас dll'ки лежат в одной директории (D:Program Filesgwt-windows-1.5.3), а jar'ники в другой (D:Program Filesapache-maven-2.1.0repocomgooglegwtgwt-user1.5.3).</p>
<div style="text-align:center">
<img src="/content/img/gwt_extjs_mysql/structure-gwt.jpg" alt="Control Panel" width="500" height="300"/>
</div>
<p>В закладке Spring скорее всего ничего править не придется потому что все три конфига подтянутся сами, но проверьте на всякий случай.</p>
<div style="text-align:center">
<img src="/content/img/gwt_extjs_mysql/structure-spring.jpg" alt="Control Panel" width="500" height="300"/>
</div>
<p>В закладке JPA подключите только что созданный источник данных.</p>
<div style="text-align:center">
<img src="/content/img/gwt_extjs_mysql/structure-jpa.jpg" alt="Control Panel" width="500" height="300"/>
</div>
<p>В закладке WEB тоже ничего делать не надо там все само подхватывается.</p>
<div style="text-align:center">
<img src="/content/img/gwt_extjs_mysql/structure-web.jpg" alt="Control Panel" width="500" height="300"/>
</div>
<p>Возвращайтесь в корень настроек проекта, там три закладки, на первой (Sources) отметьте папки с исходниками (main/java), тестами (test/java)</p>
<div style="text-align:center">
<img src="/content/img/gwt_extjs_mysql/structure-sources.jpg" alt="Control Panel" width="500" height="300"/>
</div>
<p>На второй (Paths) кажется ничего делать не надо</p>
<div style="text-align:center">
<img src="/content/img/gwt_extjs_mysql/structure-paths.jpg" alt="Control Panel" width="500" height="300"/>
</div>
<p>А на закладке (Dependencies) проверьте чтоб не было пакета gwt-user (это значит вы все-таки нажали на кнопку FIX на закладке GWT) и был файл javaee.jar</p>
<div style="text-align:center">
<img src="/content/img/gwt_extjs_mysql/structure-dependencies.jpg" alt="Control Panel" width="500" height="300"/>
</div>
<p>Так все настройки закончили переходим к запуску программы.</p>
<p>Maven и GWT навязываю нам свою структуру проекта. От Maven нам достается разделение кода на исходники (main) и тесты (test), а от GWT три пакета: client - то что в последствии станет javascript кодом, public - статические ресурсы, сюда кладется ext-js, и server - классы которые работают на сервере, например для работы с БД.</p>
<p>Для минимального запуска нужно всего несколько файлов. Клаcc ua.kiev.mabp.client.HelloWorld расширяет точку входа в приложение, там задаются основные layuot'ы. HelloWorld.css и HelloWorld.html нужны для каркаса с которым будут работать все js функции. В HelloWorld.gwt.xml указываеться какие модули наследует сценарий. И наконец web.xml определяет точку входа (servlet) в приложение.</p>
<p>В примере есть несколько layaut'ов. Первый это выбор страны и города, демонстрирует функционал связанных выпадающих списков. Второй выбор даты, календари тоже должны были не пересекаться но к сожалению в ext-js 2.0.2 этого функционала еще не было (я опишу его в следующей статье где использую ext-js 2.2.1) поэтому использована валидация полей. Выбор марки автомобиля собственно тат самый виджет ради которого вся статья. И последний это выбор цвета, там совсем все просто.</p>
<pre>
<code class="java">

package ua.kiev.mabp.client;


import com.google.gwt.core.client.EntryPoint;
import com.gwtext.client.widgets.Panel;
import com.gwtext.client.widgets.Viewport;
import com.gwtext.client.widgets.form.FieldSet;
import com.gwtext.client.widgets.layout.AnchorLayout;
import com.gwtext.client.widgets.layout.AnchorLayoutData;
import com.gwtext.client.widgets.layout.ColumnLayout;
import com.gwtext.client.widgets.layout.ColumnLayoutData;
import ua.kiev.mabp.client.widgets.CarPanel;
import ua.kiev.mabp.client.widgets.ColorPanel;
import ua.kiev.mabp.client.widgets.DatePanel;
import ua.kiev.mabp.client.widgets.LocationPanel;


/**
 * Created by IntelliJ IDEA.
 * User: CTAPbIu_MABP
 * Date: 10.04.2009
 * Time: 22:58:15
 */


public class HelloWorld implements EntryPoint {


	public void onModuleLoad() {
		Panel panel = new Panel();
		panel.setBorder(false);
		panel.setPaddings(15);
		panel.setLayout(new AnchorLayout());


		panel.add(new FieldSet(), new AnchorLayoutData("100%"));


		Panel wrapperPanel = new Panel();
		wrapperPanel.setBorder(false);


		Panel columnOnePanel = new Panel();
		columnOnePanel.setBorder(false);
		columnOnePanel.setLayout(new AnchorLayout());
		columnOnePanel.add(new LocationPanel(), new AnchorLayoutData("100%"));
		columnOnePanel.add(new DatePanel(), new AnchorLayoutData("100%"));
		columnOnePanel.add(new CarPanel(), new AnchorLayoutData("100%"));


		Panel columnTwoPanel = new Panel();
		columnTwoPanel.setBorder(false);
		columnTwoPanel.setLayout(new AnchorLayout());
		columnTwoPanel.add(new ColorPanel(), new AnchorLayoutData("100%"));


		wrapperPanel.setLayout(new ColumnLayout());
		wrapperPanel.add(columnOnePanel, new ColumnLayoutData(.5));
		wrapperPanel.add(columnTwoPanel, new ColumnLayoutData(.5));


		panel.add(wrapperPanel, new AnchorLayoutData("100%"));
		panel.add(new FieldSet(), new AnchorLayoutData("100%"));


		new Viewport(panel);
	}
}

</code>
</pre>
<p>Немного поигравшись с layaut'ами можно начать прикручивать БД, данные из которой получает выпадающий список.</p>
<pre>
<code class="java">

package ua.kiev.mabp.client.widgets;


import com.gwtext.client.core.Template;
import com.gwtext.client.core.UrlParam;
import com.gwtext.client.data.*;
import com.gwtext.client.widgets.Panel;
import com.gwtext.client.widgets.form.ComboBox;
import com.gwtext.client.widgets.form.FieldSet;
import com.gwtext.client.widgets.form.event.ComboBoxListenerAdapter;
import ua.kiev.mabp.client.proxies.MyProxy;


/**
 * Created by IntelliJ IDEA.
 * User: CTAPbIu_MABP
 * Date: 10.04.2009
 * Time: 22:58:15
 */


public class CarPanel extends FieldSet {
	public CarPanel(){
	super("Select your car");


		// определяе вид записи
		FieldDef[] fieldDefs = new FieldDef[]{new StringFieldDef("id"), new StringFieldDef("model")};
		RecordDef recordDef = new RecordDef(fieldDefs);


		// создаем хранилище
		final Store store = new Store(new MyProxy(), new ArrayReader(recordDef), true);
		store.setBaseParams(new UrlParam[]{new UrlParam("paramName", "paramValue")});


		// создаем новую панель
		// сюда будет дублироваться выбраный результат
		final Panel instructionPanel = new Panel();
		instructionPanel.setBorder(false);
		instructionPanel.setHeight(24);
		instructionPanel.setHtml("Car model doesn't selected yet");


		// создаем новый шаблон
		final Template template = new Template("&lt;div class="x-combo-list-item"&gt;" +
				"&lt;table&gt;&lt;tbody&gt;&lt;tr&gt;" +
				"&lt;td style="width:50px;border-right:2px dotted #A3BAE9;"&gt;{id}&lt;/td&gt;" +
				"&lt;td style="width:200px;border-right:2px dotted #A3BAE9;"&gt;{model}&lt;/td&gt;" +
				"&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;" +
				"&lt;div class="x-clear"&gt;&lt;/div&gt;&lt;/div&gt;");


		// создаем новый выпадающий список 
		// и присваеваем ему хранилище и шаблон
		final ComboBox carComboBox = new ComboBox();
		carComboBox.setMinChars(1);
		carComboBox.setFieldLabel("Label");
		carComboBox.setStore(store);
		carComboBox.setDisplayField("model");
		carComboBox.setMode(ComboBox.LOCAL);
		carComboBox.setTriggerAction(ComboBox.ALL);
		carComboBox.setForceSelection(true);
		carComboBox.setEmptyText("Choose car model");
		carComboBox.setTypeAhead(true);
		carComboBox.setSelectOnFocus(true);
		carComboBox.setResizable(true);
		carComboBox.setTpl(template);
		carComboBox.setHideLabel(true);


		// устанавливаем листенер
		carComboBox.addListener(new ComboBoxListenerAdapter() {
			public void onSelect(ComboBox comboBox, Record record, int index) {
				instructionPanel.setHtml(record.getAsString("id") + " " + record.getAsString("model"));
			}
		});


		// добавляем все в текущий layout
		add(carComboBox);
		add(instructionPanel);


		// загружаем в список 5 первых элементов
		store.load(0, 5);
	}
}



</code>
</pre>
<p>Итак кроме того что виджет CarPanel создает ComboBox с листенером он использует для получения данных прокси MyProxy.</p>
<p>Итак кроме того что виджет CarPanel создает ComboBox с листенером он использует для получения данных прокси MyProxy.</p>
<pre>
<code class="java">

package ua.kiev.mabp.client.proxies;


import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.gwtext.client.core.UrlParam;
import ua.kiev.mabp.client.HelloWorldService;
import ua.kiev.mabp.client.beans.ArrayBean;


/**
 * Created by IntelliJ IDEA.
 * User: CTAPbIu_MABP
 * Date: 10.04.2009
 * Time: 22:58:15
 */


public class MyProxy extends GWTProxy {


	/**
	 * Загружает данные в объект
	 *
	 * @param start позиция первого элемента
	 * @param limit количество элемента
	 * @param sort сортировка
	 * @param dir направление сортировки
	 * @param javaScriptObject объект
	 */
	public void load(int start, int limit, String sort, String dir, JavaScriptObject javaScriptObject) {
		load(start, limit, sort, dir, javaScriptObject, null);
	}


	/**
	 * Загружает данные в объект
	 *
	 * @param start позиция первого элемента
	 * @param limit количество элемента
	 * @param sort сортировка
	 * @param dir направление сортировки
	 * @param o объект
	 * @param baseParams параметры запроса
	 */
	public void load(int start, int limit, String sort, String dir, final JavaScriptObject o, UrlParam[] baseParams) {
		String[][] params = new String[0][];
		HelloWorldService.App.getInstance().getCars(start, limit, sort, dir, params, new AsyncCallback() {
			public void onFailure(Throwable caught) {
				loadResponse(o, false, 0, (JavaScriptObject) null);
			}


			public void onSuccess(Object result) {
				ArrayBean response = (ArrayBean) result;
				loadResponse(o, true, response.totalPages, response.data);
			}
		});
	}
}



</code>
</pre>
<p>Класс MyProxy наследует класс GWTProxy который был выдран мной из проекта gwt-ext-ux (com.gwtextux.client.data.GWTProxy), который я счел не нужным подключать из-за одного класса. Самый большой интерес в классе MyProxy представляет вызов метода HelloWorldService.App.getInstance().getCars() .</p>
<pre>
<code class="java">

package ua.kiev.mabp.client;


import com.google.gwt.core.client.GWT;
import com.google.gwt.user.client.rpc.RemoteService;
import com.google.gwt.user.client.rpc.ServiceDefTarget;
import ua.kiev.mabp.client.beans.ArrayBean;


/**
 * Created by IntelliJ IDEA.
 * User: CTAPbIu_MABP
 * Date: 10.04.2009
 * Time: 22:58:15
 */


public interface HelloWorldService extends RemoteService {


	public static class App {
		private static HelloWorldServiceAsync ourInstance = null;


		public static synchronized HelloWorldServiceAsync getInstance() {
			if (ourInstance == null) {
				ourInstance = (HelloWorldServiceAsync) GWT.create(HelloWorldService.class);
				((ServiceDefTarget) ourInstance).setServiceEntryPoint(GWT.getModuleBaseURL() + "ua.kiev.mabp.HelloWorld/HelloWorldService");
			}
			return ourInstance;
		}
	}


	public ArrayBean getCars(int start, int limit, String sort, String dir, String params[][]);
}

</code>
</pre>
<p>HelloWorldService это интерфейс описанный в пакете client и имеющий реализацию в пакете server. Внутренний класс App знает о бэкенде у которого можно получить данные. Этот бекэнд описывается в конфигах в HelloWorld.gwt.xml и web.xml .</p>
<pre>
<code class="java">

package ua.kiev.mabp.server;


import com.google.gwt.user.server.rpc.RemoteServiceServlet;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import ua.kiev.mabp.server.beans.DaoManager;
import ua.kiev.mabp.server.beans.ArrayHelper;
import ua.kiev.mabp.server.beans.entity.Car;
import ua.kiev.mabp.client.HelloWorldService;
import ua.kiev.mabp.client.beans.ArrayBean;
import java.util.Collection;


/**
 * Created by IntelliJ IDEA.
 * User: CTAPbIu_MABP
 * Date: 10.04.2009
 * Time: 22:58:15
 */


public class HelloWorldServiceImpl extends RemoteServiceServlet implements HelloWorldService {
	DaoManager daoManager = DaoManager.getInstance();


	public ArrayBean getCars(int start, int limit, String sort, String dir, String params[][]) {
		ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath*:ApplicationConfig.xml");
		Collection&lt;Car&gt; col = daoManager.getCarDao().getCarByCriteria(start, limit, "ASC".equals(dir), sort);
		return new ArrayBean(ArrayHelper.collectionToArrayString(col), 30);
	}
}

</code>
</pre>
<p>Реализующий интерфейс HelloWorldService класс HelloWorldServiceImpl, знает о конфигах Spring. Этот конфиг знает о файле db.properties в котором описано как подключиться к базе. Я не буду опубликовывать тут весь конфиг, только самую интересную его часть. </p>
<pre>
<code class="xml">

&lt;bean id="car" class="ua.kiev.mabp.server.beans.dao.CarDAO"&gt;
	&lt;property name="sessionFactory" ref="sessionFactory"/&gt;
&lt;/bean&gt;
&lt;bean id="daoManager" class="ua.kiev.mabp.server.beans.DaoManager" factory-method="getInstance"&gt;
	&lt;property name="carDao" ref="car"/&gt;
&lt;/bean&gt;

</code>
</pre>
<p>Тут описывается что singleton класс DaoManager для своего создания использует метод getInstance и что ему нужно наполнить свойство carDao классом CarDAO</p>
<pre>
<code class="java">

package ua.kiev.mabp.server.beans.dao;


import ua.kiev.mabp.server.beans.entity.Car;


import java.util.List;


/**
 * Created by IntelliJ IDEA.
 * User: CTAPbIu_MABP
 * Date: 10.04.2009
 * Time: 22:58:15
 */


public class CarDAO extends DAOBean  {


	/**
	 * Сохраняет элемент
	 *
	 * @param bean элемент
	 */
	public void save(Car bean) {
		getHibernateTemplate().save(Car.class.getName(), bean);
	}


	/**
	 * Удаляет элемент
	 *
	 * @param bean элемент
	 */
	public void delete(Car bean) {
		getHibernateTemplate().delete(bean);
	}


	/**
	 * Получает список объектов
	 *
	 * @param start позиция первого элемента
	 * @param limit количество элемента
	 * @param sort сортировка
	 * @param fieldName название поля
	 * @return список
	 */
	public List&lt;Car&gt; getCarByCriteria(int start, int limit, boolean sort, String fieldName) {
		return getObjectsByCriteria(start, limit, sort, fieldName, Car.class);
	}


	/**
	 * Получает весь список операций
	 *
	 * @return коллекция операций
	 */
	public List&lt;Car&gt; getAllCar() {
		return getHibernateTemplate().loadAll(Car.class);
	}


	/**
	 * Получает список операция по производству машины
	 *
	 * @param s где произведена
	 * @return коллекция операция
	 */
	public List&lt;Car&gt; getCarByModel(String s) {
		return getHibernateTemplate().findByNamedQueryAndNamedParam("findByModelFromCar", "model", s);
	}


	/**
	 * Получает операции по id
	 *
	 * @param id номер операции
	 * @return коллекция операций
	 */
	public Car getCarById(Integer id) {
		return (Car) getHibernateTemplate().get(Car.class, id);
	}
}



</code>
</pre>
<p>Класс CarDAO содержит метод getCarByCriteria который вызывается в классе HelloWorldServiceImpl. Метод возвращает список бинов Car. Немного отвлекусь и расскажу о дополнительных возможностях. Вместо вызова getCarByCriteria можно вызывать getCarByModel передавая модель машины, это будет полезно в каскадных списках, при этом findByModelFromCar описано в классе Car в виде аннотаций.</p>
<pre>
<code class="java">

package ua.kiev.mabp.server.beans.entity;


import javax.persistence.*;
import java.io.Serializable;


/**
 * Created by IntelliJ IDEA.
 * User: CTAPbIu_MABP
 * Date: 10.04.2009
 * Time: 22:58:15
 */


@Entity
@Table(name = "car")
@NamedQueries({
	@NamedQuery(name = "findByModelFromCar", query = "select t from Car t where t.model=:model order by t.model"),
	@NamedQuery(name = "findByIdFromCar", query = "select t from Car t where t.id=:id")
})
public class Car implements Serializable {
	@Id
	@GeneratedValue(strategy = GenerationType.AUTO)
	@Column(name = "id", unique = true, nullable = false, length = 11)
	private Integer id;
	@Column(name = "model")
	private String model;
	private static final long serialVersionUID = 5664909385244426031L;


	/**
	 * Getter for property 'id'.
	 *
	 * @return Value for property 'id'.
	 */
	public Integer getId() {
		return id;
	}


	/**
	 * Setter for property 'id'.
	 *
	 * @param id Value to set for property 'id'.
	 */
	public void setId(Integer id) {
		this.id = id;
	}


	/**
	 * Getter for property 'model'.
	 *
	 * @return Value for property 'model'.
	 */
	public String getModel() {
		return model;
	}


	/**
	 * Setter for property 'model'.
	 *
	 * @param model Value to set for property 'model'.
	 */
	public void setModel(String model) {
		this.model = model;
	}
}

</code>
</pre>
<p>После того как класс HelloWorldServiceImpl получит коллекцию бинов Car он должен превратить ее в двухмерный массив строк, который после сериализации можно передать клиенту. Все стек распутан, а рассказ подошел к концу. И стоит показать как выглядит то ради чего вы все это читали. </p>
<div style="text-align:center">
<img src="/content/img/gwt_extjs_mysql/result.jpg" alt="Control Panel" width="600" height="570"/>
</div>
<p>Все кто дочитал до конца молодец, а я готовлю следующую статью, до скорых встреч.</p>
<br />
]]></content:encoded>
			<wfw:commentRss>http://mabp.kiev.ua/2009/04/12/gwt-extjs-connecting-to-mysql/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>YUI: autocomplete</title>
		<link>http://mabp.kiev.ua/2006/11/28/yui-autocomplete/</link>
		<comments>http://mabp.kiev.ua/2006/11/28/yui-autocomplete/#comments</comments>
		<pubDate>Tue, 28 Nov 2006 14:25:06 +0000</pubDate>
		<dc:creator>CTAPbIu_MABP</dc:creator>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Программирование]]></category>
		<category><![CDATA[autocomplete]]></category>
		<category><![CDATA[combobox]]></category>
		<category><![CDATA[YUI]]></category>

		<guid isPermaLink="false">http://mabp.kiev.ua/?p=1203</guid>
		<description><![CDATA[Что такое AJAX, наверное, уже знают все. (кто не знает, идет гуглить), А что такое YUI я рассказывал в прошлой статье

Итак, автокомплит это технология, по которой данные взятые, например из аджакса (это совершенно не обязательно) предлагаются пользователю для быстрого заполнения полей формы. Например, пользователь пишет в поле "город" несколько букв - "Сан" ему будут предложены [...]]]></description>
			<content:encoded><![CDATA[<p>Что такое AJAX, наверное, уже знают все. (кто не знает, идет гуглить), А что такое YUI я рассказывал в <a href="http://mabp.kiev.ua/2006/11/27/yui-calendar/">прошлой статье</a>
</p>
<p>Итак, автокомплит это технология, по которой данные взятые, например из аджакса (это совершенно не обязательно) предлагаются пользователю для быстрого заполнения полей формы. Например, пользователь пишет в поле "город" несколько букв - "Сан" ему будут предложены варианты "Санкт-Петербург", "Сан-Франциско" и "Сан Ремо" и пользователь быстро может выбрать мышкой или с клавиатуры то что ему нравится.</p>
<span id="more-1203">
</span>
<p>Проблема один - AJAX. Надо писать frontend (javascript) приложение, для клиента основанное на классе JsHttpRequest и backend (например, на php) приложение с запросом к БД</p>
<p>Проблема два нарисовать красивый выпадающий список с навигацией с клавиатуры. Проблема собственно у ослика (как обычно) потому что он не обрабатывает нажатие стрелочек в отличие от всех других браузеров.</p>
<p>Кстати, как и в прошлой статье, я отнюдь не говорю что это либа это панацея всех проблем! например, она очень тяжелая. Но раз я уже начал писать, а вы читать... продолжим</p>
<p>подключаем либу</p>
<pre>
<code class="html">

&lt;script type="text/javascript" src="lib/yahoo/yahoo/yahoo.js"&gt;&lt;/script&gt;
&lt;script type="text/javascript" src="lib/yahoo/dom/dom.js"&gt;&lt;/script&gt;
&lt;script type="text/javascript" src="lib/yahoo/event/event.js"&gt;&lt;/script&gt;
&lt;script type="text/javascript" src="lib/yahoo/connection/connection.js"&gt;&lt;/script&gt;
&lt;script type="text/javascript" src="lib/yahoo/animation/animation.js"&gt;&lt;/script&gt;
&lt;script type="text/javascript" src="lib/yahoo/autocomplete/autocomplete.js"&gt;&lt;/script&gt;

</code>
</pre>
<p>и css</p>
<pre>
<code class="css">

#ysearchmod {position:relative;padding:1em;}
#firstname_root {z-index:3000;} /* for IE z-index of absolute divs inside relative divs issue */
#secondname_root {z-index:2000;} /* for IE z-index of absolute divs inside relative divs issue */
#lastname_root {z-index:1000;} /* for IE z-index of absolute divs inside relative divs issue */
.yui-ac-content {position:absolute;width:100%;border:1px solid #404040;background:#fff;overflow:hidden;z-index:9050;}
.ysearchquery {position:absolute;right:10px;color:#808080;z-index:10;}
.yui-ac-shadow {position:absolute;margin:.3em;width:100%;background:#a0a0a0;z-index:9049;}
ul {padding:5px 0;width:100%;}
li {padding:0 5px;cursor:default;white-space:nowrap;}
li.yui-ac-highlight {background:#a0a0a0;}
li.yui-ac-prehighlight {background:pink;}

</code>
</pre>
<p>Для тех кто заметил что этот css сильно отличается от предлагаемого YAHOO объясняю - так как они описывают каждый элемент уже при трех полях на ajaxe css станет пухлым и дохлым поэтому я вынес его в JavaScript</p>
<p>И еще на заметку я буду предлагать заполнять ФИО</p>
<p>Последний штрих рисуем HTML</p>
<pre>
<code class="html">

&lt;div id="firstname_root"&gt;&lt;input type="text" name="firstname" id="firstname" value=""&gt;&lt;div id="firstname_conteiner"&gt;&lt;/div&gt;&lt;div&gt;
&lt;div id="secondname_root"&gt;&lt;input type="text" name="secondname" id="secondname" value=""&gt;&lt;div id="secondname_conteiner"&gt;&lt;div id="firstname_conteiner"&gt;&lt;/div&gt;&lt;div&gt;
&lt;div id="lastname_root"&gt;&lt;input type="text" name="lastname" id="lastname" value=""&gt;&lt;div id="firstname_conteiner"&gt;&lt;div id="lastname_conteiner"&gt;&lt;/div&gt;&lt;div&gt;

</code>
</pre>
<p>А теперь в тегах &lt;_script&gt;...&lt;_/script&gt; описываем загрузчик, он же frontend приложение</p>
<pre>
<code class="javascript">

// определяем именное пространство
YAHOO.namespace("AutoComplete");
/*
Грубо говоря если вы определите YAHOO.namespace("XXX");
то следующая строчка будет выглядеть так
YAHOO.XXX = function(input){
*/


// конструктор
YAHOO.AutoComplete = function(input){
var oACDS;
var oAutoComp;
var oDiv = null;


	// CSS, вот сюда то я и вынес все излишнее свойства
	if (oDiv=document.getElementById(input))
	{
		var rDiv = document.getElementById(input+'_root');
		var cDiv = document.getElementById(input+'_conteiner');
		oDiv.style.position = "absolute";
		oDiv.style.width = "100%";
		rDiv.style.height = "2em";
		rDiv.style.position = "relative";
		rDiv.style.margin_bottom = "1.5em";
		rDiv.style.width = "100%";
		cDiv.style.position = "absolute";
		cDiv.style.top = "1.7em";
		cDiv.style.width = "100%";
	}




    // заполняем первый объект oACDS, он у нас отвечает за AJAX
    // адресс backend'a с параметрами
    oACDS = new YAHOO.widget.DS_XHR("/backend.php", ["n", "t"]);
    // вид ответа - простой текст - по одному варианту на строку
    oACDS.responseType = YAHOO.widget.DS_XHR.TYPE_FLAT;
    // кеширование
   oACDS.queryMatchSubset = true;
    // время кеширования 60 сек
    oACDS.maxCacheEntries = 60;


    // этот параметр отвечает за дополнительные параметры в url, который запрашивается
    //тоесть вместо /backend.php данные будут посланы на /backend.php?active=имя_поля
    oACDS.scriptQueryAppend = '&#038;active=' + input;


    //заполняем второй объект отвечающий за autocomplete, ему по большому счету нужно только имя поля в
    //котором должен работать скрипт
    oAutoComp = new YAHOO.widget.AutoComplete(input,input+'_conteiner', oACDS);
    // разделитель (по умолчанию дописывает в конце выбранного варианта ';')
    oAutoComp.delimChar = "";
    // задержка в секундах до отправки запроса
    oAutoComp.queryDelay = 0;
    // минимальная длина строки перед поиском
    oAutoComp.minQueryLength=1;
    // имя класса для выделенного варианта ответа
    oAutoComp.prehighlightClassName = "yui-ac-prehighlight";
    // отключает автокомплит в браузере чтоб он не мешал
    oAutoComp.allowBrowserAutocomplete = false;
    // можно еще поизвращаться с анимацией, но это вы сделаете сами


};
//ну и собственно стартуем скрипт по разу на каждое поле
YAHOO.AutoComplete('firstname')

</code>
</pre>
<br />
Теперь backend надо написать, да?!<br />
<pre>
<code class="php">

// я полностью писать не буду, просто объясню. Данные из аджакса приходят в кодировке UCS-2BE поэтому 
// ее надо перекодировать в win-1251


// таблица символов
$unicode_to_cp1251_tbl = array(
	0x0402 =&gt; "x80",	0x0403 =&gt; "x81",	0x201A =&gt; "x82",	0x0453 =&gt; "x83",
	0x201E =&gt; "x84",	0x2026 =&gt; "x85",	0x2020 =&gt; "x86",	0x2021 =&gt; "x87",
	0x20AC =&gt; "x88",	0x2030 =&gt; "x89",	0x0409 =&gt; "x8A",	0x2039 =&gt; "x8B",
	0x040A =&gt; "x8C",	0x040C =&gt; "x8D",	0x040B =&gt; "x8E",	0x040F =&gt; "x8F",
	0x0452 =&gt; "x90",	0x2018 =&gt; "x91",	0x2019 =&gt; "x92",	0x201C =&gt; "x93",
	0x201D =&gt; "x94",	0x2022 =&gt; "x95",	0x2013 =&gt; "x96",	0x2014 =&gt; "x97",
	0x2122 =&gt; "x99",	0x0459 =&gt; "x9A",	0x203A =&gt; "x9B",	0x045A =&gt; "x9C",
	0x045C =&gt; "x9D",	0x045B =&gt; "x9E",	0x045F =&gt; "x9F",	0x00A0 =&gt; "xA0",
	0x040E =&gt; "xA1",	0x045E =&gt; "xA2",	0x0408 =&gt; "xA3",	0x00A4 =&gt; "xA4",
	0x0490 =&gt; "xA5",	0x00A6 =&gt; "xA6",	0x00A7 =&gt; "xA7",	0x0401 =&gt; "xA8",
	0x00A9 =&gt; "xA9",	0x0404 =&gt; "xAA",	0x00AB =&gt; "xAB",	0x00AC =&gt; "xAC",
	0x00AD =&gt; "xAD",	0x00AE =&gt; "xAE",	0x0407 =&gt; "xAF",	0x00B0 =&gt; "xB0",
	0x00B1 =&gt; "xB1",	0x0406 =&gt; "xB2",	0x0456 =&gt; "xB3",	0x0491 =&gt; "xB4",
	0x00B5 =&gt; "xB5",	0x00B6 =&gt; "xB6",	0x00B7 =&gt; "xB7",	0x0451 =&gt; "xB8",
	0x2116 =&gt; "xB9",	0x0454 =&gt; "xBA",	0x00BB =&gt; "xBB",	0x0458 =&gt; "xBC",
	0x0405 =&gt; "xBD",	0x0455 =&gt; "xBE",	0x0457 =&gt; "xBF",	0x0410 =&gt; "xC0",
	0x0411 =&gt; "xC1",	0x0412 =&gt; "xC2",	0x0413 =&gt; "xC3",	0x0414 =&gt; "xC4",
	0x0415 =&gt; "xC5",	0x0416 =&gt; "xC6",	0x0417 =&gt; "xC7",	0x0418 =&gt; "xC8",
	0x0419 =&gt; "xC9",	0x041A =&gt; "xCA",	0x041B =&gt; "xCB",	0x041C =&gt; "xCC",
	0x041D =&gt; "xCD",	0x041E =&gt; "xCE",	0x041F =&gt; "xCF",	0x0420 =&gt; "xD0",
	0x0421 =&gt; "xD1",	0x0422 =&gt; "xD2",	0x0423 =&gt; "xD3",	0x0424 =&gt; "xD4",
	0x0425 =&gt; "xD5",	0x0426 =&gt; "xD6",	0x0427 =&gt; "xD7",	0x0428 =&gt; "xD8",
	0x0429 =&gt; "xD9",	0x042A =&gt; "xDA",	0x042B =&gt; "xDB",	0x042C =&gt; "xDC",
	0x042D =&gt; "xDD",	0x042E =&gt; "xDE",	0x042F =&gt; "xDF",	0x0430 =&gt; "xE0",
	0x0431 =&gt; "xE1",	0x0432 =&gt; "xE2",	0x0433 =&gt; "xE3",	0x0434 =&gt; "xE4",
	0x0435 =&gt; "xE5",	0x0436 =&gt; "xE6",	0x0437 =&gt; "xE7",	0x0438 =&gt; "xE8",
	0x0439 =&gt; "xE9",	0x043A =&gt; "xEA",	0x043B =&gt; "xEB",	0x043C =&gt; "xEC",
	0x043D =&gt; "xED",	0x043E =&gt; "xEE",	0x043F =&gt; "xEF",	0x0440 =&gt; "xF0",
	0x0441 =&gt; "xF1",	0x0442 =&gt; "xF2",	0x0443 =&gt; "xF3",	0x0444 =&gt; "xF4",
	0x0445 =&gt; "xF5",	0x0446 =&gt; "xF6",	0x0447 =&gt; "xF7",	0x0448 =&gt; "xF8",
	0x0449 =&gt; "xF9",	0x044A =&gt; "xFA",	0x044B =&gt; "xFB",	0x044C =&gt; "xFC",
	0x044D =&gt; "xFD",	0x044E =&gt; "xFE",	0x044F =&gt; "xFF",
);


// функция поиска по таблице
function utf8_to_cp1251($s) {
	$tbl = $GLOBALS['unicode_to_cp1251_tbl'];
	$uc = 0;
	$bits = 0;
	$r = "";
	for($i = 0, $l = strlen($s); $i &lt; $l; $i++)
	{
		$c = $s{$i};
		$b = ord($c);
		if($b &#038; 0x80) 
		{
			if($b &#038; 0x40)
			{
				if($b &#038; 0x20) 
				{
					$uc = ($b &#038; 0x0F) &lt;&lt; 12;
					$bits = 12;
				} else {
					$uc = ($b &#038; 0x1F) &lt;&lt; 6;
					$bits = 6;
				}
			} else {
				$bits -= 6;
				if($bits)
				{
					$uc |= ($b &#038; 0x3F) &lt;&lt; $bits;
				} else {
					$uc |= $b &#038; 0x3F;
					if($cc = @$tbl[$uc]) 
					{
						$r .= $cc;
					} else {
						$r .= '?';
					}
				}
			}
		} else {
			$r .= $c;
		}
	}
	return $r;
}


// применяем функцию ко всем входящим данным
foreach ($_REQUEST as $key =&gt; $value){
    $_REQUEST[$key] = utf8_to_cp1251($value);
}


// из всех данный нужно только 2
$like = $_REQUEST['query'];
$field = $_REQUEST['active'];


// соединяемся с БД
mysql_connect('localhost','user','pass');
mysql_select_db('namedatabase');


//Ну и собственно спрашиваем есть ли похожие имена
$sql = mysql_query("SELECT ".$field." AS suggestion FROM table_name WHERE ".$field." LIKE '$like%' ORDER BY ".$field."LIMIT 50");


// выводим списком ответ
while($result = mysql_fetch_assoc($sql)){
    echo $result['suggestion']."n";
}

</code>
</pre>
<p>Вот, пожалуй, и все. Кстати очень тяжелая (много килобайт весит) либа. Subsys от Котерова намного легче и практичнее, но не имеет автокомплита. Попытки втиснуть его туда насильно увенчались успехом только для FF1.5 и Opera8.0 , IE ругался</p>]]></content:encoded>
			<wfw:commentRss>http://mabp.kiev.ua/2006/11/28/yui-autocomplete/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
