Статьи

Лучшие леса с jQuery — Часть I

Леса Grails прекрасно работают из коробки. Сегодня я хочу посмотреть, как мы можем улучшить добавление данных ко многим сторонам отношения «один ко многим» с помощью jQuery, диалогового окна jQueryUI и некоторого Ajax. Используя те же доменные объекты, что и в предыдущей статье, я хочу показать, как мы можем добавлять напоминания в событие, не переходя на новую страницу, при условии, что можно создавать события без напоминаний. Для ясности, вот доменные объекты.

class Event {

String name

static hasMany = [reminders: Reminder]

static constraints = {
}
}
class Reminder {

ReminderType reminderType
Integer duration

static belongsTo = [event:Event]

static constraints = {
}

String toString() {
"${reminderType} : ${duration}"
}
}

Обратите внимание, что в напоминании я добавил ReminderType. Это простой Enum со значениями Email и SMS. Я сделал это, чтобы добавить немного мяса в форму напоминания. После того, как вы запустите новое приложение Grails, вам нужно будет загрузить несколько вещей. Первый — это jQuery. Самый простой способ получить это в Grails — просто установить плагин. Выполните «grails install-plugin jquery», а затем в views / layout / main.gsp измените тег g: javascript, чтобы использовать «jquery» вместо «application» для атрибута библиотеки.  

Мы собираемся использовать виджет Dialog в jQueryUI, поэтому вам нужно получить копию jQueryUI. Вы можете скачать его здесь . Самое простое — просто включить все и выбрать тему. После извлечения содержимого ZIP-файла поместите javascript-файл jqueryui в каталог web-app / js и всю папку темы в web-app / css. Затем добавьте следующее в ваш main.gsp:

<link rel="stylesheet" href="${resource(dir: 'css/ui-lightness', file: 'jquery-ui-1.8.custom.css')}"/>
<script type="text/javascript" src="${resource(dir: 'js', file: 'jquery-ui-1.8.custom.min.js')}"></script>

Измените любые пути по мере необходимости. Затем вам нужно будет создать домены событий и напоминаний. Как только вы это сделаете, нам просто понадобятся базовые леса для всего CRUD. На самом деле нам нужно сгенерировать его (не используя def scaffold = true), потому что нам нужно изменить часть кода скаффолдинга. Поэтому выполните следующие команды:

grails generate-all com.package.Event

grails generate-all com.package.Reminder

Запустите ваше приложение и убедитесь, что все работает как положено. Мы собираемся сосредоточить следующую часть нашего обсуждения на странице редактирования для события. Идите вперед, создайте событие и перейдите к форме редактирования. Это должно выглядеть так:

Если вы щёлкните «Добавить напоминание» прямо сейчас, вы попадете на экран создания напоминания. Мы хотим изменить поведение так, чтобы при нажатии кнопки «Добавить напоминание» отображался диалог с формой напоминания. Затем, когда мы нажимаем кнопку сохранения, на сервер отправляется ajax POST, а затем мы используем ответ для заполнения неупорядоченного списка вновь созданным напоминанием.

Сначала нам нужен новый файл JavaScript. Я создал файл с именем tutorial.js и поместил его в web-app / js. Не стесняйтесь называть это как хотите. Просто убедитесь, что вы включили его в свой main.gsp. Далее нам нужно внести некоторые изменения в edit.gsp события. В этом уроке мы собираемся жестко кодировать форму создания напоминания в редакторе события Event.gsp и использовать Dialog API jQueryUI, чтобы показать и скрыть его, когда нам это нужно. В следующем уроке я покажу, как мы можем получить форму через ajax, но сейчас я хочу сделать вещи максимально простыми.

Откройте views / event / edit.gsp и непосредственно перед закрывающим тегом body добавьте следующее (на самом деле это просто копирование и вставка из views / Remder / Create, кстати):

<div id="dialog-form" title="Create new Reminder">
<g:form action="save" method="post">
<div class="dialog">
<table>
<tbody>
<tr class="prop">
<td valign="top" class="name">
<label for="duration"><g:message code="reminder.duration.label" default="Duration"/></label>
</td>
<td valign="top" class="value ${hasErrors(bean: reminderInstance, field: 'duration', 'errors')}">
<g:textField name="duration" value="${fieldValue(bean: reminderInstance, field: 'duration')}"/>
</td>
</tr>

<tr class="prop">
<td valign="top" class="name">
<label for="reminderType"><g:message code="reminder.reminderType.label" default="Reminder Type"/></label>
</td>
<td valign="top" class="value ${hasErrors(bean: reminderInstance, field: 'reminderType', 'errors')}">
<g:select name="reminderType" from="${com.wbr.tutorial.ReminderType?.values()}" value="${reminderInstance?.reminderType}"/>
</td>
</tr>
</tbody>
</table>
</div>
</g:form>
</div>

Я обернул форму внутри div с идентификатором диалоговой формы. jQueryUI будет использовать этот идентификатор для своего селектора. Давайте создадим немного JavaScript. Откройте новый файл javascript (мой был назван tutorial.js) и начните с добавления следующего скелетного кода:

$(function() {

$('#dialog-form').dialog({
autoOpen: false,
height: 300,
width: 350,
modal: true,
buttons: {
'Create a Reminder': function() {

},
Cancel: function() {
$(this).dialog('close');
}

}
});
});

Если вы использовали jQuery до того, как этот код будет выглядеть довольно знакомым. Я не буду вдаваться в подробности, так как это выходит за рамки этого урока. Мы создали 2 кнопки для этого диалога. При нажатии кнопки «Создать напоминание» запускается функция обратного вызова. Здесь мы выполним запрос ajax для публикации формы. Если кнопка отмены сработала, мы просто закрываем диалог.

Затем нам нужно изменить ссылку «Добавить напоминание» на странице редактирования, а затем добавить обработчик события для нее в нашем javascript, чтобы она отображала форму напоминания. Удалите или закомментируйте следующую строку:

<g:link controller="reminder" action="create" params="['event.id': eventInstance?.id]">${message(code: 'default.add.label', args: [message(code: 'reminder.label', default: 'Reminder')])}</g:link>

И добавьте это на свое место:

<a href="#" id='add_reminder'>${message(code: 'default.add.label', args: [message(code: 'reminder.label', default: 'Reminder')])}</a>

Причина, по которой мы не просто присвоили тегу g: link идентификатор, заключается в том, что он будет отображать идентификатор как Reminder.id, и нам просто нужно его подключить к событию. Есть несколько других вариантов, которые мы могли бы использовать, но это простое решение. Теперь нам нужно подключить событие. Я показываю полный файл javascript до этого момента с добавленным кодом, чтобы вы могли видеть, куда он идет:

$(function() {

$('#dialog-form').dialog({
autoOpen: false,
height: 300,
width: 350,
modal: true,
buttons: {
'Create a Reminder': function() {

},
Cancel: function() {
$(this).dialog('close');
}

}
});

$('#add_reminder').click(function() {
$('#dialog-form').dialog('open');
});


});

Идите и попробуйте. Создайте новое событие и перейдите на его страницу редактирования. Нажмите кнопку Добавить напоминание, и вы должны увидеть что-то вроде этого 

Если вы нажмете Отмена, диалог должен закрыться. Если вы нажмете «Создать напоминание», ничего не произойдет. Нам все еще нужно добавить этот код. Вернитесь к своему файлу javascript и давайте заполним функцию обратного вызова Create a Reminder. Опять же, я включил весь файл javascript, а затем я расскажу о добавленном коде.

$(function() {

$('#dialog-form').dialog({
autoOpen: false,
height: 300,
width: 350,
modal: true,
buttons: {
'Create a Reminder': function() {
var duration = $("#duration").val();
var reminderType = $("#reminderType option:selected").val();
var event = $("#id").val();
$.post(contextPath + '/reminder/save', {'event.id':event, duration:duration, reminderType:reminderType}, function(data) {
}, 'json');
$(this).dialog('close');
},
Cancel: function() {
$(this).dialog('close');
}

},
close: function() {

}

});

$('#add_reminder').click(function() {
$('#dialog-form').dialog('open');
});

});

Первое, что мы делаем, — получаем параметры формы, которые мы хотим передать обратно на сервер. Таким образом, нам нужно значение продолжительности, значение RemderType, и мы должны передать обратно идентификатор события, чтобы новое напоминание было добавлено к правильному событию. Обратите внимание, что event.id — это просто скрытое поле в форме редактирования. Затем мы выдаем $ .post () серверу, присваивая ему URL, наши параметры, и определяем функцию обратного вызова. Я также определил тип ответа как json. Это будет объяснено немного позже. Нам нужно что-то сделать с ответом, но сначала нам нужно изменить метод сохранения нашего ReminderController для рендеринга json вместо обычного перенаправления, которое обычно происходит. Откройте ReminderController и замените следующий код:

 

flash.message = "${message(code: 'default.created.message', args: [message(code: 'reminder.label', default: 'Reminder'), reminderInstance.id])}"
redirect(action: "show", id: reminderInstance.id)

с этим кодом:

render reminderInstance as JSON

Просто для ясности, вот весь метод сохранения из ReminderController.groovy.

def save = {
def reminderInstance = new Reminder(params)
if (reminderInstance.save(flush: true)) {
render reminderInstance as JSON
}
else {
render(view: "create", model: [reminderInstance: reminderInstance])
}
}

Убедитесь, что вы добавили оператор импорта для JSON, если не используете IDE, которая напоминает вам об этом. Мы сохраняем новое напоминание и возвращаем напоминание как json. Это здорово, потому что нам нужно добавить новый элемент списка в неупорядоченный список на странице редактирования события, так же, как это происходит при редактировании события с существующими напоминаниями. Здесь снова больше javascript, всего файла, а затем я объясню новый код:

$(function() {

$('#dialog-form').dialog({
autoOpen: false,
height: 300,
width: 350,
modal: true,
buttons: {
'Create a Reminder': function() {
var duration = $("#duration").val();
var reminderType = $("#reminderType option:selected").val();
var event = $("#id").val();
$.post(contextPath + '/reminder/save', {'event.id':event, duration:duration, reminderType:reminderType}, function(data) {
var item = $("<li>");
var link = $("<a>").attr("href", contextPath + "/reminder/show/" + data.id).html(data.reminderType.name + " : " + data.duration);
item.append(link);
$('#reminder_list').append(item);
}, 'json');
$(this).dialog('close');
},
Cancel: function() {
$(this).dialog('close');
}

}
});

$('#add_reminder').click(function() {
$('#dialog-form').dialog('open');
});
});

Недавно добавленный код здесь является обратным вызовом для $ .post (). Сначала мы создаем новый элемент LI. Затем мы создаем новый элемент привязки и добавляем атрибут href. Обратите внимание, что когда нам нужны свойства напоминания, так как это объект json, мы можем просто ссылаться на свойства напрямую. HTML-код тега привязки имитирует то, что делается в методе toString Reminder. Таким образом, когда мы возвращаемся на эту страницу с существующими напоминаниями и добавляем новые, они выглядят одинаково. Последнее, что вам нужно сделать, — это добавить идентификатор «список напоминаний» в UL на странице редактирования, чтобы мы могли добавить элемент к нему.

Вот и все. В следующей статье я покажу, как мы имеем дело с ошибками проверки при сохранении напоминания, ограничения которого не выполняются.