Week 13: Widget Options & Dynamic Instantiation

A widget that only works with one hardcoded table and one hardcoded set of columns isn't really reusable — it's a one-off with extra steps. Widget options are the mechanism that turns a widget into something you configure per page, per instance, the same way you'd configure a component with @Input() bindings.

Module 13 of 25 Week 13 of 26 ~3–4 Hours Hands-on Exercise Included

By the end of this week, you'll be able to

  • Define a widget option schema and set per-instance option values
  • Spawn a widget dynamically from another widget's template using sp-widget
  • Build a single reusable widget configurable for different tables and columns

1. Defining a Widget Option Schema

A widget's Option schema (a field on the widget record) declares named, typed configuration values an instance of that widget can set — the exact same idea as declaring @Input() properties on an Angular component, expressed as JSON instead of decorators.

Option schema — data-table widget
[
  {
    "name": "table",
    "label": "Table",
    "type": "string",
    "default_value": "incident"
  },
  {
    "name": "max_rows",
    "label": "Max Rows",
    "type": "integer",
    "default_value": "10"
  },
  {
    "name": "title",
    "label": "Panel Title",
    "type": "string",
    "default_value": "Records"
  }
]

Once a schema exists, every time this widget is dropped into a container (via the Designer, from Week 9), an editable options form for that specific instance appears — so the same widget record can be configured completely differently on two different pages.

2. Reading Options in the Server Script

Configured option values arrive in the server script through the options object, using the same names declared in the schema:

Server Script — data-table widget
(function() {
  data.title = options.title || 'Records';
  data.rows = [];

  var gr = new GlideRecord(options.table || 'incident');
  gr.orderByDesc('sys_created_on');
  gr.setLimit(parseInt(options.max_rows, 10) || 10);
  gr.query();

  while (gr.next()) {
    data.rows.push({
      display: gr.getDisplayValue(),
      sysId: gr.getUniqueValue()
    });
  }
})();
options vs. input — don't mix these up

options (Week 13) are configured once, when the widget is placed on a page, and stay fixed for that instance. input (Week 12) is sent fresh on every c.server.update() call from client-side interaction. Options answer "how is this instance configured"; input answers "what is the client asking for right now."

3. Spawning Widgets Dynamically with sp-widget

Beyond placing widgets via the Designer, a widget's own HTML template can spawn another widget instance directly, passing options inline — useful when a parent widget needs to render a variable number of child widgets it doesn't know about until its own server script runs.

HTML Template — spawning a widget with inline options
Client Controller — resolving a widget by id/options
function(spUtil) {
  var c = this;

  c.getWidget = function(tableConfig) {
    return spUtil.get('data-table-widget-id', {
      table: tableConfig.table,
      title: tableConfig.title,
      max_rows: 5
    });
  };
}

This pattern — a parent widget deciding at runtime how many child widget instances to render, and with what configuration — is the widget-framework analogue of dynamically creating Angular components with ViewContainerRef, just resolved through spUtil instead.

4. Hands-on Exercise

Hands-on

Build a reusable, configurable data-table widget

Requirements:

  1. Create the data-table widget with the option schema and server script shown above, plus an HTML template rendering data.title as a heading and data.rows as a list.
  2. Place two instances of it on the same page: one configured for incident, one for problem, with different max_rows values.
  3. Confirm both instances render independently with their own configured table and title, from the same widget record.
  4. Add a third option, order_by_field, and use it in the server script's orderByDesc() call instead of the hardcoded sys_created_on.

5. Knowledge Check

Four quick questions. Expand each to check your answer.

Q1

What is a widget option schema the closest analogue to, in component-based frontend frameworks?

It's the widget-framework equivalent of declaring @Input() properties on a component — named, typed configuration values that a widget instance can be configured with when it's placed, expressed as JSON in the widget's Option schema field rather than as decorated class properties.

Q2

How do you read a configured option value inside a widget's server script?

Through the options object, using the same name declared in the option schema — e.g. options.table or options.max_rows — available automatically in the server script whenever that widget instance runs.

Q3

What's the key difference between options and input inside a widget's server script?

options are set once, when the widget instance is configured (via the Designer or when spawned dynamically), and stay fixed for that instance's lifetime. input is sent fresh on every c.server.update() call, reflecting whatever the client is asking for at that specific moment — options answer "how is this configured," input answers "what does the client want right now."

Q4

Why would a widget need to spawn other widgets dynamically via sp-widget instead of only placing them through the Designer?

When a parent widget doesn't know in advance how many child widget instances it needs, or with what configuration, until its own server script has run — for example rendering one data-table widget per table name returned from a dynamic list. The Designer only handles a fixed, known set of widget placements decided at page-build time.