Events

Almost all activity in an app happens in response to events such as user events, request events, and framework events. You tell the app to listen for certain events and run one or more functions whenever one of them is detected.

Event basics

Events are an easy concept to understand, particularly if you have any experience with JavaScript or jQuery. Your app listens for certain events. When one of these events occurs, the app runs one or more functions.

Topics covered in this section:

Adding event listeners

You add each event and the function to handle it as a name/value pair in the events object. Example:

events: {
  'app.created': function() {
    // handler code
  }
},

The name of the event in the example is app.created, which is a framework event that fires when the app gets created. The value is an anonymous function. You can write a named function elsewhere in the file and refer to it by name in the events object:

events: {
 'app.created': 'doSomething'
},
// later in the app...
doSomething: function() {
  // handler code
}

Because the handler function is invoked in the context of the app, the value of this inside the handler is the app. Use this in the handler to access other functions in your app.

Identifying source elements

For DOM events like a user clicking a button, identify the source element that initiates the event by adding the element's CSS class or id selector to the event name:

 'click .save': function() {
   //handler code
  }

The event name is 'click' and the source element that initiates the event has a CSS class selector named '.save'. Example: <button class="save">Save</button>. The event fires whenever the user clicks the button. If the source element has an id attribute such as <a id="update">Update</a>, you can use the id selector instead. Example: 'click #update': ....

The basic syntax is as follows:

'[event name] [optional source element]': [event handler]
Accessing event properties

When invoked by an event, the event handler function receives a jQuery event object as its first argument, plus any additional arguments that may be passed depending on the type of event. Example:

  'submit .login': function(evt) {
    evt.preventDefault();
  },

  'ticket.comment.changed': function(evt, data) {
    console.log('comment changed to ', data.newValue);
  }
...

The handler for the submit .login event above receives an event object that's assigned to an argument called evt. The name is arbitrary, but e, evt, and event are common choices. In the example above we call evt.preventDefault() so that the default action of the event is not triggered by the browser. For more information see event.preventDefault in the jQuery docs.

In the second example, the handler for ticket.comment.changed receives a data object as a second argument, which includes a newValue property containing the new value of the ticket comment.

The properties of the object vary depending on the event. Add a temporary console.dir() statement to inspect the properties in the browser console:

  'app.created': function(e) {
    console.dir(e);
    ...
  }

A handler can receive additional data depending on the event. For example, the event that fires when an AJAX request is complete passes the response data to the handler.

Example

Let's wrap up this overview of events by taking a fairly complex event and breaking it down. The following snippet makes an AJAX request when the user clicks a specific link in the app.

events: {
  'click .bookmark': function(event) {
    event.preventDefault();
    this.ajax('addBookmark');
  },
  ...
}
  • click .bookmark is the name of the event. It's saying "when the user clicks anything in the app with a class of 'bookmark', do the following..."
  • function(event) is the handler for the event
  • event.preventDefault() is saying "prevent what would normally happen when a user clicks this". If the user is clicking a link to add a bookmark, this blocks the browser from trying to navigate to that link
  • this.ajax('addBookmark') makes an AJAX request. A request definition called addBookmark configures the request

Event handlers don't have to be initiated by a user. In the example, you could add a second event to display a notification when the AJAX request is successful:

events: {
  'click .bookmark': function(event) {
    event.preventDefault();
    this.ajax('addBookmark');
  },
  'addBookmark.done': function() {
    services.notify('Bookmark Added');
  }
}

This is an example of request events. Let's break it down too:

  • addBookmark.done means "when the addBookmark request is done, do the following..."
  • services.notify means "show a notification on the screen that the bookmark was added". See Services

You could also listen for the addBookmark.fail event if you want to display a message in case a request fails.

Event reference

The framework supports several kinds of events. Some are available to all apps and some are available based on the location of the app.

Global events

The following kinds of events are available to all apps:

Per-location events

In addition to global events, your app can use events that depend on the location where your app is running.

These events typically consist of changes to Zendesk Support data made by the user in the location where the app is running. For instance, if your app runs in the ticket sidebar in Zendesk Support, you have access to a number of events that fire when the user changes various fields in the ticket.

Framework events

Framework events fire when something external happens to the app. These are generally lifecycle-type events.

app.created

Fires when the app is loaded for the first time. Effectively the same as app.activated with firstLoad set to true.

The event also fires when an app is reloaded. The framework destroys the loaded app before creating it again. See app.willDestroy below.

app.activated

Fires whenever the app becomes active.

events: {
  'app.activated': 'requestBookmarks',
  ...
}

The app.activated event may fire multiple times during a user session. For example, if the app's location is the ticket sidebar, the event fires each time the user navigates to the ticket. You can check for the first time by using the firstLoad property of the event object. However, Zendesk recommends using app.created for this purpose. See Accessing event properties. Example:

'app.activated': function(evt, data) {
  if(data.firstLoad) {
    // effectively the same as app.created
  }
  ...
}
app.deactivated

Fires when the app becomes inactive. This can mean different things depending on the location of your app. For example, if your app is in the ticket sidebar, it means that the user switched away from the tab containing this instance of your app.

app.willDestroy

Fires just before the app is destroyed. This event fires whenever your app is reloaded or the location of your app is about to destroyed. For example, if your app is in the ticket sidebar, the event fires just before the ticket tab is closed. This is the best time to clean up and persist important data. Example:

events: {
  'app.willDestroy': function() {
    clearInterval(this.intervalId);
    this.store('userInfo', this.userInfo);
  },
  ...
}

DOM events

Use DOM events to perform actions based on something the user does, such as clicking a button or typing inside a field. The name of a DOM event in the events object consists of two parts:

  • the name of the DOM event, such as click or blur
  • a CSS selector for the UI element or elements in your app that initiates the event

See Identifying source elements above. The event handler in the following example runs when a user clicks a UI element with the .btn-add class.

events: {
  'click .btn-add': function(event) {
    event.preventDefault();
    this.ajax('addBookmark');
  },
  ...
}

You can specify multiple event names and selectors by separating them with commas.

You can use any event name and any selector that jQuery supports. See the event documentation on the jQuery website for more information.

Request events

When an AJAX request is made, you can watch for the outcome of the request. A request can succeed or fail. The following example displays a notification when the addBookmark request is .done -- in other words, when the request was completed without error.

events: {
  'addBookmark.done': function() {
    services.notify('Bookmark successfully added.');
  },
  ...
}

You can watch for the following request events:

Event Meaning
.done The request was completed without error
.fail The request was made but an error occurred
.always The request was made, no matter the outcome
.delayed The request was made, but it was rate-limited and will be retried after a delay

Custom events

You can use the framework's trigger function to fire a custom event. Custom events are useful for structuring your app's features in terms of events that fire at various points in the app.

Example:

events: {
  'foo': function(data) {
    // handler codee
  }
}

// later in the app
this.trigger('foo', {arg: 1});

The trigger function calls the foo handler function and passes in the {arg: 1} object as an argument.

Hook events

A hook is like an event defined for a built-in Zendesk Support function or object. Unlike a regular event, however, the built-in function pauses while the hook handler runs. Unlike a regular event handler, the hook handler can also affect the action that triggered it.

The ability to intercept a user action and run some code that could affect the action itself opens many doors for your apps to interact with and validate content. For example, when an agent tries to save a ticket, you can make an AJAX request to your back-end system to validate a product ID in the ticket. If the ID checks out, you can let the save action proceed. If not, you can cancel the action and notify the agent to enter a valid ID.

For an example of a hook used in an app, see the Save Hook sample app on Github.

Handlers for hooks are created the same way as for events, using an inline or named function.

Inline function

'ticket.save': function() {
    // handler code
},

Named function

'ticket.save': 'ticketSaveHandler',
Available hooks

The framework currently supports one hook.

Hook Locations Description
ticket.save ticket_sidebar, new_ticket_sidebar An attempt has been made to save a ticket, either by clicking Submit or using a keyboard shortcut

The hook can only be used in the specified locations in which an app can run.

ticket.save hook

Fires immediately after a user clicks the ticket Submit As button, or presses a keyboard shortcut to save a ticket.

When a ticket.save hook fires, your app can run a custom function and decide whether the ticket save action should proceed or not. The hook handler can be synchronous or asynchronous.

A ticket.save hook handler can affect the action that triggered it in one of two ways:

  • allow the ticket save action to proceed
  • prevent the ticket save action from proceeding
Allowing a ticket save action to proceed

To allow the ticket save action to proceed in a general hook handler, return true:

'ticket.save': function() {
  // do something
  return true;
}

If your app needs to do something asynchronous such as an AJAX request, use a promise:

'ticket.save': function() {
    return this.promise(function(done, fail) {
        this.ajax('heartQuote').then(
            function(data) {
                console.log(data.quote);
                done();
            },
            function() {
                console.log('request failed but ticket.save shall pass');
                done();
            }
        );
    });
},

If the promise is resolved, the save action proceeds. See Promises for more information.

The then() method specifies handlers for the success and failure cases. Example: then(handlerDone, handlerFail). See deferred.then() on the jQuery website.

Preventing a ticket save action from proceeding

To stop the ticket save action from proceeding in a general hook handler, return false:

'ticket.save': function() {
    // do something
    return false;
}

You can notify the user that the action was cancelled. See "Notifying the user if the ticket is not saved" below.

If your app needs to do something asynchronous such as an AJAX request, use a promise:

'ticket.save': function() {
    return this.promise(function(done, fail) {
        this.ajax('heartQuote').then(
            function(data) {
                console.log(data.quote);
                fail();
            },
            function() {
                console.log('request failed and ticket.save shall fail too');
                fail();
            }
        );
    });
},

If the promise is rejected, the save action is canceled.

Notifying the user if the ticket is not saved

When a ticket.save hook handler cancels a ticket save action, you can display a custom message in a notification box on the upper-right side of the Zendesk Support interface.

image

The custom message is displayed as an error notification, meaning that it's styled as an error and is persistent in the agent interface until the user dismisses it.

You can supply the notification by returning the string from the hook handler:

'ticket.save': function() {
    // do something
    return 'The ticket wasn't saved!';
}

You can also supply the notification string as an argument to the fail() method in a promise:

'ticket.save': function() {
    return this.promise(function(done, fail) {
        this.ajax('heartQuote').then(
            function(data) {
                // the request was completed successfully
                // do something with the data
                done();
            },
            function() {
                // something wen't wrong
                fail('The ticket wasn't saved!');
            }
        );
    });
},

If you don't supply a notification message, then a default message is displayed, along with the name of the app that disallowed the ticket save. The name is the value of the 'name' property in your manifest file. Example:

{
    "name": "Hook Line and Sinker",
    "author": {
        "name": "Jennifer Hansen"
    },
    ...
}
Resolving conflicts when multiple apps respond to the hook

More than one app may respond to the ticket.save hook. For example, when a user tries to save a ticket, two apps may allow the action to proceed while a third app may not. If multiple apps respond to the ticket.save hook, the following rules apply:

  • If any app disallows the ticket save, then the save action is canceled
  • If all apps allow the ticket save action, then the save action is performed
Global timeout

Because hook handlers can be asynchronous, it's possible that a handler may never respond. If one or more hook handlers fail to respond after a global timeout period of 30 seconds, the ticket save action is canceled and the hook handlers are ignored.

Using ticket submission events

The framework provides a number of ticket submission events in addition to the ticket.save hook. See Ticket submission events in the Events doc.

Iframe events

Iframe events and the postMessage API allow your app to communicate with an external website using the Zendesk App Framework (ZAF) SDK. If you haven't used the ZAF SDK before, we recommend you look at Getting Started: IFrames in Apps first.

iframe.eventName

An event fires every time a message is posted from your external website. The event name is defined on the external website and prefixed by iframe. Example:

External website, i.e. "https://dashboard.myapp.com"
<script type="text/javascript" src="https://assets.zendesk.com/apps/sdk/latest/zaf_sdk.js"></script>
<script>
  var app = window.ZAFClient.init();
  app.postMessage('hello', { foo: true }); // post the message 'hello' to the Zendesk app
</script>
app.js
events: {
  'iframe.hello': 'handleHello'
},

handleHello: function(evt, data) {
  if (data.foo) {
    // iframe says hello
  }
}
this.postMessage(name, [data])

You can call this.postMessage() from your app to trigger an event on your external website.

Note: The postMessage API is only available after the app has established communication with the iframe. When that happens the framework will trigger the app.registered event, both in your app and the iframe.

Arguments
  • name the name of the message event.
  • data (optional) a JSON object with any data that you want to pass along with the event.

Example:

app.js
handleHello: function(evt, data) {
  if (data.awesome) {
    this.postMessage('helloIframe', { bar: true });
  }
}
External website, i.e. "https://dashboard.myapp.com"
  app.on('helloIframe', function(data) { // listen to the 'helloIframe' message sent from the app
    if (data.bar) {
      // app says hello
    }
  });

Message events

If your app runs in multiple locations, one location can communicate with another location. The Messaging API allows your app to communicate with each instance of itself running in a user's browser.

message.eventName

A message sent using this.message fires an event prefixed by message..

app.js
events: {
  'app.created': 'init',
  'message.hello': 'handleHello'
},

init: function() {
  if (this.currentLocation() === 'ticket_sidebar') {
    this.message('hello', { awesome: true }, 'top_bar');
  }
},

handleHello: function(data) {
  this.popover({
    width: 400,
    height: 200
  });
}
this.message(name, [data], [locations])

To trigger a message event on your app in another location, call this.message from your app and pass in the location you wish to send the message to.

If locations is not specified, the message will be sent to all running instances of your app in all locations.

Arguments
  • name the name of the message event.
  • data (optional) a JavaScript object with any data that you want to pass along with the event.
  • locations (optional) an array of locations, or a single location, to send the message to

Window events

Apps have access to some common DOM window events through a window event namespace.

Note: Window events are debounced in apps for performance reasons. Debounce wait values are noted below under each event's description.

Available events:

Event Meaning
window.resize The current browser window was resized to a new size
window.scroll The current browser window was scrolled to a new position
window.resize

The window.resize event fires, at most, once every ~33 (1000/30) milliseconds.

Arguments
  • width equivalent to window.innerWidth
  • height equivalent to window.innerHeight
window.scroll

The window.scroll event fires, at most, once every ~16 (1000/60) milliseconds.

Arguments
  • scrollTop equivalent to window.scrollTop

Notification events

When a notification is sent to your app via the notify REST API endpoint, the framework triggers a notification event. For more information about the notify endpoint, see REST API: Send Notification to App.

Use the following syntax to define a notification event:

events: {
  "notification.event_name": function(body, sender) {
  }
}

In the example, event_name is the name of the event passed to the notify endpoint. The handler of the notification event receives the body of the request as the first argument and a User Object for the sender of the request as the second argument.

Wildcard change events

In addition to the specific .changed events for ticket, user, and organization sidebar apps, you can also listen for a wildcard change event that fires for any of the above events.

Use the following syntax to define a wildcard event:

events: {
  "*.changed": function() {
  }
}

As with other events, the handler for a wildcard event receives an event object as the first argument. In the case of the wildcard event, the object has two special properties to help you determine the underlying event that triggered the wildcard event:

Property Meaning
propertyName The underlying property that triggered the wildcard event
newValue The value of the underlying property after the change

Example:

events: {
  "*.changed": function(e) {
    var propertyName = e.propertyName; // "ticket.assignee.user.email"
    var newValue = e.newValue;         // "sally.agent.1@example.org"
  }
}

Dynamic events can be useful for identifying and handling events that occur to fields that are installed using app requirements. Example:

events: {
  "*.changed": "handleDynamicEvents"
},

handleDynamicEvents: function(data) {
  var propertyName = data.propertyName, // "custom_field_3245"
      customFieldID = this.requirement('my_requirement_field').requirement_id; // "3245"
  if (propertyName === 'ticket.custom_field_'.fmt(customFieldID)) {
    // Do something related to that custom field...
  }
}

Ticket sidebar events

Apps that run in the ticket sidebar have additional events. For example, an event fires when the list of CCs on a ticket is changed:

events: {
  'ticket.collaborators.changed': 'newCCs'
}

// later in the app
newCCs: function() {
  this.generateTicketView();
}

For details about the example, see the Basic Ticket Information sample app on Github.

The framework has the following groups of ticket sidebar events:

Ticket change events

You can use the following events in a ticket sidebar app to listen for changes to the ticket:

  • ticket.assignee.group.id.changed
  • ticket.assignee.group.name.changed
  • ticket.assignee.user.email.changed
  • ticket.assignee.user.externalId.changed
  • ticket.assignee.user.id.changed
  • ticket.assignee.user.name.changed
  • ticket.brand.changed
  • ticket.collaborators.changed
  • ticket.comments.changed
  • ticket.custom_field_<custom field ID>.changed
  • ticket.due_date.changed
  • ticket.form.id.changed
  • ticket.postSaveAction.changed
  • ticket.priority.changed
  • ticket.problem_id.changed
  • ticket.requester.email.changed
  • ticket.requester.externalId.changed
  • ticket.requester.id.changed
  • ticket.requester.name.changed
  • ticket.sharedWith.changed
  • ticket.status.changed
  • ticket.subject.changed
  • ticket.tags.changed
  • ticket.type.changed

All the events have corresponding data API methods that you can use to inspect the data.

The events fire when the respective properties on the ticket change due to user interaction or when Zendesk Support first loads the ticket. The events can also fire when other apps make changes to the ticket.

Remember that change events relate specifically to the properties being listened to. For example, the ticket.form.id.changed event fires when the ticket form is changed. It doesn't mean that the form is fully loaded and ready to be manipulated. In these cases, you may want to use the _.defer underscore helper.

Custom ticket field change event

You can use the ticket.custom_field_<custom field ID>.changed event in a ticket sidebar app to listen for changes to custom ticket fields. Example:

events: {
  "ticket.custom_field_123123": "customFieldChanged"
}

In the example, 123123 is the ID of the custom field you want to listen to.

For custom fields only, you can pass a dynamic parameter as the custom field ID. For example, you might want to use app settings to ask the admin who installs the app for a custom field ID, and then listen for a change event for this custom field as follows:

  • ticket.custom_field_ticket.custom_field_{{reference_field_id}}.changed

Example:

events: {
  'app.created' : 'doSomething',
  'ticket.custom_field_{{reference_field_id}}.changed' : 'dataChanged'
}

{{reference_field_id}} is equal to what you defined as a setting in your app's manifest.json file:

{
    "name": "reference_field_id",
    "type": "number",
    "required": true
}
Ticket comment change events

You can use the following events in a ticket sidebar app to listen for changes to the current comment:

  • comment.text.changed
  • comment.type.changed
  • comment.attachments.changed

The comment.text.changed event fires on a new ticket when the Description field is changed.

All the events have corresponding data API methods that you can use to inspect the data.

Ticket submission events

The following events correspond to the lifecycle of a ticket save action, which may be controlled by one or more ticket.save hook handlers. The events are supported in ticket_sidebar and new_ticket_sidebar apps. For more information, see ticket.save hook.

Event Meaning
ticket.submit.start The ticket save has been initiated; hook handlers haven't run yet
ticket.submit.done The ticket save was successfully completed
ticket.submit.fail The ticket save failed due to a responding ticket.save hook handler
ticket.submit.always The ticket save lifecycle has been completed. Always fires regardless of success or failure

Examples:

'ticket.submit.start': function() {
  // handler code
}
'ticket.submit.start': 'ticketSubmitStartHandler'

The event handlers don't receive any parameters.

Ticket updated event

You can use the ticket.updated event to run some code when a ticket is updated by another agent. The event is useful for avoiding agent collisions, where one agent updates a ticket that another agent is currently working on. The event handler receives as a parameter the user who updated the ticket. See the User Data API.

The event fires when one of the following is true:

  • Another agent saved changes to the ticket and the changes have been auto-updated to all other concurrent viewers.
  • The "Stay on ticket" behavior is in effect for an agent and the agent saves changes to the ticket.
  • An update is made to the ticket with the REST API.

Example:

{
  events: {
    'ticket.updated': 'handleTicketUpdated'
    // ...
  },
  ...

  // Do something when the ticket is updated
  handleTicketUpdated: function(user) {
    if (user.id() !== this.currentUser().id()) {
      // Some other guy!
    } else {
      // Stay on ticket!
    }
  }
}
Ticket collision events

When another agent is viewing the same ticket as the logged-in agent, a ticket.viewers.changed event is fired. Listening for this event allows your app to display the other agents currently viewing the ticket.

The handler of the ticket.viewers.changed event receives an array of Collision User objects as the first argument. These objects, in addition to all the methods available for the User Object, also have isEditing() and isIdle() methods to determine the viewer's state on the ticket.

The following example uses this event to produce notifications with the list of editors on the ticket each time the viewers change.

events: {
  "ticket.viewers.changed": function(viewers) {
    var editors = viewers.filter(function(viewer) {
      return viewer.isEditing();
    });
    var editorNames = editors.map(function(editor) {
      return editor.name();
    });
    services.notify(editorNames.join(', ') + ' are editing');
  }
}

User sidebar events

User change events

You can use the following events in a user sidebar app to listen for changes to the user:

  • user.alias.changed
  • user.avatarUrl.changed
  • user.details.changed
  • user.email.changed
  • user.externalId.changed
  • user.groups.changed
  • user.name.changed
  • user.notes.changed
  • user.role.changed
  • user.signature.changed
  • user.tags.changed
  • user.timeZone.changed
  • user.<custom_field_key>.changed
  • user.organizations.changed

The user.name.changed event fires when the user's name in the user's page changes.

For custom fields, replace {custom_field_key} with the custom field's key attribute. See User Fields. Example: my_text_field.

Note: Currently, if another user changes a property in a separate window, the event will not be picked up.

All the events have corresponding data API methods that you can use to inspect the data.

Organization sidebar events

Organization change events

You can use the following events in a organization sidebar app to listen for changes to the organization:

  • organization.details.changed
  • organization.domains.changed
  • organization.group.changed
  • organization.name.changed
  • organization.notes.changed
  • organization.sharedTickets.changed
  • organization.sharedComments.changed
  • organization.tags.changed
  • organization.<custom_field_key>.changed

The organization.name.changed event fires when the organization's name in the organization's page changes.

For custom fields, replace {custom_field_key} with the custom field's key attribute. See Organization Fields. Example: my_text_field.

Note: Currently, if another user changes a property in a separate window, the event will not be picked up.

All the events have corresponding data API methods that you can use to inspect the data.

Nav bar and Top bar events

Because nav bar and top bar apps are always active, the app.created and app.activated events fire as soon as Zendesk Support loads. However, this doesn't mean your app pane is visible or in the DOM. The app is inserted into the DOM and becomes visible when a user clicks your app's icon. It is also possible to activate a Nav bar app directly by navigating to it using the URL. If you need to insert the pane into the DOM without making it visible see the preloadPane API.

Once the app pane is in the DOM users can switch away and back to it.

The framework provides the following events to manage the different states: pane.created, pane.activated and pane.deactivated.

pane.created

Fires when the app pane is inserted into the DOM.

 'pane.created': function() {
   //handler code
  }
pane.activated

Fires when the app becomes visible on the page.

 'pane.activated': function() {
   //handler code
  }
pane.deactivated

Fires when the user switches away from your app and the app is no longer visible.

 'pane.deactivated': function() {
   //handler code
  }

Just like app.activated, the handler for pane.activated receives an event object with a firstLoad property to indicate whether the app was inserted into the DOM for the first time. This is useful if you need to manipulate the DOM as soon as the app renders. Note that if the pane has been preloaded by calling the preloadPane API the firstLoad property will be false, since the pane will have already been inserted into the DOM.

Nav bar apps can switch to different states by calling this.switchTo at any time. Once the app gets inserted into the DOM, it renders the template defined by the last call to this.switchTo, along with any data passed to it. If you don't call this.switchTo, the app renders the template defined by the defaultState property.

Note: You can't manipulate the DOM before pane.activated fires for the first time. Here's an example:

  events: {
    // Lifecycle
    'app.created':    'appCreated',
    'pane.activated': 'paneActivated'
  },

  appCreated: function() {
    this.setIconState('inactive', this.assetURL('spinner.gif'));
    this.ajax('getUserRelatedInfo', this.currentUser().id());
  },

  paneActivated: function(data) {
    if (data.firstLoad) {
      this.inDOM = true;
      // DOM manipulation can be done from this point
    }
  }
Nav bar routing event

Nav bar apps differ from other locations in that it's possible to browse directly to the content pane of a specific app using a direct link. Example:

https://z3n.zendesk.com/agent/apps/app-name

The last part of the link, app-name, corresponds to the unique name given to the app at installation. To make the name URL-friendly and format it consistently, the name is run through a sanitization process. Learn more.

To see a nav bar app route in action, install a nav bar app in Zendesk Support and click the app's icon in the Nav bar. The browser's URL should include agent/apps plus the sanitized version of your app installation name.

app.route.changed

In addition to browsing directly to a nav bar app, you can include additional segments in the app URL. Example: https://z3n.zendesk.com/agent/apps/app-name/foo/bar/baz.

When including additional segments, the parts after the sanitized app title are passed to the app being routed to by way of the app.route.changed event.

Example:

  events: {
    'app.route.changed': 'handleRouteChange'
  },

  handleRouteChange: function(evt, eventData) {
    console.log(eventData.appRoute); // "foo/bar/baz"
    console.log(eventData.appTitle); // "my-app"
  }
URL parameters

When the app.route.changed event fires in a nav bar app, the event data object passed to a handler includes a key for appParams. The appParams key contains a JSON object representation of any URL parameters that exist at the time the route is processed.

Note: URL parameters other than the zat parameter are stripped from the URL after processing but are available in the app.

Example:

Given the Nav bar app route https://z3n.zendesk.com/agent/apps/app-name/foo/bar/baz?quux=true&boz=2.

  events: {
    'app.route.changed': 'handleRouteChange'
  },

  handleRouteChange: function(evt, eventData) {
    console.log(eventData.appRoute); // "foo/bar/baz"
    console.log(eventData.appTitle); // "my-app"
    console.log(eventData.appParams); // { quux: 'true', boz: 2 }
  }
Nav bar and Top bar DOM events

You can use the .app_view or .app_pane selectors to listen for events initiated by the nav bar icon or the app pane respectively.

Example:

  events: {
    'click a.app_view': 'handleIconClick',
    'click .app_pane a': 'handleAppClick'
  },

  handleIconClick: function() {
    if (!this.loaded) { return false; }
  },

  handleAppClick: function() {
    var link = event.target.href;
    this.switchTo(link);
    event.preventDefault();
  }

Background events

Ticket saved event

The ticket.saved event corresponds to the completion of the server-side ticket save action. The event is supported for apps that run in the background location. Note that this event does not fire in the ticket_sidebar or new_ticket_sidebar locations because they are generally removed from the interface before this event fires.

Example:

events: {
  'ticket.saved': function(data) {
    this.ajax('sendTicketIdToServer', data.ticket.id()).then(function() {
      services.notify("A ticket ID has been submitted.");
    }).fail(function() {
      services.notify("Failed to submit a ticket ID.");
    });
  }
}

The event handler receives a ticket object as a single argument.