Widget structure
Every widget has four parts:
- HTML Template — the rendered markup, using AngularJS binding syntax
- Client Controller — AngularJS controller for client-side logic
- Server Script — runs on the server when the widget loads
- CSS / SCSS — widget-specific styles
Data flow: server to client
The server script runs first when the widget loads. It populates the data object with server-side data. This data is then available in the client controller via c.data:
// Server Script
(function() {
data.incidents = [];
var gr = new GlideRecord('incident');
gr.addEncodedQuery('active=true^assigned_to=javascript:gs.getUserID()');
gr.setLimit(10);
gr.query();
while (gr.next()) {
data.incidents.push({
number: gr.getValue('number'),
short_description: gr.getValue('short_description'),
state: gr.getDisplayValue('state')
});
}
})();// HTML Template
<div>
<div ng-repeat="inc in c.data.incidents">
<strong>{{inc.number}}</strong> — {{inc.short_description}}
</div>
</div>Client-to-server communication
To call the server from the client (after initial load), use c.server.get():
// Client Controller
api.controller = function($scope, $timeout) {
var c = this;
c.refreshData = function() {
c.server.get({action: 'refresh', filter: c.data.filter})
.then(function(response) {
c.data.incidents = response.data.incidents;
});
};
};
// Server Script - handle client calls
if (input && input.action == 'refresh') {
// Re-query with new filter
data.incidents = getIncidents(input.filter);
}spUtil service
spUtil is the Service Portal utility service available in client controllers. Key methods: spUtil.addInfoMessage(), spUtil.addErrorMessage(), spUtil.openModal() for modals.