1

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

以前我通过传入this函数onchangeonkeyup属性来获得对表单元素的引用。但是,现在我想在表单没有改变的情况下运行一些脚本。

我知道我可以在表单元素上设置 ID 属性,但是如果用户都使用此构建器将两个构建步骤添加到构建中,这将不起作用。

我尝试在我的构建器类上生成一个随机 ID,然后使用它来构造元素的 ID,并将其写入 jelly 文件中的一些 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 内置的任何东西来支持同一个果冻文件的多个实例能够引用它们自己的元素?

4

2 回答 2

1

有一个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,因此脚本执行必须稍微延迟)。

于 2015-03-04T13:15:06.907 回答
1

It looks like this solution below might work, but I haven't got very far with it yet.

In my builder class I've added an inner class called 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;
    }
}
...

Then in the descriptor Java class, provide a JavaScript function to create one of these with a unique ID:

com.example.MyBuilder.DescriptorImpl:

    private int lastEditorId = 0;

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

Then in my jelly file I call that method and pass the returned object into st:include, loading a new jelly file to render the fields:

com/example/MyBuilder/config.jelly:

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

(Although this appears to have to be inside an f:entry element - or perhaps other elements, I haven't tried - otherwise it doesn't seem to get included when a new build step for this builder is added to the job config.)

And finally I create that new editor.jelly file to render the fields (which has to be in a folder whose name reflects the Editor class, as the it object being passed into st:include is of type 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>

(The call to setTimeout is due to the fact that when adding new build steps, the elements haven't been added to the DOM by the time that the script executes, so the script execution has to be deferred slightly).

However, this breaks the link between the f:entry elements and the equivalent fields in the builder class, and I'm not sure what to do about that. So this is an incomplete answer.

EDIT: I'm not sure if the f:entry elements would have worked or not, as I had forgotten to add the field to the builder class when I was testing it, which was (at least one reason) why I did not see any data saved from this field when I tried this. However, I am now using the solution from my other answer, so I have not gone back to test whether it would have worked or not.

于 2015-03-04T00:57:08.420 回答