Итак … теперь у вас должно быть достаточно хорошее понимание того, как объединить Wicket с Spring и Hibernate , создать ваши DAO и службы и поместить этот код в тестовую сеть. Мы видим, что наш фундамент очень прочный … но нам не хватает глазного леденца … так что давайте прыгнем в интерфейс и покажем, где действительно сияет Wicket.
Базовый класс
Большинство, если не все веб-приложения используют какой-то базовый шаблон для удаления дублирования, например, верхний и нижний колонтитулы. У Wicket есть встроенный способ обработки этого вместо того, чтобы использовать отдельную библиотеку, такую как SiteMesh . Wicket использует наследование для облегчения шаблонов. Они предоставляют свой собственный базовый класс, называемый WebPage , с которого базовый класс нашего приложения будет расширяться для начала работы. Класс WebPage устанавливает нас с пустой веб-страницей за считанные секунды. Для нашего приложения у нас есть простой верхний / нижний колонтитул, который мы хотим использовать на всех наших страницах, и очень простое меню, которое я бросил в базовую страницу, которую я назвал BasePage .
public class BasePage extends WebPage {...
Это вместе с HTML-страницей дает нам базовый шаблон, от которого будут расширяться все наши страницы.
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<title>Mystic Paste</title>
<link rel="stylesheet" type="text/css" href="css/style.css"/>
<!--[if IE]>
<link rel="stylesheet" type="text/css" href="css/ie.css" />
<![endif]-->
</head>
<body>
<div id="leftSide"> </div>
<div id="rightSide"> </div>
<div id="center">
<!-- header -->
<div id="header">
<div id="logo"><a class="logo" href="http://projects.mysticcoders.com/paste"> </a></div>
</div>
<div id="nav">
<ul id="menus">
<li class="cat-item"><a class="home" title="Home" href="http://www.mysticcoders.com/">Home</a></li>
<li wicket:id="newLinkContainer" class="cat-item"><a wicket:id="newLink" href="#" title="New Paste">New</a></li>
<li wicket:id="historyLinkContainer" class="cat-item"><a wicket:id="historyLink" href="#" title="View Paste History">History</a></li>
<li><a class="lastmenu" href="javascript: return false;"> </a></li>
</ul>
</div>
<div id="header_bottom"> </div>
<!-- content -->
<div id="content">
<wicket:child/>
</div>
<div class="clear"> </div>
<!-- footer -->
<div id="footer_left"> </div>
<div id="footer_right"> </div>
<div id="footer_center">
<div id="copyright">Copyright © 2000-2009 Mystic Coders, LLC</div>
</div>
</div>
<div id="logo_footer"><img src="images/logo_bottom.png" width="74" height="57"/></div>
</body>
</html>
Этот HTML-файл находится в файловой системе в том же пакете, что и ваш класс BasePage, и называется так же, но с расширением .html… BasePage.html. Мы решили отделить java-файлы от html, поместив html в ту же структуру пакета под папкой ресурсов. Обратите внимание на атрибуты wicket: id и тег <wicket: child />… атрибуты wicket: id используются в коде java для идентификации компонентов, а тег wicket: child используется в качестве заполнителя, указывающего, что любая страница, расширяющая эту страницу будет заполнять тело тега. 2 ссылки с атрибутами wicket: id используются для ссылок на пункты меню, а окружающие теги li содержат атрибуты wicket: id для облегчения выделения текущей страницы.
Вставь это!
Калитка начинает веселиться, когда мы входим в формы. Нам нужно создать форму, которая позволит пользователю выбирать тип языка для форматирования вставленного содержимого, независимо от того, является ли это личным сообщением (не очень легко угаданный URL и не будет отображаться в истории), и самим содержимым. Мы хотим убедиться, что конечный результат имеет довольно простой URL, который легко скопировать и вставить.
Первое, что мы обычно делаем, это придумываем класс страницы и HTML… так что мы собираемся создать класс, который расширяет наш BasePage:
public class PasteItemPage extends BasePage {...
Соответствующая html-страница снова находится в том же пакете, что и класс Java, и называется так же.
PasteItemPage.html:
<wicket:extend>
<form wicket:id="pasteForm">
<div id="paste_options">
<ul>
<li>private:</li>
<li><input wicket:id="private" type="checkbox" /></li>
</ul>
<ul>
<li>language:</li>
<li>
<select wicket:id="type" class="language">
<option>Choose One</option>
<option>Java</option>
<option>Groovy</option>
<option>PHP</option>
</select>
</li>
</ul>
</div>
<div id="paste_content">
<div id="textLeft"> </div>
<div id="textRight"> </div>
<div id="textCenter"><textarea wicket:id="content" wrap="off"></textarea></div>
</div>
<div id="paste_submit"><input type="submit" value="Paste" /></div>
</form>
</wicket:extend>
Обратите внимание на теги wicket: extends, которые сообщают Wicket, что все содержимое этих тегов является содержимым, которое нас интересует… например, вы можете иметь весь html-файл с тегами html / head / body, если хотите, и wicket будет игнорировать все, кроме для данных между калиткой: расширить теги.
В калитку: ID атрибуты помещаются в форме и компонентов формы. Эти атрибуты позволят нам создать форму Wicket и связать ее с компонентами формы.
Wicket предоставляет компоненты практически для всего, что вы хотите сделать, поэтому мы расширяем класс Wicket Form и добавляем его на нашу страницу, затем добавляем поля формы ( DropDownChoice , CheckBox иTextArea ) в форму.
Компоненты в Wicket являются иерархическими, вы ДОЛЖНЫ вложить / добавить свои компоненты в свой Java-код, чтобы точно соответствовать вложенности ваших HTML-компонентов. Например, следующий фрагмент взят из BasePage.html:
<li wicket:id="newLinkContainer" class="cat-item"><a wicket:id="newLink" href="#" title="New Paste">New</a></li>
Соответствующий код Java выглядит следующим образом:
WebMarkupContainer newLinkContainer = new WebMarkupContainer("newLinkContainer");
...
newLinkContainer.add(new BookmarkablePageLink("newLink", PasteItemPage.class));
add(newLinkContainer);
В html-разметке тег href, помеченный с помощью wicket: id = ”newLink” , вложен внутрь тега li, помеченного с помощью wicket: id = ”newLinkContainer” . Поэтому нам нужно сопоставить эту иерархию в нашем соответствующем Java-коде. В коде Java я создал компонент WebMarkupContainer с id = ”newLinkContainer” для соответствия нашему тегу li , затем я добавляю вложенный BookmarkablePageLink с id = ”newLink” в компонент newLinkContainer. Затем я добавляю компонент newLinkContainer на страницу, так как newLinkContainer не содержится ни в каких других тегах калитки.
Это вложение может быть очень глубоким в зависимости от макета веб-страницы. Нетрудно отследить вложение, но иногда вы можете забыть исправить html или код Java при внесении изменений в любой файл. Однако разработчики Wicket создали чистое сообщение об ошибке, которое появляется при запуске приложения, и существует несоответствие между вашим html и кодом Java. Например, если я использую наш пример выше и добавляю newLink на страницу, а не в newLinkContainer, я получаю следующее сообщение об ошибке:
WicketMessage: Невозможно найти компонент с идентификатором ‘newLink’ в [MarkupContainer [Component id = newLinkContainer]]. Это означает, что вы объявили wicket: id = newLink в своей разметке, но вы либо вообще не добавили компонент на свою страницу, либо иерархия не соответствует.
[markup = file: / … paste / web / pages / paste / PasteItemPage.html
Эти сообщения об ошибках облегчают поиск проблем с иерархией, а не предполагают, где проблема может быть.
Ниже приводится полный исходный код Java для нашего PasteItemPage :
public class PasteItemPage extends BasePage {
@SpringBean
PasteService pasteService;
public PasteItemPage() {
super(PasteItemPage.class);
add(new PasteForm("pasteForm", new CompoundPropertyModel(new PasteItem())));
}
public class PasteForm extends Form {
public PasteForm(String id, IModel model) {
super(id, model);
add(new CheckBox("private"));
add(new DropDownChoice("type", Arrays.asList(LanguageType.JAVA, LanguageType.CSS, LanguageType.HTML)));
add(new TextArea("content"));
}
@Override
protected void onSubmit() {
PasteItem pasteItem = (PasteItem) PasteForm.this.getModelObject();
pasteService.createItem("web", pasteItem);
PageParameters params = new PageParameters();
if (pasteItem.isPrivate()) {
params.put("0", pasteItem.getPrivateToken());
setResponsePage(ViewPrivatePage.class, params);
} else {
params.put("0", Long.toString(pasteItem.getId()));
setResponsePage(ViewPublicPage.class, params);
}
}
}
}
Как вы могли заметить … Wicket использует модели для поддержки компонентов. В нашем случае … мы используем CompoundPropertyModel, который делает его чрезвычайно простым для привязки к компонентам. В основном, он сообщает любому компоненту, который использует эту модель, чтобы связать свойство из объекта модели с компонентом, используя идентификатор компонента. Например, у нас есть add (новый CheckBox (”private”)); в котором говорится, что мы хотим добавить компонент CheckBox с идентификатором «private» и связать его со свойством объекта нашей модели с таким же именем (приватное поле PasteItem). Я добавил CompoundPropertyModelк компоненту формы, который автоматически поддерживает все компоненты, добавленные в форму, но может быть легко переопределен путем простой передачи новой модели любым компонентам, которым требуется другая модель. Есть много других типов моделей на выбор, поскольку вам может не понадобиться или вы хотите CompountPropertyModel из-за несоответствия в именах и тому подобного. Компоненты DropDownChoice и TextArea связаны с тегами html SELECT и TEXTAREA в одном поместье.
Последний этап отправки формы завершается переопределением метода onSubmit формы и сохранением объекта модели простым вызовом нашего сервиса. Вот и все для захвата пользовательского ввода и его сохранения … не уверен, что это становится намного проще, чем это!
Как часть процедуры сохранения, еще один примечательный момент — вот как мы переходим на следующую страницу:
PageParameters params = new PageParameters();
if (pasteItem.isPrivate()) {
params.put("0", pasteItem.getPrivateToken());
setResponsePage(ViewPrivatePage.class, params);
} else {
params.put("0", Long.toString(pasteItem.getId()));
setResponsePage(ViewPublicPage.class, params);
}
Метод setResponsePage это именно то, что … мы даем ему страницу, которую мы хотим переадресовать … в этом случае, если это личное сообщение, то мы пересылаем на нашу страницу, которую мы создали для личных вставок, в противном случае, мы пересылаем нашу обычную общедоступную Просмотр страницы. Обратите внимание, что мы создаем PageParametersобъект, Wicket абстрагирует от вас ужасный объект запроса и предоставляет вам удобный объект для добавления и извлечения параметров страницы. Теперь … как я уже упоминал ранее, нам нужны простые URL-адреса … поэтому обычно вы помещаете что-то вроде params.put (”id”, pasteItem.getId ()); и это передало бы параметр запроса id = 5 или на страницах с закладками Wickets, вы бы увидели что-то вроде http://your.domain.com/view/id/5. Мы решили, что не хотим, чтобы идентификатор отображался, так как он не используется внутри самого URL-адреса … так что … Wicket дает нам возможность создать собственную стратегию кодирования URL-адресов и предоставляет несколько уже реализованных стратегий. В классе Wicket Application, который был создан в первый день, мы можем добавить следующее:
mount(new IndexedParamUrlCodingStrategy("/view", ViewPublicPage.class));
Это говорит Wicket, что любой пересылающий на мой ViewPublicPage будет использовать IndexedParamUrlCodingStrategy … который работает следующим образом: мы добавляем / извлекаем параметры из PageParameters, используя ключи 0, 1, 2… и т. Д. Как вы можете видеть в нашем примере кода, мы используем 0 поскольку у нас есть только один параметр. Конечным результатом этого является то, что наш URL будет выглядеть примерно так:
http://your.domain.com/view/5
Это не так уж много, но у него есть немного более чистый URL-адрес, и в зависимости от вашего приложения это может сильно помочь с SEO.
Просто и чисто
показали вам очень простую форму и насколько легко создать представление рабочей формы, но заметили ли вы, что в html нет каких-либо фрагментов кода java?
На мой взгляд, это одна из лучших функций калитки, есть Wicket: идентификаторы, но это атрибуты и теги, которые игнорируются большинством дизайнеров GUI, таких как Dreamweaver, поэтому HTML может быть перенесен туда и обратно, если это необходимо, без графики Дизайнерские шланги разработчиков тяжелой работы. Даже если вам нужно взять свежую копию HTML… гораздо проще просто добавить викет: идентификаторы обратно, чем объединить все XML или JSP, с которыми большинство других сред Java заставляют разработчика работать ,
Прошли времена System.outs в вашем jsps, чтобы выяснить, что там происходит. С Wicket… весь ваш код находится в классах Java, которые можно легко отлаживать с помощью вашей любимой IDE. Вы можете пройти через свои петли, чтобы увидеть, что вы населяете и почему. Вы даже можете отлаживать части вызовов Ajax, так как компоненты, включенные в Wicket Ajax, скрывают сложность отправки Ajax и извлечения данных.
Хватит болтовни… давайте посмотрим на некоторые действия и список блага!
история
Обработка форм и компонентов замечательны, но я думаю, что на странице истории показаны некоторые из моих любимых компонентов в Wicket. Существует хороший выбор различных типов компонентов повторителей и отличный компонент подкачки, который мы будем использовать для отображения истории вставки.
Начнем с разговора о компоненте DataView, который мы будем использовать для отображения вставок. Компонент DataView — это повторитель, который позволяет нам легко пометить то, что мы хотим повторить в html, и заполнить данные из нашего объекта модели. Это делается путем добавления DataView на нашу страницу , а затем реализует в DataView сек populateItem метод следующим образом :
add(historyDataView = new DataView("history", new HistoryDataProvider(pasteService), 10) {
protected void populateItem(Item item) {
PasteItem pasteItem = (PasteItem) item.getModelObject();
PageParameters params = new PageParameters();
params.put("0", Long.toString(pasteItem.getId()));
item.add(new BookmarkablePageLink("viewLink", ViewPublicPage.class, params));
final String[] contentLines = pasteItem.getContent().split("\n");
item.add(new Label("lineCount", "(" + contentLines.length + " Line" + (contentLines.length > 1 ? "s" : "") + ")"));
item.add(new Label("posted", getElapsedTimeSincePost(pasteItem)));
List lines = new ArrayList();
int count = 0;
for (String contentLine : contentLines) {
count++;
if (count > 5) {
break;
} else {
lines.add(contentLine);
}
}
item.add(new ListView("content", lines) {
protected void populateItem(ListItem item) {
String content = (String) item.getModelObject();
Label contentLine = new Label("contentLine", ((item.getIndex() + 1) + " ").substring(0, 5) + content.replaceAll("\r", "").replaceAll("\n", ""));
item.add(contentLine);
if ((item.getIndex() + 1) % 2 == 0) {
item.add(new SimpleAttributeModifier("class", "highlight"));
}
}
});
item.add(new BookmarkablePageLink("viewLink2", ViewPublicPage.class, params) {
@Override
public boolean isVisible() {
return contentLines.length > 5;
}
});
}
});
и соответствующий HTML:
<div wicket:id="history" class="historyItem">
<div class="view">
<div class="historyItemHeader">
<div class="historyItemView"><a wicket:id="viewLink" href="#">View Paste</a></div><div wicket:id="lineCount" class="historyItemLines">(27 lines)</div><div wicket:id="posted" class="historyItemTime">1 hour ago</div>
</div>
<div class="historyItemHeaderBottom"> </div>
<div wicket:id="content"><re wicket:id="contentLine">asdfl;kajsdf; a;sldkfj a;lskdjf</re></div>
<div class="historyItemView"><a wicket:id="viewLink2">More...</a></div>
</div>
</div>
Копаясь … вы помечаете калитку: id, который вы хотите повторить … в нашем случае это контейнерный элемент для элемента истории, который мы пометили как wicket: id = «history». Для каждого объекта (PasteItem) в нашем списке моделей мы собираемся получить новый div с содержимым. Для каждого объекта в списке мы добавляем BookmarkablePageLink, который ссылается на представление вставки, количество строк и истекшее время, которое мы добавляем в качестве компонентов Label , повторитель для отображения первых 5 строк вставки и ссылку «Еще», которая отображает только если в пасте более 5 строк. BookmarkablePageLink означает , что мы будем иметь «чистый» URL , и мы уже рассмотрели PageParameters . Этикетка
имеет удобный конструктор, позволяющий использовать String вместо того, чтобы заключать их в модель. Как упоминалось ранее, количество строк и истекшее время выводятся и поэтому не могут быть извлечены из объекта модели, а заданы вручную. Тогда у нас есть другой тип повторителя для отображения пасты. Я выбрал ListView, так как я передаю ему List, и мне не нужно беспокоиться о длине или подкачке. Последний добавляемый нами компонент — это условная ссылка на представление вставки, где мы переопределяем метод isVisible, чтобы сообщить Wicket, является ли этот компонент видимым.
Это охватывает DataView … а теперь, как насчет подкачки? У калитки есть PagingNavigatorКомпонент, который имеет встроенный механизм пейджинга, который можно легко переопределить, чтобы приспособить практически любой вид пейджинга, который желает ваше маленькое сердце. Требования для использования PagingNavigator состоят в том, что вам нужно начать с повторного сервера, который реализует IPageable ( DataView ), и вам нужно будет предоставить DataView провайдеру данных, который реализует IDataProvider . Я решил расширить DefaultDataProvider и реализовать следующим образом:
public class HistoryDataProvider extends DefaultDataProvider {
PasteService pasteService;
public HistoryDataProvider(PasteService pasteService) {
this.pasteService = pasteService;
}
public Iterator iterator(int first, int count) {
return pasteService.getLatestItems("web", count, first, false).iterator();
}
public int size() {
return new Long(pasteService.getLatestItemsCount("web")).intValue();
}
public IModel model(Object object) {
return new Model((PasteItem) object);
}
}
Вы можете видеть, что поставщик данных позволяет нам извлекать только то, что отображается на текущей странице, и дает механизму подкачки общее значение счетчика с помощью метода размера. В свою очередь, пейджинговый механизм обеспечивает запуск и подсчет того, что должно отображаться.
Последнее — это добавление компонентов PagingNavigator, которые я выбрал для показа вверху и внизу списка.
HistoryPage.html
<wicket:extend>
<div class="navContainer"><div wicket:id="pageNav" class="pageNav"><a href="#">Previous</a><a href="#">1</a><a href="#">2</a><a href="#">Next</a></div></div>
<div wicket:id="history" class="historyItem">
<div class="view">
<div class="historyItemHeader">
<div class="historyItemView"><a wicket:id="viewLink" href="#">View Paste</a></div><div wicket:id="lineCount" class="historyItemLines">(27 lines)</div><div wicket:id="posted" class="historyItemTime">1 hour ago</div>
</div>
<div class="historyItemHeaderBottom"> </div>
<div wicket:id="content"><re wicket:id="contentLine">asdfl;kajsdf; a;sldkfj a;lskdjf</re></div>
<div class="historyItemView"><a wicket:id="viewLink2">More...</a></div>
</div>
</div>
<div class="navContainer"><div wicket:id="pageNav2" class="pageNav"><a href="#">Previous</a><a href="#">1</a><a href="#">2</a><a href="#">Next</a></div></div>
</wicket:extend>
и HistoryPage.java
public class HistoryPage extends BasePage {
@SpringBean
PasteService pasteService;
SimpleDateFormat sdf = new SimpleDateFormat("MM/dd/yyyy");
DataView historyDataView;
public HistoryPage() {
super(HistoryPage.class);
add(historyDataView = new DataView("history", new HistoryDataProvider(pasteService), 10) {
protected void populateItem(Item item) {
PasteItem pasteItem = (PasteItem) item.getModelObject();
PageParameters params = new PageParameters();
params.put("0", Long.toString(pasteItem.getId()));
item.add(new BookmarkablePageLink("viewLink", ViewPublicPage.class, params));
final String[] contentLines = pasteItem.getContent().split("\n");
item.add(new Label("lineCount", "(" + contentLines.length + " Line" + (contentLines.length > 1 ? "s" : "") + ")"));
item.add(new Label("posted", getElapsedTimeSincePost(pasteItem)));
List lines = new ArrayList();
int count = 0;
for (String contentLine : contentLines) {
count++;
if (count > 5) {
break;
} else {
lines.add(contentLine);
}
}
item.add(new ListView("content", lines) {
protected void populateItem(ListItem item) {
String content = (String) item.getModelObject();
Label contentLine = new Label("contentLine", ((item.getIndex() + 1) + " ").substring(0, 5) + content.replaceAll("\r", "").replaceAll("\n", ""));
item.add(contentLine);
if ((item.getIndex() + 1) % 2 == 0) {
item.add(new SimpleAttributeModifier("class", "highlight"));
}
}
});
item.add(new BookmarkablePageLink("viewLink2", ViewPublicPage.class, params) {
@Override
public boolean isVisible() {
return contentLines.length > 5;
}
});
}
});
add(new PagingNavigator("pageNav", historyDataView));
add(new PagingNavigator("pageNav2", historyDataView));
}
}
Обратите внимание, что мы только что добавили 2 PagingNavigator в нижней части кода, передавая DataView, который мы создали выше. Вот и все … теперь у вас есть полностью функционирующая страница истории с навигацией по страницам. Опять же, не уверен, что это может стать намного проще, чем это.
тестирование
Тестируешь, говоришь? Вау … мы не можем протестировать интерфейс, не пройдя через много проблем, не так ли?
Что ж, правда в том, что Wicket предоставляет способ довольно частого тестирования, и это так же просто, как тестировать любой другой код Java!
Что бы мы хотели проверить? Что ж, я считаю, что мы хотели бы проверить, что успешная вставка действительно пойдет на правильную страницу и что представление поста будет содержать то, что мы вставили. Возможно, мы также захотим посмотреть, работают ли наши ссылки … они переходят на правильную страницу? У нас нет сложного приложения, поэтому мы собираемся показать небольшой тест, но среда тестирования может проверять практически все, что может произойти на странице. А пока взгляните на этот простой тест:
public class TestPastePage extends AbstractIntegrationTest {
@SpringBeanByType
private PasteService svc;
@SpringBeanByType
private PasteItemDao dao;
protected WicketTester tester;
@Before
public void setup() {
AnnotApplicationContextMock appctx = new
AnnotApplicationContextMock();
appctx.putBean("pasteDao", dao);
appctx.putBean("pasteService", svc);
tester = new WicketTester(MysticPasteApplication.class);
WebApplication app = tester.getApplication();
app.addComponentInstantiationListener(new SpringComponentInjector(app, appctx));
}
@Test
public void testPaste() {
tester.startPage(PasteItemPage.class);
tester.assertRenderedPage(PasteItemPage.class);
FormTester ft = tester.newFormTester("pasteForm");
ft.select("type", 0);
ft.setValue("content", "blahblahblah");
ft.submit();
tester.assertRenderedPage(ViewPublicPage.class);
tester.assertContains("blahblahblah");
tester.assertLabel("type", "JAVA");
}
@Test
public void testHistoryMenuClick() {
tester.startPage(PasteItemPage.class);
tester.assertRenderedPage(PasteItemPage.class);
tester.clickLink("historyLinkContainer:historyLink");
tester.assertRenderedPage(HistoryPage.class);
}
}
Ну … это выглядит достаточно просто. Сначала мы проверим, работает ли вставка, посмотрев, что происходит в testPaste:
- начать страницу, которую мы хотим посмотреть.
- подтвердите, что страница была отображена и мы все еще на этой странице.
- настроить тестер форм.
- установите значения для раскрывающегося списка языков и содержимого вставки.
- отправить форму.
- утверждать, что это пошло на страницу, мы ожидали, что это перейдет к следующему.
- посмотрим, содержит ли он пасту, в данном случае «бла-бла».
- и, наконец, посмотрите, установлена ли метка для языка на JAVA.
Очень круто … тест пройден … дальше мы тестируем ссылку на историю пунктов меню. Мы открываем нашу стартовую страницу, удаляем ссылку через clickLink, которая установлена на нашу ссылку истории, и затем проверяем, что она действительно перешла на нашу страницу истории.
Могу поспорить, вы никогда не думали, что тестирование внешнего кода может быть таким простым. WicketTester делает всю работу таким образом , теперь вы можете иметь гораздо больший охват испытания , чем вы обычно имеют с веб — приложением.
Вывод
Wicket позволяет разработчику создавать приложения так же быстро, как и любой другой фреймворк, который я видел до сих пор, сохраняя HTML как можно более чистым. Иногда я вынужден возвращаться к старым приложениям и иметь дело с jsps как старым, так и новым, и у меня всегда возникают головная боль и кровотечение из-за ударов, принятых при работе с jsps и jstl. Хотелось бы, чтобы у меня было время и пространство, чтобы подробнее узнать о некоторых полезных компонентах, которые предлагает Wicket, и я даже не коснулся компонентов Ajax Wickets в этой версии приложения MysticPaste. Обратите пристальное внимание на наш блог, чтобы увидеть последующие посты и высшее образование в Wicket, когда мы вносим улучшения в приложение MysticPaste. Мы также продолжим публиковать советы и рекомендации по калитке по мере их появления.
Mystic Coders, LLC has been coding web magic since 2000. Mystic is a full-service Development Agency specializing in Enterprise development with Java. They are usually involved in developing enterprise-grade software for companies large and small, and have experience working in diverse industries, including b2b, b2c, and government-based projects. Mystic has done work with large companies such as LeapFrog, Nestlé, Harrah’s Entertainment and the Los Angeles Conventions & Visitor’s Bureau, among others. Andrew Lombardi, CTO of Mystic, is available for speaking engagements.
For more about Mystic, check us out at http://www.mysticcoders.com