我正在编写一个Jenkins Builder,在构建配置页面中用于其配置的果冻脚本中,我有一些Javascript,我想在加载表单时运行它,以进行服务器查找并获取一些信息来帮助用户进行配置,这也将在用户更改表单值时执行。
以前,我通过将this
传递给onchange
或onkeyup
属性中的函数来获得对表单元素的引用。然而,现在我想运行一些脚本,即使表单没有更改。
我知道我可以在表单元素上设置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:include
的it
对象的类型是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
元素是否有效,因为我在测试它时忘记了将字段添加到生成器类中,这是(至少一个原因)为什么我在尝试此操作时没有看到从该字段保存的任何数据。然而,我现在使用的是另一个答案中的解决方案,所以我没有回去测试它是否有效。