Play Framework — регистрация пользовательского DataBinder для динамических полей

Используя Play 2.3.7 (Java), у меня есть следующий сценарий.

У меня есть класс CSVData, который содержит список типа CSVField. Вот атрибуты для этих классов:

public class CSVData{

private String name;
private String description;
private String dataFilePath;
private List<CSVField> fields;
private Double latitude;
private Double longitude;


// rest of class... }

и

public class CSVField {
    private String name;
    private String type;

...}

Сложность создания формы для ввода CSVData заключается в том, что у меня есть вложенный атрибут List<CSVField>, а CSVField — это настраиваемый тип, содержащий две строки. Мне нужно, чтобы форма была динамической, чтобы она могла принимать произвольное количество CSVFields (не менее 1). Согласно документации по форме Java, мне кажется, что я должен зарегистрировать собственный DataBinder для CSVField, однако я не могу найти примеры, которые делают это с несколькими входными строками. Этот пример подобен, но только связывает одно поле.

Вот видео о том, какой тип пользовательского ввода мне нужен. Я сделал свое представление, используя этот пример кода для добавления динамических полей. Комбинация текстового поля (имя) и выбора элемента раскрывающегося списка (тип) — это то, что мне нужно для привязки к CSVField, а затем добавить к List<CSVField> fields в объекте CSVData. Как это сделать с помощью Play Framework?


EDIT: в моем контроллере я пробовал это

Form<CSVData> formData = Form.form(CSVData.class).bindFromRequest();

И в представлении я пробую это

@helper.repeat(csvForm("fields"), min = 1) { csvField =>

    @multiDataField(csvField,
        label = "Column Name and Type",
        gsnTypes,
        help = "Enter the column names and respective types for the data items in the file")

}

Где multiDataField — это этот шаблон. Но он неправильно связывает динамические поля и выдает недопустимую ошибку проверки fields. Я думаю, моя проблема в том, что я не знаю, какие атрибуты name использовать в моем шаблоне multiDataField. Любой совет?


person KJ50    schedule 16.02.2015    source источник


Ответы (1)


Вам не нужно никакого связывателя данных клиентов. Списки со сложными объектами поддерживаются без дополнительной регистрации привязки.

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

Здесь у вас есть полный пример об Play и Forms или непосредственно в TypeSafe

ИЗМЕНИТЬ

Внутри блока повторения csvField является экземпляром каждого объекта формы в вашем списке. Затем вам нужно будет добавить все элементы HTML, необходимые для вашего представления. Например (упрощенно без Bootstrap):

@helper.repeat(csvForm("fields"), min = 1) { csvField =>
    Name: <input type="text" name='@csvField("name").name' value='@csvField("name").value'>
    Type: <input type="text" name='@csvField("type").name' value='@csvField("type").value'>
}

Вы можете найти более полный пример в примерах, представленных в Play 2.2. х. Чтобы скомпилировать его в 2.3.x, возможно, нужно что-то немного изменить и не использовать Bootstrap 3.x, но логика та же.

ИЗМЕНИТЬ (2)

Если вы хотите динамически добавить элементы в представление, вам необходимо позаботиться о том, чтобы при добавлении нового элемента был установлен правильный номер массива. Для этого вам нужно будет использовать JQuery:

$('.addCSVField').click(function() {
    var CSVFields = $(this).parents('.CSVField');
    var template = $('.CSVField_template', CSVFields);
    template.before('<div class="clearfix CSVField">' + template.html() + '</div>');
    renumber();
})

$('.removeCSVField').click(function() {
    $(this).parents('.CSVField').remove();
    renumber(); 
})  

var renumber = function() {
    $('.CSVField').each(function(i) {
        $('input', this).each(function() {
            $(this).attr('name', $(this).attr('name').replace(/fields\[.+?\]/g, 'fields[' + i + ']'));
        })
    })
}

И тогда вам нужно будет изменить свой код HTML/Scala на что-то вроде этого:

@fieldGroup(field: Field, className: String = "CSVField") = {
    <div class="well @className">
        <a class="removeCSVField btn danger pull-right">Remove this field</a>
        Name: <input type="text" name='@field("name").name' value='@field("name").value'>
        Type: <input type="text" name='@field("type").name' value='@field("type").value'>
    </div>
}   

@repeat(csvForm("fields")) { csvField =>
    @fieldGroup(csvField)
}

@**
 * Keep an hidden block that will be used as template for Javascript copy code
 **@
@fieldGroup(csvForm("fields[x]"),className = "CSVField_template")   
<a class="addCSVField btn success">Add another field</a>

И добавить стиль CSS .CSVField_template{display: none;}

Я ничего из этого не тестировал, поэтому может не скомпилироваться. Однако я просто следовал тому же подходу, что и в Пример форм (воспроизведение 2.2.x

person Didac Montero    schedule 16.02.2015
comment
На самом деле я построил свое приложение на основе этого примера формы. Я пытался использовать блок repeat, как вы сказали, но это дает мне ошибку проверки. Смотрите мое редактирование для получения дополнительной информации - person KJ50; 17.02.2015
comment
Проверьте мой ответ. Внутри @multiDataField вы должны указать два элемента вашего объекта CSVField: имя и тип. - person Didac Montero; 17.02.2015
comment
Большое спасибо! Я не знал, как указать синтаксис @csvField("name").name, но теперь это имеет смысл. Проблема, с которой я столкнулся сейчас, заключается в том, что мой код jQuery для динамического добавления дополнительных полей очень наивен и клонирует предыдущий элемент. Таким образом, они оба получают name="fields[0].name" в качестве атрибута name. Знаете ли вы, есть ли в Play какие-либо помощники javascript для таких ситуаций? Возможно, что-то, что работает с помощником @repeat? - person KJ50; 18.02.2015
comment
Проверьте мой комментарий. Там у вас есть немного более надежный код. Я рекомендую вам проверить этот пример - person Didac Montero; 18.02.2015
comment
Оказалось, что главное, что мне было нужно, это функция renumber(), так что этот пример был очень полезен. Еще раз спасибо! - person KJ50; 21.02.2015