-
Task
-
Resolution: Not A Defect
-
Minor
-
None
-
Jenkins 2.32.2, Active Choices 1.5.2, Chrome v. 56
-
Powered by SuggestiMate
Using an Active Choices Reactive Reference Parameter and having it return Formatted HTML with a script portion (onchange, etc...) works great unless the groovy script references other dependent build parameters such as strings, Active Choice parameters, etc... The returned HTML renders properly, just without the script. Attached is a (hopefully) very simple job configuration to demonstrate.
The groovy script for generating the sample test is as follows (DummyVar is a string parameter defined just above this parameter):
//Uncomment this line, causing DummyVar to be referenced, and the script will be lost at runtime. //def a = DummyVar def FullHTML = "" FullHTML += "<head>" FullHTML += "<script type='text/javascript'> jQuery(document).on('ready', function() { function updateOutputValue() { jQuery('#value').val('Booya!'); };" FullHTML += " updateOutputValue();" FullHTML += "});" FullHTML += "</script>" FullHTML += "</head>" FullHTML += "<body><form><input id='value' name='value' > </form></body>" return FullHTML
[JENKINS-42034] Reactive Reference parameter with Formated HTML loses script section when binding to other build parameters
kinow I think this works as expected. I have attached a new config[1].xml where I simplified the 'ScriptVariables' parameter script so I can easily explain what I think it's going on.
Here is the simplified script:
def a = DummyVar def FullHTML = """ <script type='text/javascript'> jQuery(document).on('ready', function() { function updateOutputValue() { jQuery('#value').val('Booya!'); }; updateOutputValue(); }); </script> <input id='value' name="value" type="text" class="setting-input " value="$a"> """ return FullHTML
The original script assigned a value through the jQuery selector on page load.
That is the key point I think. The page load script only runs when the build form is first loaded. Not when the parameter is refreshed on a cascade event.
So if you are not referencing another parameter the page loads and the script sets the value to 'Booya'. However, if you are referencing another parameter it triggers an update event as soon as the DummyVar is set. Since the original script did not use the DummyVar then the returned <input> value was null.
In this example, where I'm using the DummyVar value you see that it updates the <input> value to the DummyVar value
Great explanation - as always - Ioannis. Thanks for taking a look at this issue. I think we can mark it as Won't Fix (you were faster than me ), and wait for bmatzen's feedback.
ps: Releasing 1.5.3 to update centre in 15 minutes
I really appreciate you guys investigating this. I absolutely love this plugin and really get the most out of it, but I'm also pretty novice with Javascript, so I appreciate the help differentiating between Javascript issues versus issues with the actual plugin. BTW I appreciate you pointing out the simplified triple quote syntax for the HTML - much simpler!
I've reviewed your feedback, and see that while your solution handles the sample I gave, my sample was pretty stripped down from what I'm really doing, and I need to make an update to help me do what I really want to do. I consulted a co-worker who is much more savvy with Javascript, and have come up with a modified version of your script above, which I would have expected would work, but is not.
def FullHTML = """
<script type='text/javascript'>
function updateOutputValue(NewVal) {
jQuery('#value').val(NewVal);
};
// Handler for the document loading
jQuery(document).on('ready', function() {
alert( "Handler for .ready() begin." );
updateOutputValue('We are live');
alert( "Handler for .ready() end." );
});
// Handler for the 'setting-input' class, of which DummyVar is a part.
jQuery('.setting-input').change(function() {
alert( "Handler for setting-input .change() begin." );
updateOutputValue('DummyVar got updated');
alert( "Handler for setting-input .change() end." );
});
</script>
<input id='value' name='value' type='text' class='setting-input' value='default value'>
"""
return FullHTML
What I'm expecting to happen is for 'value' to first have 'default value', then get updated to 'We are live'. This is working properly.
Then if/when DummyVar changes (and I leave the text field), I'd expect 'value' to get 'DummyVar got updated'. Instead what I'm seeing is, although I get the Javascript alerts for changes to '.setting-input' class, instead of 'value' changing to 'DummyVar got updated', or even staying the same (no effect), it's reverting to 'default value'. Although I get the Javascript alerts for changes to '.setting-input' class, and see 'value' flash to 'DummyVar got updated', it's immediately reverting to 'default value'.
Alternatively if I remove the default value (i.e. have "<input id='value' name='value' type='text'>"), changes to DummyVar cause 'value' to clear out. So either way it's as though the control gets reloaded.
Hopefully it's clear to you guys what's happening here - quite likely the issue has to do with my script and the way Active Choices interacts with it - but I'm hoping you can clear things up and with luck steer me to some changes to my script to resolve this.
Note that when I use this script outside of Jenkins, simply in its own HTML file, 'value' updates properly per my expectations. Here is that script:
<head>
<input id='DummyVal' name='DummyVal' type='text' class='setting-input' value='asdf'><br>
<input id='value' name='value' type='text' class='setting-input' value='default value'>
<script src='http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js' type='text/javascript'></script>
<script type='text/javascript'>
function updateOutputValue(NewVal) {
jQuery('#value').val(NewVal);
};
jQuery(document).on('ready', function() {
alert( "Handler for .ready() begin." );
updateOutputValue('We are live');
alert( "Handler for .ready() end." );
});
jQuery('.setting-input').change(function() {
alert( "Handler for setting-input .change() begin." );
updateOutputValue('DummyVar got updated');
alert( "Handler for setting-input .change() end." );
});
</script>
</head>
</body>
Thanks again for investigating, and again I'm so appreciative of this plugin.
Thanks,
Bill
Hi Ioannis, I was wondering if you'd had a chance to review this, and could re-open this issue. We heavily rely on this plugin, and having this functionality resolved (whether it's a change/fix to the plugin or a change to my Javscript if perhaps there's something I can do on my end) would allow us to move forward with some powerful user interfaces, but we're currently blocked on this. Thank you for all of your help so far, and this great plugin.
BTW, I'm happy to create a new ticket that better describes the issue, has a better title, etc, if that helps.
Bill
Hi bmatzen; It would be much easier for me to review this if you can provide an example project configuration file. Strip it to the bare minimum to demonstrate the problem and your expectation.
A couple things to keep in mind when you are working with an Active-Choices parameter that returns formatted HTML that contains also javascript.
- Separate javascript from HTML. Create an AC-Ref with just the <script> tag. This should not reference any other parameters and it's just used to load the javascript on the page
- I never use head and body elements. These are loaded by the Jenkins build form so I don't know what effect they have
- Make sure that the HTML input elements displayed with an AC-Reference parameter have an attribute name="value" if you want their value returned by the AC parameter
- You can actively change such values via javascript but make sure that you trigger a change event so that the new values get propagated to downstream AC parameters that reference the one that was changed. See: also https://issues.jenkins-ci.org/browse/JENKINS-33326
Thank you Ioannis, I've just attached config.xml per your request. Hopefully this demonstrates the issue. I reviewed your list of things to keep in mind - I believe I'm following 1-3 properly but please correct me if I'm wrong. Number 4 was interesting, but I don't believe it solves the issue. Regardless of whether I manually update the first (referenced) text field (named 'DummyVar' in my example project) or use the .trigger('change') event, the behavior is the same. After the alert popups for the change handler, the referencing (downstream) parameter first flashes to the expected value ('DummyVar got updated') but then immediately changes back to the default from the HTML definition ('default value').
I could really use some help on this one.
Thanks,
Bill
Hi Bill;
You are indeed violating rule #1 since the javascript and the <input> element that provides the value to your parameter are not separated but reside in the same AC Reactive Ref (ScriptVariables). As a result the javascript is reloaded when your DummyVar changes. I believe that what you are observing are:
- First the action from the loaded javascript that flashes what you expect
- Second the action from the update of the ScriptVariables parameter that
- reloads the java script
- sets the input value to 'default value'
I believe I have narrowed down what is happening here, and have simplified some of the "noise" so hopefully that helps.
What I'm seeing is that the groovy script for each AC-Reactive Reference is run TWICE. Once where the referenced parameters DO NOT exist (i.e. binding.variables.containsKey() returns false for referenced parameters) and a second time where the referenced parameters DO exist. Although I return Javascript both times (at least the code tries to), I've found that the Javascript I return the first is what gets used.
I've attached (config-doubleload.xml) a sample job ("ScriptIssue") that demonstrates this. I return Javascript that has a a document "ready" handler that simply does an alert popup. The text from the alert indicates whether it was from the first load of the parameter or the second load (based on whether the referenced parameter - "StringParameter" - existed). Again what I'm seeing is it's the first load's returned Javascript that gets used (so the popup shows "GroovyScript1's default handler for .ready()", as opposed to "GroovyScript1's updated handler for .ready()"). I've added a second build parameter (GroovyScript2) of the same type with the same script except it doesn't directly reference "StringParameter". Nevertheless it behaves the same, so it doesn't actually have anything to do with the dependency, but simply the first time the script is run - that's the Javascript that gets used.
I included one more scenario - parameter "GroovyScript3" - which DOES NOT return any Javascript the first time, but does the second time. In this case, the Javascript returned doesn't seem to take affect (no alert for it). So again it follows the trend where only the Javascript returned when the build parameter's groovy script is first run that is used.
I have println statements in the groovy scripts of the attached config file, and these of course print to jenkins.out.log, where this is what gets printed:
Loading parameter for GroovyScript1: StringParameter doesn't exist
Loading parameter for GroovyScript2: StringParameter doesn't exist
Loading parameter for GroovyScript3: StringParameter doesn't exist (not returning any Javascript)
Loading parameter for GroovyScript1: StringParameter exists
Loading parameter for GroovyScript2: StringParameter doesn't exist
Loading parameter for GroovyScript3: StringParameter exists
I'm pretty certain this is the root cause of what I've been fighting with for all these months - hopefully you guys can confirm this, or straighten me out if I'm doing something wrong. ** Thanks for your help on this!
Thanks,
Bill
I can duplicate this in another environment that has nothing to do with Javascript. In the attached config-5-4-2017 file, I have a simple job with a string parameter ("RefA") and an AC Reactive Parameter. The AC Reactive Parameter has a groovy script that gets run twice, once where RefA is unknown, and once where it is known, in that order. I return a list with just one item ("What's RefA") if RefA is unknown, whereas if it is known, I load an XML snippet, and add a value from there into the list that I return. The logging in jenkins.out.log proves that it's running twice, and in the second time, RefA is known and I am putting a proper value into the list that I return. Nevertheless the UI shows "What's RefA", which was populated on the first groovy run.
What's more, if I simply comment out the line that adds the XML value to the list (in the second run, where RefA is known), that resulting list is the one the UI shows! Of course in that case it's an empty dropdown, but adding a fake value also shows. Note that this appears to be the same issue as JENKINS-37935, which was closed a "Cannot reproduce".
Thanks,
Bill
Hi, I wanted to check in to see if you were able to reproduce this at all. I understand if you haven't been able to get to this, but I was hoping to at least know if you were able to understand the issue better with my latest update. I love this plugin and use it a ton, and am hoping to see this issue resolved (or my code fixed if that's what is needed) to move forward with using it more extensively.
Thanks,
Bill
bmatzen With a small change (highlighted) it all works as expected.
You got to insure that the returned array is an array of Strings (not XML nodes as it was before). Then I get this in the UI
Hope it helps. I'm not sure if the script is run multiple times. I can check with kinow
Hi Ioannis, thanks for taking a look, unfortunately this doesn't resolve the issue. As you can see in your output, it's still showing "What's RefA" in the server list, which means it's not working as expected. If you review my last post, I go into more detail. You mentioned you could check with Bruno about whether the script runs multiple times - would you please do that?
Thanks,
Bill
Not sure what the issue is about exactly. Had a quick look at the issue, but didn't have enough time to read all the comments.
If someone could summarize the issue, maybe I can help? We have 4 small/medium issues fixed already, besides the security issue in Scriptler that is blocking our release.
In the meantime I think we can squeeze a few more issues likes this for the next release
I would be so appreciative if you could take a look into this one. I understand the comment thread is pretty long and has evolved a bit from the original message. But if you focus on the one from 5/4/2017 (beginning with "I can duplicate this in another environment that has nothing to do with Javascript..."), that is the most isolated/concise example, and the root issue that is causing me grief. If this could be working around or dare I say "fixed" (if it is truly a defect), we would be able to do some really cool things with this plugin (beyond what we're already doing!).
Hi bmatzen, thanks a lot for attaching a config.xml file. It saves me minutes, and sometimes hours, trying to reproduce the issue
In less than 5 minutes I successfully reproduced your issue. However, at least in Eclipse I can see a serialization exception, where Jenkins' Stapler (its web framework), or more specifically one of its dependencies responsible for parsing, complains about a certain type of object that it could not understand.
The issue is, from what I could tell, in this part of your active-choices script:
a.b.c.@Name.each{{{}}
println "Found name '" + it
names << it}
The `it` variable in the loop will be an instance of `groovy.util.slurpersupport.Attribute`. You are adding it to the array names (which doesn't use any type as with generics in Java).
The array of `groovy.util.slurpersupport.Attribute` objects is returned to the client, but the framework fails to serialize them, and so you only see the old value, that was calculated in the first time the parameter was executed.
To solve the issue, you have to make sure to send collections of strings, or elements that the framework will successfully serialize as strings (better stick to always returning strings if possible). Here's a quick way to fix it:
a.b.c.@Name.each{{{}}
{{ println "Found name '" + it}}
{{ names << it.text()}}
}
The `.text()` method is documented here.
Tested on Eclipse IDE, with the latest version from git master branch, but I believe it should work with 1.5.3 as well. Thanks a lot for your patience, and let us know if that works!
Cheers
Bruno
JIRA is not happy with my formatting, sorry. Bill, hope you know which part of the code I'm referring to. Let me know if you want me to upload my updated config.xml. Ignore the extra { }`s that JIRA keeps adding to my preformatted blocks (easier to work on Java code than on this JIRA editor )
Hi Bruno, thank you for your investigation into this - this does in fact resolve that sample. and I'll watch out for those types of issues in the future.
The prior example (2017-05-03 16:47), however, remains an issue, and appears unrelated to this. This does go back to Javascript so a little more complex, but if you wouldn't mind focusing in on that comment / attachment, that remains a sticking point for me. If you could also touch on the "double load" behavior - that strongly plays into this - that would be informative. Again, thank you so much for this plugin and for investigation my issues.
Thanks,
Bill
Hi bmatzen, glad it worked.
Searching for 2017-05-03 on this ticket doesn't return anything for me. In your previous comments, only when you quoted a part of the text I could find the attachment.
I *think* JIRA takes into consideration each user's time zone when displaying comments and attachments. As I live in a weird time zone, probably what I see is different than most others here Could you point to a file name, or quote the beginning of the comment, please, Bill?
I will try to chase what's going on in the JavaScript layer. I am aware of parameters being evaluated twice. But as it is normally harmless, and I never found a simple explanation, I've been postponing working on this. Maybe now we can spend some time investigating it together.
Thanks and sorry for the mess with time zones & comments.
Bruno
Sorry about that Bruno - ya, the one that starts out "I believe I have narrowed down what is happening here" and references "config-doubleload.xml"...
Thanks,
Bill
Not a problem, and thanks for the prompt reply.
Here's what is says for me on that comment: bmatzen added a comment - 2017-05-04 11:47, so I believe JIRA is indeed storing some UTC value and then rendering the currentUser() tz time. Would be good to have a comment ID somewhere I think... sorry for digressing.
Will update the ticket once I have time to investigate it (bit busy with the biouno website today)
Bruno
Hmmm, did some initial analysis, and there are a few different ways to fix it. I think the scripts are evaluated many times because we have, for the cascade parameters and for dynamic parameters, they are updated once they are displayed on the UI.
There is an old issue, somewhere in JIRA (bad memory, sorry) where users had to click somewhere to active check boxes or radio buttons. It is tricky to control the DOM creation and the dynamic parameters. So it was the easiest solution at that time (I think it was after a major refactoring in the code, when it was uno-choice, in our own update centre).
Now it looks like we need to properly fix it. I will log an issue for that, linking to this one. Feel free to watch and comment on that issue.
What I have in my mind as a possible and good solution is:
- Create a graph/tree of all parameters, hierarchically
- Walk the graph/tree, evaluating each parameter
- Re-use this graph model to evaluate active-choices parameters without an UI
This last option has been on my mind for a while. There is another old issue, to support timers/pipelines/cron jobs/etc. But given the current nature of the plug-in, it is impossible to evaluate plug-ins without an UI.
With this graph/tree model, we would solve the issue of evaluating parameters once in the UI (DOM/JS/HTML), and also adding the ability to have new backend parameters (Java only).
This would be great Bruno - I see the new issue (JENKINS-45507) and will monitor that for updates. I'm also more than happy to test anything out before you officially release any update. Thank you so much for investigating this and getting to the root issue.
Thanks,
Bill
I'm not sure referencing the parameter is the problem. Your script is returning HTML with a <script> tag. The plug-in includes the HTML you provided using innerHTML. That may be the problem.
ioannis has way more experience than I using JavaScript with the reactive parameters. Ioannis, could you take a look at this script, please? Would be great if you could confirm it there's a bug in the plug-in behaviour. Looking at the code, I can't really understand what's going on, except that script tag is never evaluated, as far as I can tell.