sourcesense/jira-multi-level-cascade-select — Issue #14: Fields not set correctly on "Edit" screen
Author: RandallGee6845464839
Created: 2011-11-02 05:06:47+00:00
State: open

I'm using cascadingselect-2.5-SNAPSHOT-bundle.zip with Jira 4.4.

I'm currently using Firefox 7.0.1 on Mac OS X, but I've seen this on other browsers on other platforms.

I see problems editing an issue with a 3 or more level cascaded select field. When I set values in that field, and then I go to edit the bug, I expect to see that field represented by a series drop-down menus, and those menus should initially be set to the same values as the actual field.

Instead, when I go to the edit screen, I find that the 1st level is set correctly, but the 2nd and subsequent levels are incorrectly set to the first item in the menu, or blank.

For example, a custom field be set to "Hardware - Networking - Switch - PCA", but when I try to edit the issue, I see instead "Hardware - Access point - (blank) - Addons".

I did some investigating, and it turns out this issue is entirely one of presentation. The bug lies in the Javascript used to set and update the drop-down menus once the page is loaded.

The page uses Javascript to display the cascading fields, so that the selections in the right hand fields can change when you select something in the left hand field. That is, when you make a selection on a field, it triggers a bit of Javascript which edits the field to the right.

When the page is initially loaded, it first renders all the selections, and then issues a Javascript command to set each of the fields in turn. Each time it sets a field, it triggers the change for the field on the right, so that when it goes to set that field, all the options are already in their proper place.

The problem is that it's using triggers to make these changes, and it's assuming that all the triggers will go off before it goes to set the next field. And that's not necessarily true! What actually happens is that it's setting the first field, and then it sets off the event that will trigger the code to change the next field. But before the next field actually gets changed on that trigger, the code goes on and sets the next field, which triggers a change to the third field. After all this happens, the event gets handled, and it is not until now that we actually get to the code that was fired off by the first trigger, and that resets the second field. So the second field was actually correct for a few milliseconds, but by the time the user sees it, it's wrong.

You're not going to get this problem when you change the fields manually, since you can't change the fields this fast. It only happens on the page load.

Now, I think that under jquery 1.3.2, all the event handling and trigger firing takes place in the order in which the author intended, and everything works. You can actually see in the HTML source of the edit screen that it tries to load "/includes/js/jquery-1.3.2.min.js". (You should get rid of that, by the way.) But the Atlassian UI that Jira 4.4 uses already includes a later version of jquery, and the code in multilevelcascadingUtil.js doesn't work with that version. (And many parts of the Atlassian UI won't work with jquery 1.3.2.)

I was able to fix this by editing multilevelcascadingUtil.js to not explicitly trigger events on the initial page load. Here's the diff:

--- multilevelcascadingUtil.js-original 2011-04-27 11:49:12.000000000 -0400
+++ multilevelcascadingUtil.js 2011-11-01 01:21:15.851220817 -0400
@@ -1,10 +1,15 @@
+var masterSlaveMap = {};
+var slaveOptionsMap = {};
+
function dynamicMultiLevelSelect(masterId, slaveId) {
var masterSelector = "select#" + masterId;
var slaveSelector = "select#" + slaveId;
+ masterSlaveMap[masterSelector] = slaveSelector;
jQuery(document).ready(function($) {
// Code that uses jQuery's $ can follow here.

var slaveOptions = $("option", slaveSelector);
+ slaveOptionsMap[slaveSelector] = slaveOptions;
$(masterSelector).change(
function(event) {
$(slaveSelector).hide();
@@ -50,9 +55,39 @@
var option = $("option[value='"+value+"']", select);
if (option.size()>0) {
$(select).attr("selectedIndex", option.get(0).index);
- $(select).trigger("change");
- } else {
- clickFirstVisible(select);
+ while (select in masterSlaveMap) {
+ select = masterSlaveMap[select];
+
+ // selectOption is only called on the page load. We don't
+ // want to trigger anything on page load, since we can't
+ // guarrantee that things will get executed in the correct
+ // order. So we crib code from dynamicMultiLevelSelect
+ // but we use a loop instead of depending on the change event.
+
+ var slaveOptions = slaveOptionsMap[select];
+ $(select).hide();
+ $(select).empty();
+ var options = slaveOptions.filter("."+value);
+ options.filter("[value='-1']").each(
+ function() {
+ $(select).append(this);
+ }
+ );
+ slaveOptions.filter(".select").each(
+ function() {
+ $(select).append(this);
+ }
+ );
+ options.filter("[value!='-1']").each(
+ function() {
+ $(select).append(this);
+ }
+ );
+
+ $(select).show();
+ $(select).attr("selectedIndex", 0);
+ value = $(select).val();
+ }
}
});
-}
\ No newline at end of file
+}

You could make the Javascript more elegant if you changed the way selectOption is called, but that would require changing the Java code, and I didn't want to do that for my installation.