Изучение Grails: Изменение представления данных с помощью Groovy Server Pages

Groovy Server Pages (GSP) отвечает за Web-компоненту в Web-инфраструктуре Grails. В третьей части серии статей Изучение Grails Скотт Дэвис демонстрирует все тонкости работы с GSP. Убедитесь, насколько легко пользоваться библиотекой тегов Grails, комбинировать фрагменты GSP и настраивать стандартные шаблоны для создания автоматически генерируемых каркасных (scaffolded) представлений.

Скотт Дэвис, главный редактор, AboutGroovy.com

Скотт Дэвис (Scott Davis) является международно признанным автором, лектором и разработчиком программного обеспечения. Среди его книг: Groovy Recipes: Greasing the Wheels of Java, GIS for Web Developers: Adding Where to Your Application, The Google Maps API и JBoss At Work.



16.04.2009

В первых двух статьях из этой серии мы рассказали об основных строительных блоках Grails — инфраструктуры для создания Web-приложений. Я неоднократно упоминал, что в основе Grails лежит архитектурная схема «модель-представление-контроллер» (Model-View-Controller, MVC) (см. Ресурсы), и что при связывании компонентов инфраструктуры Grails поддерживает использование соглашений о конфигурациях. В Grails используются интуитивно понятные стандартные имена файлов и каталогов, в противоположность старому, более подверженному ошибкам подходу, при котором эти имена задавались вручную во внешнем конфигурационном файле. Например, в первой статье рассказывалось, что контроллеры имеют суффикс Controller и находятся в каталоге grails-app/controller. Во второй статье мы видели, что модели доменов находятся в каталоге grails-app/domain.

В этом месяце я завершу триптих об MVC обсуждением представлений данных в Grails. Представления (что естественно) хранятся в каталоге grails-app/views. Но новшество здесь не только в понятном названии каталога. Я расскажу о Groovy Server Pages (GSP) и упомяну многочисленные альтернативные возможности представления данных. Я также расскажу о стандартных библиотеках тегов Grails и покажу, как легко создавать собственные библиотеки тегов. Наконец, я покажу, как реализовать принцип минимального дублирования кода (DRY, см. Ресурсы) превращая путем разложения типовые общих фрагментыов кода GSP на в их пользовательские собственные частичные шаблоны. Наконец, я научу настраивать стандартные шаблоны для генерируемых представлений, что позволяет найти баланс между удобством автоматически создаваемых представлений и желанием выйти за пределы стандартного интерфейса приложений Grails.

Об этой серии статей

Grails — это современная инфраструктура для разработки Web-приложений, который соединяет такие давно знакомые Java-технологии, как Spring и Hibernate, с современными подходами, такими как соглашения о конфигурации. Grails написана на Groovy и прозрачно интегрируется со старыми программами на Java, обладая при этом гибкостью и динамизмом скриптового языка. Познакомившись с Grails, вы будете по-новому смотреть на Web-разработку.

Представление данных в приложениях Grails

Для уровня представления в Grails используются GSP-страницы. Термин Groovy в названии Groovy Server Pages — это не только базовая технология, но и язык, которым можно воспользоваться, если нужно быстро написать один-два скриптлета. Это похоже на технологию Java™ Server Pages (JSP), которая позволяет добавлять немного Java-кода в Web-страницы, и на RHTML (основная технология просмотра для Ruby on Rails), которая позволяет вставить некоторое количество Ruby-кода между тегами HTML.

Да, на скриптлеты в Java-сообществе долгое время смотрели с неодобрением. Они ведут к низшей форме повторного использования технологии — копированию и вставке — и к другим преступлениям против технологической морали. (Есть огромная разница между можно и обязательно.) Буква G в названии GSP должна напомнить возвышенным достойным представителям Java-сообщества, что речь идет только о языке реализации и не более того. Библиотеки тегов и частичные шаблоны Groovy предоставляют более развитые способы многократного использования программного кода и поведения в Web-страницах.

GSP-страницы являются основой принятого в Grails ориентированного на страницы MVC-подхода к представлениям. В этом подходе основной рабочей единицей является страница. Страница List дает ссылку на страницу Show. Со страницы Show можно добраться до страницы Edit, и так далее. Такая организация жизненного цикла в Web знакома и опытным разработчикам на Struts, и современным энтузиастам Rails.

Я упоминаю об этом потому, что в последние годы произошел бурный рост не ориентированных на страницы технологий представлений (см. Ресурсы). Все более популярными становятся компонентно-ориентированные Web-инфраструктуры, такие как JavaServer Faces (JSF) и Tapestry. Революция Ajax породила многочисленные решения на основе JavaScript, например, библиотеки Dojo и Yahoo! UI (YUI). Платформы функционально насыщенных интернет-приложений (Rich Internet Application, RIA), например, Adobe Flash и Google Web Toolkit (GWT), обещают удобство развертывания Web-приложений в сочетании с более насыщенным пользовательским интерфейсом, схожим с приложениями для ПК. К счастью, Grails легко сочетается со всеми этими технологиями представлений.

Смысл разделения задач в MVC состоит в том, что оно позволяет легко придать Web-приложению любой желаемый внешний вид. Популярная инфраструктура подключаемых модулей в Grails позволяет реализовать многие альтернативные решения в GSP одной командой установки плагина: grails install-plugin away. (Посмотрите ссылку на полный список имеющихся подключаемых модулей в Ресурсах или введите в командной строке grails list-plugins). Многие из этих подключаемых модулей поддерживаются представителями сообщества, которым нравится использовать Grails вместе со своей любимой технологией уровня представления.

Хотя в Grails нет собственных автоматических средств подключения JSF, ничто не мешает использовать их вместе. Приложение Grails является стандартным приложением Java EE, поэтому можно просто поместить нужные JAR-файлы в каталог lib, добавить предполагаемые настройки в конфигурационный файл WEB-INF/Web.xml и написать приложение, как обычно. Приложение Grails развертывается в стандартном контейнере сервлетов, поэтому Grails поддерживает JSP-страницы точно так же, как и GSP-страницы. Существуют подключаемые модули Grails для Echo2 и Wicket (оба являются компонентно-ориентированными Web-инфраструктурами), поэтому ничто не мешает создать подключаемые модули и для JSF или Tapestry.

Точно так же можно добавить в Grails инфраструктуры Ajax, например, Dojo или YUI: просто скопируйте их JavaScript-библиотеки в каталог Web-app/js. Prototype и Scriptaculous устанавливаются вместе с Grails по умолчанию. Подключаемый модуль RichUI выбирает лучшие интерфейсные виджеты из различных библиотек Ajax.

Если просмотреть список подключаемых модулей, то мы увидим поддержку таких RIA-клиентов, как Flex, OpenLazlo, GWT и ZK. Таким образом, недостатка в альтернативных решениях для представления данных в приложениях Grails нет. Но давайте поговорим подробнее о собственной технологии просмотра, которую Grails поддерживает «из коробки» — GSP.


Краткое введение в GSP

GSP-страницу можно распознать несколькими способами. Её безошибочно можно угадать по расширению имени файла .gsp и по обильному использованию тегов, начинающихся с <g:. Фактически GSP-страница является просто стандартным HTML с добавлением тегов Grails для представления динамического контента. Некоторые из альтернативных технологий представлений, упомянутых мной в предыдущем разделе, являются непроницаемыми слоями абстракции, скрывающими детали HTML, CSS и JavaScript за слоем Java, ActionScript или другого языка программирования. GSP — это тонкий фасад Groovy поверх стандартного HTML, что позволяет легко выходить за рамки этой инфраструктуры и использовать собственно Web-технологии там, когда это необходимо.

Однако найти GSP-страницы в нашем приложении Trip Planner в его нынешнем виде непросто. (Мы начали создание приложения-примера Trip Planner в первых двух статьях этой серии. Если вы не следили за изложением до этого момента, теперь самое время догнать). Сейчас в этом приложении используется динамическая генерация каркасов (scaffolding) для представлений, поэтому каталог trip-planner/grails-app/views пуст. Откройте в текстовом редакторе файл grails-app/controller/TripController.groovy, показанный в листинге 1, и вы увидите команду, используемую для включения динамического создания каркасов:

Листинг 1. Класс TripController
class TripController{
  def scaffold = Trip
}

Строка def scaffold = Trip сообщает Grails, что нужно генерировать GSP-страницы динамически во время выполнения. Это очень удобно для автоматического поддержания представлений в актуальном состоянии при изменении модели домена, но не дает особого материала для изучения GSP.

Введите grails generate-all Trip в корне каталога trip-planner. Ответьте y на вопрос, нужно ли переопределить существующий контроллер. (Можно также ответить a (all), чтобы переопределить всё без повторных запросов). Теперь должен получиться полный класс TripController с замыканиями create, edit, list и show (в числе прочих). Должен также появиться каталог grails-app/views/trip с четырьмя GSP-страницами: create.gsp, edit.gsp, list.gsp и show.gsp.

Здесь работает соглашение по конфигурации. Когда вы входите на http://localhost:9090/trip-planner/trip/list TripController получает задание заполнить список объектов доменной модели Trip и передать его в представление trip/list.gsp. Еще раз взгляните в текстовом редакторе на файл TripController.groovy, показанный в листинге 2:

Листинг 2. Полностью заполненный класс TripController
class TripController{
  ...
  def list = {
    if(!params.max) params.max = 10
    [ tripList: Trip.list( params ) ]
  }
  ...
}

Это небольшое замыкание извлекает 10 записей Trip из базы данных, преобразует их в POGO (Plain Old Groovy Objects — «простые Groovy-объекты») и сохраняет их в ArrayList с именем tripList. Затем страница list.gsp проходит по этому списку, строя таблицу HTML строка за строкой.

В следующем разделе рассматриваются многие популярные теги Grails, включая тег <g:each>, используемый для отображения каждого Trip на Web-странице.


Теги Grails

Широко используемый тег Grails <g:each> выполняет итерирование по всем (each) элементам списка. Чтобы увидеть его в действии, откройте в текстовом редакторе grails-app/views/trip/list.gsp (см. листинг 3):

Листинг 3. Представление list.gsp
<g:each in="${tripList}" status="i" var="trip">
  <tr class="${(i % 2) == 0 ? 'even' : 'odd'}">
    <td><link action="show" id="${trip.id}">${trip.id?.encodeAsHTML()}</g:link></td>
    <td>${trip.airline?.encodeAsHTML()}</td>
    <td>${trip.name?.encodeAsHTML()}</td>
    <td>${trip.city?.encodeAsHTML()}</td>
    <td>${trip.startDate?.encodeAsHTML()}</td>
    <td>${trip.endDate?.encodeAsHTML()}</td>
  </tr>
</g:each>

Атрибут status в теге <g:each> является простым полем счетчика. (Обратите внимание, что это значение используется в следующей строке в тройном операторе, который устанавливает стиль CSS even или odd.) Атрибут var дает возможность назвать переменную, используемую для хранения текущего элемента. Если изменить имя на foo, то тогда нужно изменить следующие строки на ${foo.airline?.encodeAsHTML()} и так далее. (Оператор ?.— используется в Groovy для того, чтобы избежать исключений NullPointerException. Это краткая запись для: "вызвать метод the encodeAsHTML() в случае, если airline не пуст; в противном случае просто вернуть пустую строку.")

Еще один распространенный тег Grails — это <g:link>. Как уже должно быть понятно, он создает HTML-ссылку <a href> . Ничто не мешает непосредственно использовать тег <a href> , но этот удобный тег принимает атрибуты, например action, id и controller. Если нужно добраться просто до значения href без окружающих тегов anchor, можно использовать вместо этого <g:createLink>. В верхней части list.gsp виден третий тег, возвращающий ссылку: <g:createLinkTo>. Этот тег принимает атрибуты dir and file вместо логических атрибутов controller, action и id. В листинге 4 link и createLinkTo показаны в действии:

Листинг 4. Сравнение тегов link и createLinkTo
<div class="nav">
  <span class="menuButton"><a class="home" href="${createLinkTo(dir:'')}">Home</a></span>
  <span class="menuButton"><link class="create" action="create">New Trip</g:link></span>
</div>

В листинге 4 обратите внимание на то, что теги Grails можно вызывать в двух равноценных формах — либо как теги в угловых скобках, либо как вызов метода в фигурных скобках. Запись в фигурных скобках (официально известная как синтаксис языка выражений (Expression Language, EL)) лучше подходит для случаев, когда вызов метода встроен в атрибуты другого тега.

Несколькими строками ниже в list.gsp можно увидеть в действии еще один популярный тег Grails в действии: <g:if>, показанный в листинге 5. В данном случае в нем говорится: "если атрибут flash.message не пустой, показать его."

Листинг 5. Тег <g:if>
<h1>Trip List</h1>
<if test="${flash.message}">
  <div class="message">${flash.message}</div>
</g:if>

При просмотре сгенерированных представлений вы увидите еще много тегов Grails в действии. Тег <g:paginate> отображает ссылки "предыдущее" и "следующее", если в базе данных содержится больше Trip, чем текущие 10, умещающиеся на экране. Тег <g:sortable> делает заголовки столбцов чувствительными к нажатиям кнопки мыши для сортировки. При просмотре других GSP-страниц вы встретите теги, относящиеся к HTML-формам, например, <g:form> и <g:submit>. В онлайновой документации по Grails перечисляются все имеющиеся теги Grails и даются примеры их использования (см. Ресурсы).


Библиотеки пользовательских тегов

Хотя стандартные теги Grails весьма полезны, когда-либо вам все равно потребуются свои собственные пользовательские теги. Многие бывалые разработчики на Java (и я в том числе) открыто говорят: "Да, правильным архитектурным решением здесь является пользовательская библиотека тегов," — а затем вместо этого украдкой, пока никто не видит, пишут скриптлет. Написание пользовательской библиотеки тегов JSP требует так много дополнительных усилий, что скриптлеты часто берут верх, поскольку оказываются путем наименьшего сопротивления. Это неправильный, но, к сожалению, легкий путь.

Скриптлеты — это ужасно! Это самое настоящее хакерство. Они нарушают парадигму HTML, основанную на тегах, и вводят сырой код прямо в представление. Плох даже не сам программный код; плохо то, что отсутствует инкапсуляция и возможность повторного использования кода. Единственный способ повторно использовать скриптлет — это копирование и вставка, что приводит к ошибкам, раздуванию программного кода и вопиющим нарушениям требования минимизации дублирования кода. Я уже не говорю о том, что скриптлеты невозможно тестировать.

При всём при этом я должен сознаться, что сам написал изрядное количество JSP-скриптлетов, когда поджимали сроки. Стандартная библиотека тегов JSP (JSTL) изо всех сил помогала мне сойти с порочного пути, но написание собственных специальных JSP-тегов все равно представляло проблему. К тому времени, когда я написал бы свой пользовательский тег JSP на Java, скомпилировал бы его и провозился с тем, чтобы сделать дескриптор библиотеки тегов (Tag Library Descriptor, TLD) в правильном формате и в нужном месте, я бы уже совершенно забыл, для чего, собственно, писал этот тег. Что касается написания тестов для проверки моего нового JSP-тега — давайте вспомним, куда ведет дорога, вымощенная добрыми намерениями.

По сравнению с этим написание пользовательских библиотек тегов в Grails — совсем пустяковое дело. Эта инфраструктура помогает легко сделать все необходимое, включая написание тестов. Например, мне часто бывает нужно поместить стереотипное уведомление об авторских правах внизу Web-страниц. Оно должно иметь вид © 2002 - 2008, FakeCo Inc. All Rights Reserved. Дело в следующем: мне нужно, чтобы на месте второго года указывался текущий год. В листинге 6 показывается, как это можно сделать с помощью скриптлета:

Листинг 6. Уведомление об авторских правах с помощью скриптлета
<div id="copyright">
&copy; 2002 - ${Calendar.getInstance().get(Calendar.YEAR)}, 
    FakeCo Inc. All Rights Reserved.
</div>

Теперь, когда понятно, как вставить текущий год хакерским путем, давайте создадим пользовательский тег, делающий то же самое. Для начала введите grails create-tag-lib Date. Будет создано два файла: grails-app/taglib/DateTagLib.groovy (библиотека тегов) и grails-app/test/integration/DateTagLibTests.groovy (тест). Добавьте текст программы, приведенный в листинге 7, в DateTagLib.groovy:

Листинг 7. Простой пользовательский тег Grails
class DateTagLib {
  def thisYear = {
    out << Calendar.getInstance().get(Calendar.YEAR)
  }
}

В листинге 7 создается тег <g:thisYear>. Как можно видеть, год пишется непосредственно в выходной поток. В листинге 8 показан новый тег в действии:

Листинг 8. Уведомление об авторских правах с помощью пользовательского тега
<div id="copyright">
&copy; 2002 - <g:thisYear />, FakeCo Inc. All Rights Reserved.
</div>

В этом месте может возникнуть мысль, что все уже сделано. Я скромно намекну, что сделано лишь полдела.


Тестирование библиотеки тегов

Даже если все пока выглядит отлично, следует всё-таки написать тест, чтобы убедиться, что этот тег не перестанет работать в будущем. Майкл Фезерс (Michael Feathers), автор книги Working Effectively with Legacy Code, говорит, что любая программа без теста является устаревшей. Чтобы убедиться, что г-н Фезерс не будет гневаться на нас, добавим код из листинга 9 в DateTagLibTests.groovy:

Листинг 9. Тест для пользовательского тега
class DateTagLibTests extends GroovyTestCase {
  def dateTagLib

  void setUp(){
    dateTagLib = new DateTagLib()
  }

  void testThisYear() {
    String expected = Calendar.getInstance().get(Calendar.YEAR)
    assertEquals("the years don't match", expected, dateTagLib.thisYear())
  }
}

GroovyTestCase— это тонкий фасад Groovy поверх JUnit 3.x TestCase. Написание теста для однострочного тега может показаться чрезмерным, но на самом деле часто именно однострочные программы оказываются источником неприятностей. Написать тест нетрудно, и лучше перестраховаться, чем потом жалеть. Введите grails test-app для запуска теста. Если все нормально, должно появиться сообщение, приведенное в листинге 10:

Листинг 10. Прохождение тестов в Grails
-------------------------------------------------------
Running 2 Integration Tests...
Running test DateTagLibTests...
                    testThisYear...SUCCESS
Running test TripTests...
                    testSomething...SUCCESS
Integration Tests Completed in 506ms
-------------------------------------------------------

Если появление TripTests застало нас врасплох, ничего страшного. Когда вы ввели grails create-domain-class Trip, тест был создан автоматически. На самом деле каждая команда create в Grails создает соответствующий тест. Да, тестирование настолько важно в современной разработке программ. Если у вас еще не выработалась привычка писать тесты, пусть Grails мягко подтолкнет вас в правильном направлении. Не пожалеете.

Кроме запуска тестов, команда grails test-app создает симпатичный HTML-отчет. Откройте в браузере test/reports/html/index.html, и вы увидите стандартный JUnit-отчет о тестировании, как показано на рис. 1.

Рис. 1. Отчет о тестировании модуля
Отчет о тестировании модуля

Простой пользовательский тег написан и проверен. Теперь можно создать чуть более сложный тег.


Расширенные пользовательские теги

Более сложные теги могут иметь атрибуты и тело тега. Например, наше решение для указания авторских прав в теперешнем виде все еще требует, на мой взгляд, слишком много копирования и вставки. Я бы обернул все текущие действия в тег, действительно удобный для повторного использования, например, в такой: <g:copyright startYear="2002">FakeCo Inc.</g:copyright>. Текст приводится в листинге 11:

Листинг 11. Тег Grails с атрибутами и телом
class DateTagLib {
  def thisYear = {
    out << Calendar.getInstance().get(Calendar.YEAR)
  }

  def copyright = { attrs, body ->
    out << "<div id='copyright'>"
    out << "&copy; ${attrs['startYear']} - ${thisYear()}, ${body()}"
    out << " All Rights Reserved."
    out << "</div>"
  }
}

Обратите внимание, что attrs является HashMap атрибутов тега. Я использую его здесь, чтобы захватить атрибут startYear. Я вызываю тег thisYear как замыкание. (Это такой же вызов замыкания, который я мог бы сделать с GSP-страницы в фигурных скобках, если бы захотел.) Аналогичным образом, body передается в тег как замыкание, поэтому я вызываю его так же, как вызывал бы любой другой тег. Это позволяет вкладывать мои пользовательские теги в GSP на любую глубину.

Читатели уже, вероятно, заметили, что в пользовательских библиотеках тегов используется то же пространство имен g:, что и в стандартных библиотеках тегов Grails. Если вам захочется поместить собственную библиотеку тегов в пользовательское пространство имен, добавьте static namespace = 'trip' к DateTagLib.groovy. В таком GSP тег теперь будет иметь вид <trip:copyright startYear="2002">FakeCo Inc.</trip:copyright>.


Частичные шаблоны

Пользовательские теги являются отличным способом многократно использовать небольшие фрагменты программного кода вместо того, чтобы копировать и вставлять скриптлеты. Для более крупных блоков разметки GSP можно использовать частичные шаблоны.

В документации по Grails частичные шаблоны официально называются шаблонами (templates). Единственная проблема состоит в том, что слово шаблон перегружено и имеет в Grails несколько разных значений. В следующем разделе вы встретитесь со стандартными шаблонами, которые устанавливаются для изменения сгенерированных каркасных (scaffolded) представлений. Изменения в этих шаблонах могут также включать и частичные шаблоны, о которых я собираюсь рассказать в этом разделе. Чтобы уменьшить путаницу, я позаимствую часть терминологии у сообщества Rails и назову эти штуки частичными шаблонами (partial templates), или просто парциалами (partials).

Частичный шаблон — это фрагмент GSP-программы, который может совместно использоваться несколькими Web-страницами. Например, предположим, что я хочу сделать стандартный нижний колонтитул на всех своих страницах. Для этого я создам парциал с именем _footer.gsp. Ведущее подчеркивание — указание инфраструктуре (а также визуальная подсказка разработчику), что этот код не является завершенной, правильно сформированной GSP-страницей. Если я создам этот файл в каталоге grails-app/views/trip, он будет виден только для представлений Trip. Я помещу его в каталог grails-app/views, чтобы он мог совместно использоваться всеми страницами. В листинге 12 приводится частичный шаблон для глобального общего колонтитула:

Листинг 12. Частичный шаблон Grails
<div id="footer">
  <g:copyright startYear='2002'>FakeCo, Inc.</g:copyright>

  <div id="powered-by">
    <img src="${createLinkTo(dir:'images', file:'grails-powered.jpg')}" />
  </div>
</div>

Видно, что частичный шаблон позволяет вам выразить желаемое в синтаксисе HTML/GSP. Напротив, пользовательская библиотека тегов пишется на Groovy. Еще один способ разграничить области применения — библиотека тегов обычно лучше подходит для инкапсуляции микродействий, тогда как частичные шаблоны удобны для повторного использования элементов шаблона.

Чтобы этот пример работал так, как написано, нужно загрузить кнопку "Powered by Grails" в каталог grails-app/Web-app/images (см. Ресурсы). На странице загрузки есть множество вспомогательных материалов, от логотипов высокого разрешения до значков 16x16.

В листинге 13 показывается, как вставить свой только что созданный колонтитул в нижнюю часть страницы list.gsp:

Листинг 13. Визуализация частичного шаблона
<html><body>
...
<g:render template="/footer" />
</body></html>

Обратите внимание, что при визуализации шаблона подчеркивание убирается. Если _footer.gsp находится в каталоге trip, то нужно убрать и ведущую косую черту. Можно рассматривать каталог grails-app/views как корень иерархии представлений.


Настройка генерации каркаса по умолчанию (default scaffolding)

Теперь, когда мы имеем правильные, пригодные для тестированияе, повторно используемые компоненты, можно сделать их частью генерации каркаса по умолчанию. Вспомните, что каркас по умолчанию — это то, что динамически генерируется, когда в контроллер вставляется def scaffold = Foo. Генерация каркаса по умолчанию служит также источником GSP-страниц, которые создаются по команде grails generate-views Trip или grails generate-all Trip.

Чтобы настроить генерацию каркаса по умолчанию, введите grails install-templates. Это добавит к проекту новый каталог src/templates. Должны появиться три каталога с именами artifacts, scaffolding и war.

Каталог artifacts содержит шаблоны для различных классов Groovy: Controller, DomainClass, TagLibи так далее. Если, к примеру, потребуется расширить для всех контроллеров абстрактный родительский класс, то эти изменения можно делать здесь. Все новые контроллеры будут создаваться на основе кода этого измененного шаблона. (Некоторые добавляют def scaffold = @artifact.name@, тогда динамическая генерация каркаса становится стандартным поведением для всех контроллеров пользователя.)

Каталог war содержит файл Web.xml, знакомый всем разработчикам на Java EE. Если нужно добавить свои собственные параметры, фильтры или сервлеты, то это делается здесь. (Энтузиасты JSF: слушайте внимательно!) Когда пишется команда grails war, то именно находящийся здесь файл Web.xml включается в получающийся WAR.

В каталоге scaffolding содержатся заготовки для динамически генерируемых представлений. Откройте list.gsp и добавьте <g:render template="/footer" /> в конец файла. Поскольку эти шаблоны совместно используются во всех представлениях, убедитесь, что используемые частичные шаблоны являются глобальными.

Теперь, когда мы подправили представление List, настало время проверить, подействовали ли эти изменения. Изменения стандартных шаблонов — одна из немногих ситуаций, требующих перезагрузки сервера. Когда Grails снова заработает, зайдите на http://localhost:9090/trip-planner/airline/list в браузере. Если в AirlineController используется генерация каркаса по умолчанию, то внизу страницы должен появиться новый колонтитул.


Заключение

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

В следующем месяце знакомство с Web-инфраструктурой Grails будет сфокусировано на Ajax. Способность делать "микро"-запросы HTTP без перезагрузки всей страницы — это секретный ингредиент, используемый в Google Maps, Flickr и многих других популярных Web-сайтах. Мы применим немного этой магии в Grails. А именно, мы создадим связи "многие-ко-многим" и воспользуемся Ajax, чтобы сделать работу пользователей естественной и приятной.

А пока — удачи в освоении Grails!


Загрузка

ОписаниеИмяРазмер
Sample codej-grails03118.zip256KB

Ресурсы

Научиться

Получить продукты и технологии

  • Grails: загрузите последнюю версию Grails.
  • Кнопка "Powered by Grails": эта кнопка нужна для примера приложения в этой статье.

Обсудить

Комментарии

developerWorks: Войти

Обязательные поля отмечены звездочкой (*).


Нужен IBM ID?
Забыли Ваш IBM ID?


Забыли Ваш пароль?
Изменить пароль

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Профиль создается, когда вы первый раз заходите в developerWorks. Информация в вашем профиле (имя, страна / регион, название компании) отображается для всех пользователей и будет сопровождать любой опубликованный вами контент пока вы специально не укажите скрыть название вашей компании. Вы можете обновить ваш IBM аккаунт в любое время.

Вся введенная информация защищена.

Выберите имя, которое будет отображаться на экране



При первом входе в developerWorks для Вас будет создан профиль и Вам нужно будет выбрать Отображаемое имя. Оно будет выводиться рядом с контентом, опубликованным Вами в developerWorks.

Отображаемое имя должно иметь длину от 3 символов до 31 символа. Ваше Имя в системе должно быть уникальным. В качестве имени по соображениям приватности нельзя использовать контактный e-mail.

Обязательные поля отмечены звездочкой (*).

(Отображаемое имя должно иметь длину от 3 символов до 31 символа.)

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Вся введенная информация защищена.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=40
Zone=Технология Java, Open source
ArticleID=382984
ArticleTitle=Изучение Grails: Изменение представления данных с помощью Groovy Server Pages
publish-date=04162009