Last updated Dec 8, 2017

Rate this page:

Creating a gadget JavaScript object

This page tells you how to create a gadget using the Atlassian Gadgets JavaScript Framework.

High-Level Format for Creating a Gadget

The framework uses a declarative approach in terms of configuration. View, configuration and authorisation parameters are passed into the constructor and used to configure the gadget. See Gadget Object API for methods available on the gadget object.

The top-level options are:

1
2
var gadget = AJS.Gadget({
    baseUrl: "__ATLASSIAN_BASE_URL__",
    useOauth: ...,
    config: ...,
    view:...
});

Where:

  • baseUrl -- A required option used to pass the base URL to the framework. The framework uses this URL for prefixing relative Ajax requests and also makes it available from gadget.getBaseUrl() and AJS.gadget.getBaseUrl().
  • useOauth -- An optional parameter that is used to configure the type of authentication used. Here you must provide a URL that requires authentication. Typically we use "/rest/gadget/1.0/currentUser". The framework will then try to broker an authenticated connection in the following order:
    1. Current session (a gadget being served locally, e.g. JIRA displaying a JIRA gadget), then
    2. A Trusted Applications connection, then
    3. OAuth. A gadget being served in a non-Atlassian container will typically use OAuth.
  • config -- You can use this optional parameter to define the configuration form. If this parameter is not defined, it is assumed that the gadget has no configuration options. Please note that this is not the built-in Opensocial Gadget configuration screen. The latter did not meet our needs so we decided to implement our own. See GADGETDEV:below.
  • view -- This object defines the view to display. See GADGETDEV:below.

Authentication

The framework will automatically try to broker an authenticated connection to the server, based on the URL provided in the useOauth configuration option described GADGETDEV:above. If the value passed in is "always", the framework will always try and use OAuth. If the value passed in is a URL, the framework will determine the best method of invoking that URL. The URL must not allow anonymous access. JIRA currently provides the following REST resource: /rest/gadget/1.0/currentUser

The framework tries to connect to the server in the following order:

  1. Local -- We can determine whether the gadget is being served locally (e.g. JIRA serving out a JIRA gadget) and then we can just use the local browser to talk to the server. This is the most efficient method of communication as no proxies are used.
  2. Trusted Applications -- If a successful request returns from the supplied resource when no OAuth token is passed, it must be a trusted application. We rely on the container server to use its Trusted Applications connection to the remote server for authentication.
  3. OAuth - If the previous two methods fail, we fall back to OAuth.

More: Using Authentication in your Gadget

Configuration Form

Early on in our Gadget development, we realised that the OpenSocial gadgets' mechanism for creating configuration forms was not going to meet our needs. The fields must all be known beforehand, options are static and fields cannot be manipulated programatically. OpenSocial also only supports very basic fields. Our framework offers all the options available in the Opensocial Gadget framework for creating a configuration form. In addition, our framework allows for Ajax populated lists, custom fields and even dynamically included fields.

Typically, when loaded, a gadget will show a config screen first, then the normal gadget view. The user can choose to view it in canvas view if defined.

In order to take full advantage of the features in the framework, you should include the following in your gadget XML:

1
2
        <Require feature="setprefs" />
        <Require feature="views" />
        <Require feature="dynamic-height" />

Where:

  • setprefs -- This is required to persist user preferences.
  • views -- This is used to determine whether the current user can edit the preferences or not. Please note that this is an Atlassian-specific extension to the feature and will not work in other containers. The edit button will always be shown in other containers.
  • dynamic-height -- Provides dynamic height adjustment in the config screen, view creation and on resize.

The config object consists of two parts: Ajax option definitions and a descriptor. When the form is generated, the Ajax options are retrieved and then fed into the descriptor which then returns an object fully defining the configuration form. The framework will then render the form. When the form is submitted, the form will use the passed in validation URL to validate the form and if successful, will then save the preferences to the server. If validation fails, the form will display the errors inline.

All fields on the configuration form will be persisted as UserPrefs against the gadget.

1
2
...
config: {
    descriptor: function(){...},
    args: {Function, Array}
},
...

Where:

Ajax Options

This can either be an array of options to retrieve, or a function that returns a similar array.

1
2
...
args: [
    {
        key: "key to access options by",
        ajaxOptions:  "/rest/gadget/1.0/projects" /* this can also be a more complex JQuery style remote call */
    },
    ... more options to retrieve
]
...

Where:

jQuery style remote Ajax options

1
2
1
2
...
args: [
    {
        key: "project",
        ajaxOptions:  {
            url: "/rest/gadget/1.0/projects",
            data: {
                restrictToPermission: "create"
            },
            type: "POST"
        }
    },
    ... more options to retrieve
]
...

Note: This is just a fictitious REST resource. The example will not work.

Configuration Descriptor

The configuration descriptor is a function that returns an object defining the configuration form. After the Ajax options have been retrieved, they are passed into the function to get the object that defines the form.

The function is called within the scope of the gadget. Hence this refers to the current gadget object.

1
2
...
descriptor: function(args){
    /* This is called within the scope of the current gadget.  "this" refers to the current gadget */
    return {
        action: "validation url", /* A url to validate the form against */
        theme: "", /* The layout of the form - "top-label" or "long-label" */
        fields:[
            {
                /* Field descriptor */
            },
            ... more fields
        ]
    };
},
...

Where:

  • args -- This is an object populated by the results of the Ajax options defined in the args parameter of the config object. If you retrieve a list of projects via Ajax, this list can be accessed via: args.projects
  • action -- This is a URL pointing to a REST resource that will validate the form and return appropriate errors if the form contains invalid data. Please see the information about the format for the REST resource validating gadget configuration for the structure and return codes of the response.
  • theme -- This tells the form how to lay itself out. There are two possible values:
    • "top-label" -- This displays the field label above the field. Useful for narrow forms.
    • "long-label" -- This displays the label on the same line as the field. Better suited to forms with a lot of width available.
  • fields -- This is an array of GADGETDEV:field objects.

More: Theming your Gadget Dynamically

Configuration Fields

Configuration forms are made up of list of fields. Typically, these fields relate to UserPref fields defined in the gadget XML. We usually define our UserPrefs in the XML as hidden UserPrefs. This will ensure that the field will not appear in the OpenSocial gadget configuration form. E.g.

1
2
    <UserPref name="project" datatype="hidden" />

We support the following types of fields (details are on the Field Definitions page):

  • Hidden fields -- A hidden input area on the form whose value is passed back to the server during validation and preference saving.
  • Text input -- A single line text input.
  • Text area -- A text area for accepting larger text inputs.
  • Select fields -- A simple selection list that displays a list of options. It is possible to display a list of options retrieved from a REST resource.
  • Multi-select fields -- A multi-select field where it is possible to select multiple values. It is possible to display a list of options retrieved from a REST resource.
  • Radio button group -- A set of radio buttons for selecting unique values. It is possible to display a list of options retrieved from a REST resource.
  • Check box group -- A set of check boxes to select multiple values. It is possible to display a list of options retrieved from a REST resource.
  • Custom fields -- A user defined field. This allows users to insert arbitrary HTML into the form.
  • Callback builder -- An empty <div/> is inserted into the form. After form creation, a callback handler is called passing in the jQuery wrapped div. This is the most powerful field creation technique and can be used to build highly interactive fields.

More:

View

This object defines the view to display, as shown in the high level overview GADGETDEV:above.

1
2
config: {...},
view: {
    enableReload: true,
    onResizeReload: true,
    onResizeAdjustHeight: false,
    template: function (args) {},
    args: [{
        key: "chart",
        ajaxOptions: {...}
    }]
}

Where:

View Template Function

The template function is responsible for rendering the gadget's view. The function is run within the scope of the gadget so 'this' refers to the gadget. See the Gadget Object API for the methods available.

The template creates HTML using jQuery. It does not include styling. Styling is done later via CSS.

The function takes one argument - args - a map containing the response from the Ajax calls. This map is keyed by the specified key.

The function must be able to be called multiple times for reloading and resizing.

Example:

1
2
view: {
    enableReload: true,
    onResizeAdjustHeight: true,
    template: function (args) {
        var gadget = this;
        var filters = args.favFilters.filters;

        if (!filters){
            gadget.getView().removeClass("loading").html("<p>__MSG_gadget.favourite.filters.no.favourites__</p>");
        } else {
            var list = AJS.$("<ul/>").attr("id", "filter-list");

            AJS.$(filters).each(function(){
                list.append(
                    AJS.$("<li/>").append(
                        AJS.$("<div/>").addClass("filter-name").append(
                            AJS.$("<a/>").attr({
                                target: "_parent",
                                title: gadgets.util.escapeString(this.description),
                                href: "__ATLASSIAN_BASE_URL__/secure/IssueNavigator.jspa?mode=hide&requestId=" + this.value
                            }).text(this.label)
                        )
                    ).append(
                        AJS.$("<div/>").addClass("filter-count").text(this.count)
                    ).click(function () {
                        if (window.parent){
                            window.parent.location = "__ATLASSIAN_BASE_URL__/secure/IssueNavigator.jspa?mode=hide&requestId=" + this.value;
                        } else {
                            window.location = "__ATLASSIAN_BASE_URL__/secure/IssueNavigator.jspa?mode=hide&requestId=" + this.value;
                        }
                    })
                );
            });
            gadget.getView().html(list);
        }
    },
    args: [{
        key: "favFilters",
        ajaxOptions: function () {
            return {
                url: "/rest/gadget/1.0/favfilters",
                data:  {
                    showCounts : this.getPref("showCounts")
                }
            };
        }
    }]
}

Using the Atlassian Gadgets JavaScript Framework

Writing an Atlassian Gadget

Gadget Developer Documentation

Rate this page: