1. c.server.update()
Calling c.server.update() re-runs the widget's server script from
the client, sending along whatever is currently on c.data, and
returns a promise that resolves once the server script finishes — at which
point c.data is replaced with whatever the server script produced
this time.
function() {
var c = this;
c.data.searchTerm = '';
c.runSearch = function() {
c.data.action = 'search'; // the server script reads this to decide what to do
c.server.update(); // re-runs the server script; c.data gets replaced on resolve
};
}
(function() {
if (input && input.action === 'search') {
data.results = [];
var gr = new GlideRecord('incident');
gr.addQuery('short_description', 'CONTAINS', data.searchTerm || '');
gr.setLimit(20);
gr.query();
while (gr.next()) {
data.results.push({
number: gr.getValue('number'),
shortDescription: gr.getValue('short_description')
});
}
}
})();
2. The input Object
Notice the server script reads from input, not data,
to check what the client sent — input is the server script's view
of the client's c.data at the moment update() was
called, distinct from the data object the script is about to
populate for the response. Keeping this distinction straight (client sends via
input, server responds via data) avoids a lot of
confusion once a widget has several different actions flowing through the same
update() call.
3. Loading & Error States
c.server.update() returns a standard promise — use
.then() and a boolean flag to drive a loading indicator, exactly
like you would around any async call in a modern front end:
c.runSearch = function() {
c.loading = true;
c.error = null;
c.data.action = 'search';
c.server.update().then(function() {
c.loading = false;
}, function() {
c.loading = false;
c.error = 'Search failed. Please try again.';
});
};
{{c.error}}
4. Embedding One Widget Inside Another
A server script can render a completely separate widget and hand its markup to the current widget's data — useful for composing a page out of smaller, reusable widgets from within a parent widget's own server script, rather than only via the page designer.
(function() {
data.headerWidget = $sp.getWidget('page-header', {
title: 'Search Results'
}).html;
})();
5. Hands-on Exercise
Build a live incident search widget
Requirements:
- Build the search widget shown above, with an
ng-model-bound search box and a "Search" button callingc.runSearch(). - Add the loading/error state pattern, including a disabled button state while a search is in flight.
- Deliberately break the server script (e.g. reference an undefined variable) and confirm your error state renders correctly when the promise rejects.
- Fix the server script, then confirm the happy path renders a result list correctly.
c.server.update() rejects its promise when the server script throws an unhandled exception — that's the mechanism your error-state test in step 3 is exercising, not something you need to manually simulate.
6. Knowledge Check
Four quick questions. Expand each to check your answer.
Q1
What does c.server.update() actually do?
What does c.server.update() actually do?
It re-runs the widget's server script from the client, sending along the current contents of c.data, and returns a promise that resolves once the server script finishes — at which point c.data on the client is replaced with whatever the server script produced during that run.
Q2
What's the difference between input and data inside a widget's server script during an update() call?
What's the difference between input and data inside a widget's server script during an update() call?
input is the server script's read-only view of what the client's c.data looked like at the moment update() was called — how the script knows what the client is asking for. data is the object the server script populates to send back as the new c.data on the client. Client-to-server flows through input; server-to-client flows through data.
Q3
How do you build a loading indicator around a c.server.update() call?
How do you build a loading indicator around a c.server.update() call?
Set a boolean flag (e.g. c.loading = true) before calling update(), then use .then() with success and failure callbacks to reset it to false in both cases — success clears it normally, failure clears it and also sets an error message. The template reads that flag with ng-if/ng-disabled to reflect the in-flight state.
Q4
What does $sp.getWidget() let you do that the page designer alone can't?
What does $sp.getWidget() let you do that the page designer alone can't?
It lets a widget's own server script render a separate widget programmatically and use its output HTML as part of the current widget's data — composing widgets together from within server script logic, rather than only by dragging widgets into containers via the Service Portal Designer.