Parts of javascript not working in issue navigator

Happy new year everyone! It’s a new year and I have some new problems with which I could use some help.

I have some javascript in my addon which doesn’t work in the navigator but does work when I’m directly viewing the issue. Or atleast some parts of the javascript won’t work. The AJS.$(document).on('change keyup keydown paste cut', 'textarea', function () works after I add something to the textarea but outside of the navigator it changes size automatically. The rest of my code simply doesn’t run.

I’ve tried changing and adding tags in my web-resource but it doesn’t seem to do the trick for me.

Can someone help me figure out what I’m doing wrong? Or atleast point me in the right direction?

My javascript file:

AJS.toInit(function($) {

    AJS.$(document).on('change keyup keydown paste cut', 'textarea', function () {
        $(this).height(0).height(this.scrollHeight);
    }).find('textarea').change();

    AJS.$('.customer-note__expand-note').click(function(){
        AJS.$header = AJS.$(this);
        AJS.$content = AJS.$header.next();
        AJS.$content.slideToggle(0);
        AJS.$(this).toggleClass('customer-note_head-collapse');
        AJS.$(this).toggleClass('customer-note_head-expand');
    });

    AJS.$('.customer-note__button').click(function(){

        var id = AJS.$(this).attr('id').replace("saveButtonTIG", "");

        console.log(id);
        console.log("noteInputTIG"+id);

        data = {};
        data.content = document.getElementById("noteInputTIG"+id).value;
        data.domain = document.getElementById("domainInputTIG"+id).value;
        console.log(document.getElementById("reporterInputTIG"+id).value);
        console.log(document.getElementById("noteInputTIG"+id).value);
        console.log(document.getElementById("domainInputTIG"+id).value);
        console.log(JSON.stringify(data));

        AJS.$.ajax({
            url: AJS.params.baseURL + "/rest/noteresource/1.0/note/" + document.getElementById("reporterInputTIG"+id).value,
            type: "POST",
            data:JSON.stringify(data),
            dataType: "json",
            contentType: "application/json; charset=utf-8",
            success: function(msg){
                console.log("Success !");
            }
        });
    });
});

Atlassian-plugin.xml

<?xml version="1.0" encoding="UTF-8"?>

<atlassian-plugin key="${atlassian.plugin.key}" name="${project.name}" plugins-version="2">
  <plugin-info>
    <description><![CDATA[
               <p>Add notes to the issue-view and manage who can view them!</p>
               <p>For further instructions and troubleshooting please refer to <a href="https://tig.nl/jira" target="_blank" rel="noopener noreferrer">https://tig.nl/jira</a>.</p>
            ]]></description>
    <version>${project.version}</version>
    <vendor name="${project.organization.name}" url="${project.organization.url}"/>
    <param name="plugin-icon">images/note_logo_72x72.png</param>
    <param name="plugin-logo">images/note_logo.png</param>
    <param name="vendor-icon">images/tig_jira_v2.png</param>
    <param name="vendor-logo">images/tig_logo_72x72.png</param>
  </plugin-info>
  <!-- add our i18n resource -->
  <resource type="i18n" name="i18n" location="tigNoteExtension"/>

  <!-- add our web resources -->
  <web-resource key="tigNoteExtension-resources" name="tigNoteExtension Web Resources">
    <dependency>com.atlassian.auiplugin:ajs</dependency>
    <context>atl.general</context>
    <context>jira.navigator.advanced</context>
    <context>jira.navigator.simple</context>
    <resource type="download" name="tigNoteExtension.css" location="css/tigNoteExtension.css"/>
    <resource type="download" name="tigNoteExtension.js" location="js/tigNoteExtension.js"/>
    <resource type="download" name="images/" location="/images"/>
  </web-resource>

  <web-panel name="IssuePageInput" i18n-name-key="issue-page-input.name" key="issue-page-input" location="atl.jira.view.issue.right.context" weight="1">
    <description key="issue-page-input.description">User notes for Jira</description>
    <context-provider class="tig.jira.extension.tigNoteExtension.NoteContextProvider"/>
    <resource name="view" type="velocity" location="/vm/note-input.vm"/>
    <label key="issue-page-input.title"/>
  </web-panel>
  <ao key="ao-module">
    <description>The configuration of the Active Objects service</description>
    <entity>tig.jira.extension.tigNoteExtension.entity.Note</entity>
  </ao>
  <rest name="Note Resource" i18n-name-key="note-resource.name" key="note-resource" path="/noteresource" version="1.0">
    <description key="note-resource.description">The Note Resource Plugin</description>
    <package>tig.jira.extension.tigNoteExtension.rest</package>
  </rest>

</atlassian-plugin>

velocity file

$webResourceManager.requireResourcesForContext("tigNoteExtensionResource")

<div id="tigNoteExtensionContent" class="dont-default-focus ajs-dirty-warning-exempt">
    <img alt="" src="images/note_logo.png"
         style="height: 50px; width: 50px" id="imgVIP"/>
    <img src="$req.contextPath/download/resources/tig.jira.extension.tigNoteExtension:tigNoteExtension-resources/images/note_logo_72x72.png" id="imgVIPTrue"/>
    <p>$i18n.getText("tigNoteExtension.toptext") $reporterName $domain</p><br/>
        <div class="customer-note__head-background">
            <h2 class="customer-note__expand-note customer-note__head customer-note_head-expand">$reporterId</h2>

        <div class="customer-note__content-div">

            <p>Notities van $reporterId</p><br/>
            <textarea id="noteInputTIG" class="customer-note__content customer-note__content--editable ajs-dirty-warning-exempt" name="noteInputTIG" placeholder="$i18n.getText("tigNoteExtension.placeholder")">$content</textarea>
            <input class="hidden" id="reporterInputTIG" name="reporterInputTIG" value="$reporterId"></input>
            <input class="hidden" id="domainInputTIG" name="domainInputTIG" value="$domain"></input>
            <button id="saveButtonTIG" class="customer-note__button aui-button" name="saveButtonTIG">$i18n.getText("tigNoteExtension.save")</button>
        </div></div><br/>


        #foreach ($item in $noteArray)
            #if ($item.getCurrentReporter() == false)
                <div class="customer-note__head-background">
                    <h2 class="customer-note__expand-note customer-note__head customer-note_head-expand">$item.getUserId()</h2>

                <div class="customer-note__content-div">

                    <p>Notities van $item.getUserId()</p><br/>
                    <textarea id="noteInputTIG$item.getId()" class="customer-note__content customer-note__content--editable ajs-dirty-warning-exempt" name="noteInputTIG" placeholder="$i18n.getText("tigNoteExtension.placeholder")">$item.getContent()</textarea>
                    <input class="hidden" id="reporterInputTIG$item.getId()" name="reporterInputTIG" value="$item.getUserId()"></input>
                    <input class="hidden" id="domainInputTIG$item.getId()" name="domainInputTIG" value="$domain"></input>
                    <button id="saveButtonTIG$item.getId()" class="customer-note__button aui-button" name="saveButtonTIG">$i18n.getText("tigNoteExtension.save")</button>
                </div></div><br/>
            #end
        #end
</div>

Does it stop working when you navigate from one issue to the other? If so, it might be because the target has changed and that your callbacks still point to previously viewed HTML elements.

Try to hook up to Jira events such as NEW_CONTEXT_ADDED. Depending on which version of Jira you’re targetting, you could inject the Jira module for the events and work from that. E.g.:

require([
			'jira/util/events',
		  	'jira/util/events/types',
		  	'jira/util/events/reasons',
		 ], function(Events, EventTypes, EventReasons) {

	"use strict";
	
    Events.bind(EventTypes.NEW_CONTENT_ADDED, function(e, jiraContext, reason) {
        switch (reason) {
        case EventReasons.pageLoad:
        case EventReasons.panelRefreshed:
        case EventReasons.dialogReady:
        case EventReasons.inlineEditStarted:
            break;
        }
    });

You should also take care of clearing out any previously linked function otherwise, if the user browses many issues, you may end up with functions being called all over the place and freezing the browser, especially if the function is a little bit heavy.

No it doesn’t work even on the first issue shown. Say I search for a issue using the title. It doesn’t matter if it is the first issue shown (without clicking anything) or one I navigate to. My javascript doesn’t seem to load. The weirdest thing about it is (as I stated before) that my first function (which autosizes a textarea) works after I add some input to the textarea. In other words it does what it should do only after I add some manual input to the already isnerted input. When I directly view a issue this same textarea is already resized based on the input I inserted into it using java.

I don’t quite get it I followed your link and changed my javascript but I have the exact same issue. I tried it using your snippet (which honestly I don’t quite get) and using some code from your link but it doesn’t seem to work. My javascript just doesn’t work in the navigator.

New code (link example):

AJS.$(function() {
JIRA.bind(JIRA.Events.NEW_CONTENT_ADDED, function(e, context, reason) {
var $context = AJS.$(context);

    // Find our web panel. Handles the pageLoad and panelRefreshed reasons.
    var $webPanel = $context.find("*").andSelf().filter("#issue-page-input");
    if ($webPanel.length > 0) {
        AJS.$(document).on('change keyup keydown paste cut', 'textarea', function () {
            $(this).height(0).height(this.scrollHeight);
        }).find('textarea').change();

        AJS.$('.customer-note__expand-note').click(function(){
            AJS.$header = AJS.$(this);
            AJS.$content = AJS.$header.next();
            AJS.$content.slideToggle(0);
            AJS.$(this).toggleClass('customer-note_head-collapse');
            AJS.$(this).toggleClass('customer-note_head-expand');
        });

        AJS.$('.customer-note__button').click(function(){
            console.log("Wryyyyyy");

            var id = AJS.$(this).attr('id').replace("saveButtonTIG", "");

            console.log(id);
            console.log("noteInputTIG"+id);

            data = {};
            data.content = document.getElementById("noteInputTIG"+id).value;
            data.domain = document.getElementById("domainInputTIG"+id).value;
            console.log(document.getElementById("reporterInputTIG"+id).value);
            console.log(document.getElementById("noteInputTIG"+id).value);
            console.log(document.getElementById("domainInputTIG"+id).value);
            console.log(JSON.stringify(data));

            AJS.$.ajax({
                url: AJS.params.baseURL + "/rest/noteresource/1.0/note/" + document.getElementById("reporterInputTIG"+id).value,
                type: "POST",
                data:JSON.stringify(data),
                dataType: "json",
                contentType: "application/json; charset=utf-8",
                success: function(msg){
                    console.log("Success !");
                }
            });
        });
    }
});

});

New code (snippet example)

AJS.$(function() {
require([
‘jira/util/events’,
‘jira/util/events/types’,
‘jira/util/events/reasons’,
], function(Events, EventTypes, EventReasons) {

    "use strict";

    Events.bind(EventTypes.NEW_CONTENT_ADDED, function (e, jiraContext, reason) {
        switch (reason) {
            case EventReasons.pageLoad:
            case EventReasons.panelRefreshed:
            case EventReasons.dialogReady:
            case EventReasons.inlineEditStarted:
                break;
        }
    });

    AJS.$(document).on('change keyup keydown paste cut', 'textarea', function () {
        $(this).height(0).height(this.scrollHeight);
    }).find('textarea').change();

    AJS.$('.customer-note__expand-note').click(function(){
        AJS.$header = AJS.$(this);
        AJS.$content = AJS.$header.next();
        AJS.$content.slideToggle(0);
        AJS.$(this).toggleClass('customer-note_head-collapse');
        AJS.$(this).toggleClass('customer-note_head-expand');
    });

    AJS.$('.customer-note__button').click(function(){
        console.log("Wryyyyyy");

        var id = AJS.$(this).attr('id').replace("saveButtonTIG", "");

        console.log(id);
        console.log("noteInputTIG"+id);

        data = {};
        data.content = document.getElementById("noteInputTIG"+id).value;
        data.domain = document.getElementById("domainInputTIG"+id).value;
        console.log(document.getElementById("reporterInputTIG"+id).value);
        console.log(document.getElementById("noteInputTIG"+id).value);
        console.log(document.getElementById("domainInputTIG"+id).value);
        console.log(JSON.stringify(data));

        AJS.$.ajax({
            url: AJS.params.baseURL + "/rest/noteresource/1.0/note/" + document.getElementById("reporterInputTIG"+id).value,
            type: "POST",
            data:JSON.stringify(data),
            dataType: "json",
            contentType: "application/json; charset=utf-8",
            success: function(msg){
                console.log("Success !");
            }
        });
    });
});

});

Ok, let’s try in small increments. Let’s first see if we are called when the page load and when a new issue is navigated to. I assume here that you are coding for Jira 7.x. Reuse an existing web ressource (.js file) and have this for content:

require([
			'jira/util/events',
		  	'jira/util/events/types',
		  	'jira/util/events/reasons',
		 ], function(Events, EventTypes, EventReasons) {

	"use strict";
	
    Events.bind(EventTypes.NEW_CONTENT_ADDED, function(e, jiraContext, reason) {
        switch (reason) {
        case EventReasons.pageLoad:
        case EventReasons.panelRefreshed:
            var pageInputs= jiraContext.find('#issue-page-input');
            break;
        }
    });
});

This is a javascript module. If you’re not familiar with it, read about AMD (Asynchronous Module Definition). Jira uses a simplified version of it via AlmondJS.

Now, put in Chrome a breakpoint on the “pageInputs” line. You should be called when Jira loads the page or refreshes a web panel. The jiraContext object is already a jQuery object. Make sure that:

  1. The breakpoint is reached when the page initially load
  2. The breakpoint is reached when you navigate from one issue to the next

If all of these things happen then you know that you can instantiate your stuff properly and the rest is just programming. You should be able to find your HTML fragments via the jiraContext object.

Just don’t forget to release your click event handlers when you navigate from one issue to the next.

I tried your sugesstion and the breakpoint was reached when I loaded the page but it didn’t load when switching between issues.

Edit: There is also something I didn’t notice before. When I use your code and I switch between issue A and B the javascript won’t load but when I then switch from B back to A the javascript does work. So each issue needs to load a second time before it detects my javascript.

Edit 2: I removed PageLoad from the code and only left PanelRefresh. This gives the exact same results in that it only loads my javascript after switching back and fort between issues.

A little bit strange. That’s the mechanism that I use and it seems to work. What version of Jira are you using? Also, instead of waiting for PanelRefresh, set your breakpoint to the switch statement. You should be called many time. Take a look at the reason maybe we’re looking for the wrong reason.

So after look around some more and trying some new search terms I stumbled on this topic: https://community.atlassian.com/t5/Answers-Developer-Questions/Javascript-code-is-unable-to-load-when-issue-summary-is-open/qaq-p/499651. So I did what suggested there and used:

JIRA.bind(JIRA.Events.ISSUE_REFRESHED, function() {
// your code
 });

This did the trick and my code now loads like normal inside the navigator.