Как получить ссылку на элементы формы в сценарии Jenkins jelly для конфигурации Builder при загрузке формы?

Я пишу Jenkins Builder, и в сценарии желе для его настройки на странице конфигурации сборки у меня есть некоторый Javascript, который я хочу запустить при загрузке формы, чтобы выполнить поиск на сервере и получить некоторую информацию, чтобы помочь пользователю с их настройка, которая также будет выполняться при изменении пользователем значений формы.

Раньше я получал ссылки на элементы формы, передавая this функциям в атрибутах onchange или onkeyup. Однако теперь я хочу запустить какой-то скрипт, даже если форма не изменилась.

Я знаю, что могу установить атрибуты ID для элементов формы, однако это не сработает, если пользователи добавят в сборку два этапа сборки, используя этот конструктор.

Я попытался сгенерировать случайный идентификатор в своем классе строителя, а затем использовать его для создания идентификаторов для элементов и записать его в некоторый Javascript в файле желе, чтобы я мог найти там эти элементы, но это не инициализируется до тех пор, пока пользователь сохраняет, поэтому он не будет работать, если пользователь добавит два экземпляра этого построителя без сохранения задания:

<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:f="/lib/form">
  <f:entry title="Entry 1">
    <f:textbox field="field1" id="${instance.id}-field1" onchange="fieldChanged('${instance.id}-field1')"/>
  </f:entry>
  <script type="text/javascript">
    function fieldChanged(elementId) {
      ...
    }

    fieldChanged('${instance.id}-field1');
  </script>
</j:jelly>

Существуют ли какие-либо соглашения о том, как делать подобные вещи? Что-нибудь встроенное в Jenkins/jelly для поддержки нескольких экземпляров одного и того же файла jelly, которые могут ссылаться на свои собственные элементы?


person Martin Pain    schedule 03.03.2015    source источник


Ответы (2)


Есть решение с использованием j:set, которое проще, чем мой другой ответ.

com.example.MyBuilder.DescriptorImpl:

private int lastEditorId = 0;
...
@JavaScriptMethod
public synchronized String createEditorId() {
    return String.valueOf(lastEditorId++);
}

com/example/MyBuilder/config.jelly:

...
<j:set var="editorId" value="${descriptor.createEditorId()}" />
<f:entry title="Field">
    <f:textbox field="field" id="field-${editorId}"/>
    <p id="message-${editorId}"></p>
</f:entry>
<script>
    setTimeout(function(){
        var field = document.getElementById('field-${editorId}');
        var p = document.getElementById('message-${editorId}');
        p.textContent = "Initial value: "+field.value;
    }, 50);
</script>

(Вызов setTimeout по-прежнему связан с тем, что при добавлении новых шагов сборки элементы не были добавлены в DOM к моменту выполнения скрипта, поэтому выполнение скрипта нужно немного отложить).

person Martin Pain    schedule 04.03.2015

Похоже, что это решение ниже может работать, но я еще не очень далеко продвинулся с ним.

В моем классе строителя я добавил внутренний класс под названием Editor:

com.example.MyBuilder(.Editor):

...
public static class Editor {
    private final String id;

    public Editor(final String id) {
        this.id = id;
    }

    public String getId() {
        return id;
    }
}
...

Затем в классе Java-дескриптора предоставьте функцию JavaScript для создания одного из них с уникальным идентификатором:

com.example.MyBuilder.DescriptorImpl:

    private int lastEditorId = 0;

    @JavaScriptMethod
    public synchronized Editor createEditor() {
        return new Editor(String.valueOf(lastEditorId++));
    }

Затем в моем файле желе я вызываю этот метод и передаю возвращенный объект в st:include, загружая новый файл желе для отображения полей:

com/example/MyBuilder/config.jelly:

<st:include page="editor.jelly" it="${descriptor.createEditor()}" />

(Хотя кажется, что это должно быть внутри элемента f:entry - или, возможно, других элементов, я не пробовал - в противном случае, похоже, он не включается, когда в конфигурацию задания добавляется новый шаг сборки для этого компоновщика.)

И, наконец, я создаю этот новый файл editor.jelly для отображения полей (который должен находиться в папке, имя которой отражает класс Editor, поскольку объект it, передаваемый в st:include, имеет тип Editor):

com/example/MyBuilder/Editor/editor.jelly:

<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:f="/lib/form">
<l:ajax>
    <f:entry title="Field">
        <f:textbox field="field" id="field-${it.id}"/>
        <p id="message-${it.id}"></p>
    </f:entry>
    <script>
        setTimeout(function(){
            var field = document.getElementById('field-${it.id}');
            var p = document.getElementById('message-${it.id}');
            p.textContent = "Initial value: "+field.value;
        }, 50);
    </script>
</l:ajax>
</j:jelly>

(Вызов setTimeout связан с тем, что при добавлении новых шагов сборки элементы не были добавлены в DOM к моменту выполнения скрипта, поэтому выполнение скрипта нужно немного отложить).

Однако это разрывает связь между элементами f:entry и эквивалентными полями в классе построителя, и я не уверен, что с этим делать. Так что это неполный ответ.

EDIT: я не уверен, сработали бы элементы f:entry или нет, так как я забыл добавить поле в класс построителя, когда тестировал его, что было (по крайней мере, одна причина) почему я не видел никаких данных, сохраненных из этого поля, когда пробовал это. Однако сейчас я использую решение из моего другого ответа, поэтому я не вернулся, чтобы проверить, сработало бы оно или нет.

person Martin Pain    schedule 04.03.2015