Widgets.Base

All widgets – in-built as well as custom – inherit from the base widget. For a full list of the available widgets to extend from, see Base Widgets.

Widgets.Base

Methods and properties that are not defined by default, but instead provided purely for extension purposes, are highlighted as follows: EXTEND.

The examples in the reference omit the boilerplate that is needed. For example if you are inheriting from the text widget and setting the customEl property, the code would look like this:

widget.js
return Widgets.Text.extend({
    customEl: '.widget-content-body',

    ... // Other code inside the class here
}) 

For conciseness this is abbreviated below as follows:

Example
customEl: '.widget-content-body',

Life cycle

The widget life cycle is split into a number of stages in order:

  1. Initialization
    1. External module management
    2. Data prefetching
  2. Widgets.Base#Data loading
  3. Widgets.Base#Rendering
  4. Interaction
  5. Widgets.Base#Destruction

The methods and properties are all documented in their respective stage.

Properties that are set by the base widget (as for example model) are documented in the stage where they become available. These properties are only available in stages after the one that set it.

Initialization

In this stage the widget is wired up with the basics, configuration options, visual options (if needed).

The stage is executed once when creating the widget (once in the life cycle of a dashboard).

If models or collections are required, they should be initialised here. By default, a widget extending Base widget (without visuals or logic) gets a initialized Squirro Item collection. Extending other widgets gets their respective collections.


afterInitialize()

widget.afterInitialize(/* no arguments */)     EXTEND

This method is called after the class has been initialized. It is a good place to set up preloading , event listeners, etc.

Example
afterInitialize: function () {
    this.updateTime();
},

External module management

It is possible to bundle a custom widget together with additional JavaScript modules, for example 3rd party libraries. The API provides support for managing such modules on several levels.

At the very least, a customResources object is constructed, mapping module (file)names to script load Promises. The load Promise, when executed, will asynchronously download the module and execute it in global context, resolving itself when done, and therefore allowing followup code to be executed afterwards.

Example
this.customResources['my-module.js']

To execute code after module is loaded, execute the promise and chain a followup function as you would with Promises.

Example
this.customResources['my-module.js']().then(function () {
    // Module has been loaded, do something.
});

A further level of management is available (and recommended to use) in the API. A custom widget may request registration of JavaScript modules on the dashboard level, to ensure modules are:

  • reused across widgets, if same modules are required.
  • reused across instances of one custom widget, if more than one exists in any given dashboard.
  • loaded before stages following initialization are reached (in beforeSetup stage already).
  • if required, can loaded before any widget renders (global dashboard modules)

It also helps to reduce code complexity by reducing the need to create chained, nested code.

Registration of modules should happen in the initialization stage, and can be done using the dashboard model's registerPreloadedResource method as follows:

registerPreloadedResource()

dashboard.registerPreloadedResource(name, resourcePromise, context)

Used to register custom dashboard JavaScript modules.

Example
afterInitialize: function () {
    this.dashboard.registerPreloadedResource('my-module', this.customResources['my-module.js'], this);
},

In this example, we've simplified the entire module management to one call, during the widget's initialize stage, to dashboard models' registerPreloadedResource method.

The method takes three parameters:

  • name - the unique identifier for the module. If more than one widget requests modules of the same name, it will be loaded only once and its load promise shared with the views that require it.
  • resourcePromise - promise of asynchronously loading and executing the script in global context
  • context - determines whether loading of the module should block only the widget defined by passing its view reference here; or the entire dashboard, when the context is not passed.
    • Useful when loading a module is essential to the correct rendering of the dashboard, and it is inconvenient for one reason or another, to ensure loading on a per-widget basis.

Guarantees:

  • The module will be fetched asynchronously only on demand (by calling directly customResources promise and/or the registerPreloadedResource method).
  • The module is ready and available during the beforeSetup stage already, so before collection initialisation.

Data prefetching

The API supports loading additional data via Backbone Models/Collections using the registerPrefetch() pattern. Prefetching should be registered during the initialization stage, and ensures the data objects are ready to use in beforeSetup stage already.

registerPrefetch()

widget.registerPrefetch(model)

Used to register custom Backbone models/collections.

Example
afterInitialize: function () {
    // Ensure user object is prefetched before
    this.registerPrefetch(this.user);
},

Any Backbone Model/Collection can be registered in this way. For more information about Backbone.js, please consult its documentation.

Example
afterInitialize: function () {
	this._customModel = new Backbone.Model({
		url: '/my/custom/api'
	});
	this.registerPrefetch(this._customModel);
},

Setup

In this stage the collection is set up to prepare for data to be loaded from the Squirro backend.

This stage is executed once, when the widget is created.

beforeSetup()

widget.beforeSetup(/* no arguments */)     EXTEND

This method is called before the setup phase happens.

afterSetup()

widget.afterSetup(/* no arguments */)     EXTEND

This method is called after the setup phase happens. This is a useful place to attach events to the collection, as it's initialised here (but not yet loaded).

Example
afterSetup: function () {
    this.collection.on('add', this.itemAdded, this);
},

collection

widget.collection

A Backbone.js collection as instantiated by the base widget. This contains all the data that the widget needs for rendering. It is fetched before the rendering stage.

Example
afterRender: function () {
    console.log(this.collection.length);
},

dashboard

widget.dashboard

A Backbone.js model that represents the currently rendered dashboard. See dashboard store for the main usage of this model.

getCustomCollectionParams()

widget.getCustomCollectionParams(/* no arguments */)     EXTEND

Returns an object with key/value pairs of parameters which should be passed into the widget's collection constructor.

Currently supported parameters:

  • additionalQuery - specifies custom query part that will be added to the current dashboard query to produce the widget's query.
    • Allows presenting different datasets in different widgets
    • Note, produces more requests. Always take care to ensure the overall performance is not affected.
  • createdBefore - specifies an upper time restriction for the widget's collection fetch.
    • Supports both absolute (ISO format) dates and relative strings, e.g. '24h', '3months'
  • createdAfter - like createdBefore, but for lower time restriction
  • keywords - set to true to include the item keywords in the initial result
Example
getCustomCollectionParams: function () {
	return {
		additionalQuery: 'Company:Squirro',
		createdBefore: '24h',
		createdAfter: '2011-01-01T00:00:00',
	};
},

Rendering

In this stage the widget content is rendered into its container. The container is managed by the Squirro Dashboard automatically. So after this stage the widget is fully in the page's DOM tree and displayed to the user.

This stage is executed each time the widget is rendered - usually when the dashboard query changes as a result of a search or a filter interaction.

afterRender()

widget.afterRender(/* no arguments */)     EXTEND

This method is called after the rendering has been done (after renderContent). When this is executed, the widget is guaranteed to have injected itself into the DOM, and thus normal techniques of manipulating DOM elements apply (jQuery can be used for example).

The default implementation of afterRender appends the custom template to the widget and also installs any custom events (see Widgets.Base#customEvents).

Example
afterRender: function () {
    this.$el.append(this.customTemplate());
},

customEl

widget.customEl     EXTEND

A special property of the widget that defines the element into which the custom template is rendered. This is a CSS selector that is executed against the root node of the current widget.

Example
customEl: '.widget-content',

customTemplate()

widget.customTemplate(/* no arguments */)

Renders the custom template and returns the resulting HTML code. The default behavior of this method is to call the customWidgetTemplate function with the return value of getCustomTemplateParams method.

customWidgetTemplate()

widget.customWidgetTemplate(params)

This is a templating function that returns a HTML string templated from the given params hash.

This is assigned automatically by Squirro to be a Underscore.js template function rendering the widget.html file.

getCustomTemplateParams()

widget.getCustomTemplateParams(/* no arguments */)     EXTEND

Returns a hash of all the parameters that are passed into the custom template. The return value of this method is used as input for the customWidgetTemplate call.

customResources

widget.customResources['myTemplate.html'](parameters)

If any additional (apart from widget.html) HTML templates have been uploaded with the widget, they can be accessed trough this property.

Contains a dictionary with keys being the template file names, and values the preconfigured template functions, which will return the correct HTML string upon execution.

Optional parameters can be passed in and accessed in the template.

widget.customResources['myStylesheet.css']()

If any additional (apart from widget.css) CSS stylesheets have been uploaded with the widget, they can be accessed trough this property.

Contains a dictionary with keys being the template file names, and values the functions which will ensure the CSS is in the document for the lifecycle of the widget (and will be unloaded afterwards).

renderContent()

widget.renderContent(/* no arguments */)

This renders the content of the widget. In the base widget, this method does nothing - but in all the other widgets it contains the logic for displaying the widget content in the dashboard.

To prevent the default rendering of a widget completely, replace this function with noop:

renderContent: _.noop

In that case rendering can be fully customized and overwritten in the afterRender method.

isEmpty()

widget.isEmpty(/* no arguments */)

This should return true if the widget does not have any data to display. The default implementation uses this.collection.size() and returns true if the size is 0. When creating a widget from a facet collection, that is usually not the desired behaviour. In those cases, isEmpty can be overwritten.

As a lot of the processing of a widget may be needed to determine whether any data is to be displayed, widgets sometimes end up caching the result of that in the isEmpty method. The following is an example from a D3 widget:

isEmpty: function () {
    var squirroData = this.collection.indexed.$item_created_at.values;

    this.d3_data = d3.nest()
         …
         .map(squirroData);

    return this.d3_data.size() === 0;
},

ALLOW_OVERFLOW

ALLOW_OVERFLOW: false

If the widget contains content which will pop up or out of the widget boundary then set this value to true.

Interaction

Squirro Widgets provide interaction by subscribing events to its DOM elements and handling them. All of these events fall into the interaction stage.

The recommended way to install custom events is Widgets.Base#customEvents. To add the event handlers manually, for example by using jQuery listeners directly, attach them in the afterRender method.

customEvents

widget.customEvents     EXTEND

The customEvents hash can be used to specify a set of DOM events that will be listened for and delegated to methods in the widget view. This is an extension to the default events hash of Backbone.js and behaves very similarly.

This is an example that extends the facets table with an additional click handler.

See dashboard store for information on the store property being used here.

return Widgets.FacetsTable.extend({
    customEvents: {
        'click tr': 'onRowClicked',
    },
 
    onRowClicked: function () {
        this.dashboard.store.set({'additionalInfo': true});
    },
});

This does not currently work. Instead, use the _.extend pattern with the events property:

return Widgets.FacetsTable.extend({
    events: _.extend(Widgets.FacetsTable.prototype.events, {
        'click tr': 'onRowClicked',
    }),

    onRowClicked: function () {
        this.dashboard.store.set({'additionalInfo': true});
    },
});

onStoreChange()

widget.onStoreChange(model)     EXTEND

Called when a store variable changes (see Widgets.Base#Dashboard Store).

The model argument can be used to access the exact property or properties that have changed by accessing model.changed. That property is a hash containing all the attributes that changed in the last set call.

Reference: https://backbonejs.org/#Model-changed

Example
onStoreChange: function (model) {
    if (_.has(model.changed, 'mykey')) {
        // mykey has changed
    }
},

Destruction

In this stage, the widget views are destroyed, its events unbound and its content removed from the DOM.

This stage is executed once, when destroying the widget.

afterClose()

widget.afterClose(/* no arguments */)     EXTEND

This method is called when the widget is being removed from the document. It is a good place to clean up, such as removing event listeners.

Example
afterClose: function () {
    this.cancelTimer();
},