如何在加载表单时获得对Builder配置的Jenkins果冻脚本中表单元素的引用



我正在编写一个Jenkins Builder,在构建配置页面中用于其配置的果冻脚本中,我有一些Javascript,我想在加载表单时运行它,以进行服务器查找并获取一些信息来帮助用户进行配置,这也将在用户更改表单值时执行。

以前,我通过将this传递给onchangeonkeyup属性中的函数来获得对表单元素的引用。然而,现在我想运行一些脚本,即使表单没有更改。

我知道我可以在表单元素上设置ID属性,但如果用户在构建中添加两个构建步骤,都使用这个构建器,那就行不通了。

我试着在我的构建器类上生成一个随机ID,然后用它来构建元素的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文件的多个实例能够引用它们自己的元素?

有一个使用j:set的解决方案,它比我的其他答案更简单。

com.example.MyBuilder.DescriptorImpl:

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

com/example/MyBuilder/config.elly:

...
<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中,因此脚本执行必须稍微推迟)。

下面的这个解决方案看起来可能可行,但我还没有走多远。

在我的构建器类中,我添加了一个名为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函数来创建其中一个具有唯一ID的函数:

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.elly:

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

(尽管这似乎必须在f:entry元素中——或者可能是其他元素中,我还没有尝试过——否则,当将该构建器的新构建步骤添加到作业配置中时,它似乎不会被包括在内。)

最后,我创建了新的editor.jelly文件来渲染字段(它必须位于一个文件夹中,该文件夹的名称反映了Editor类,因为传递到st:includeit对象的类型是Editor):

com/example/MyBuilder/Editor/Editor.jeelly:

<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元素和构建器类中等效字段之间的链接,我不知道该怎么办。所以这是一个不完整的答案。

编辑:我不确定f:entry元素是否有效,因为我在测试它时忘记了将字段添加到生成器类中,这是(至少一个原因)为什么我在尝试此操作时没有看到从该字段保存的任何数据。然而,我现在使用的是另一个答案中的解决方案,所以我没有回去测试它是否有效。

最新更新