

- Subscribe to RSS Feed
- Mark as New
- Mark as Read
- Bookmark
- Subscribe
- Printer Friendly Page
- Report Inappropriate Content
I feel like I get to teach this ServiceNow concept at least once a month and so it’s about time that I write it all out!
Hi everyone, I’m Earl, one of your developer advocates here at ServiceNow, and today we’ll explore the dance between the server script and the client script, and how the data
and input
objects enable our widgets to communicate back and forth.
Basically, if you’ve ever worked with ServiceNow widgets and found yourself wondering, “Wait, how does data
actually get from point A to point B?” then this is for you.
The Cheat Sheet
Maybe you’re here just for the cheat sheet/summary. Here you go:
The Big Picture
Before we jump into code, let’s take a moment to paint the bigger picture. In a ServiceNow Service Portal widget, we have two main pieces:
-
Server Script – This runs on the ServiceNow server. It initializes or updates the
data
object. -
Client Script – This runs in the user’s browser. It sees
$scope.data
and can interact with the user, update things on the screen, and—crucially—send information back to the server using$scope.server.update()
.
At first, it might sound a bit like a tennis match: the ball (our data) goes from server to client, then from client to server, and back again. But once you see it in action, it feels quite intuitive.
A Minimal Example
Server Script
(function() {
data.serverMessage = "Hello from the server!";
})();
Client Controller
api.controller=function() {
var c = this;
console.log("Server says:", c.data.serverMessage);
}
HTML Template
<div>
<p>The server message is: {{data.serverMessage}}</p>
</div>
Here’s our first mini-walkthrough. You can think of it as the “Hello World” of ServiceNow widgets.
-
In our Server Script, we’re setting
data.serverMessage = "Hello from the server!"
. -
The Client Controller sees
c.data.serverMessage
as soon as the page loads. -
Our HTML template simply displays it with
{{data.serverMessage}}
.
Your data is officially traveling across the internet from your instance to the user’s browser. It’s a small start, but it’s the foundation of everything else we’ll be exploring.
Introducing input
/* Server Script */
(function() {
if (input && input.displayName) {
data.displayName = input.displayName;
} else {
data.displayName = "No name provided";
}
})();
Now let’s add a twist: the input object. Sometimes, your widget needs parameters—maybe the user wants to provide some information. In Service Portal, that’s where input
comes into play.
Think of input
as the “stuff coming in” from the client script. If input.displayName
exists (from the client script/user), we set data.displayName
to that; otherwise, we default it to “No name provided.”
This ensures your widget can be adapted to different situations without needing to rewrite the code every time.
Client-to-Server Communication
So far, we’ve learned that the server sends data to the client on page load. But what if we want to go the other way? Let’s create a user-driven scenario:
-
The user types a greeting in an input box.
-
We send that greeting to the server script.
-
The server script modifies some data based on the user’s input.
-
The server sends a response back.
Code for Two-Way Data Exchange
Server Script
(function() {
data.serverMessage = "Hello from the server!";
if (input && input.greet && input.greetText) {
data.serverMessage = "Server responds: " + input.greetText;
}
if (input && input.displayName) {
data.displayName = input.displayName;
} else {
data.displayName = "No name provided";
}
})();
Client Controller
api.controller=function() {
var c = this;
c.clientText = "";
c.sayHello = function() {
c.data.greet = true;
c.data.greetText = c.clientText;
c.server.update().then(function() {
alert("Server says: " + c.data.serverMessage);
});
};
}
HTML Template
<div>
<h3>Hello World Widget</h3>
<p>Display Name: {{data.displayName}}</p>
<div>
<input type="text" ng-model="c.clientText" placeholder="Type a greeting..." />
<button class="btn btn-primary" ng-click="c.sayHello()">Send Greeting</button>
</div>
<p>Server Message: {{data.serverMessage}}</p>
</div>
Here’s where the magic happens:
-
Server Script sets a default
serverMessage
and updates it if we seeinput.greetText
. -
Client Script collects the user’s input in
c.clientText
and usesc.server.update()
to send data back. -
ServiceNow receives this data as
input.greetText
, updatesdata.serverMessage
, and returns it to the client. -
The client’s
c.data
is refreshed, so we can display the newserverMessage
.
This pattern is crucial for any widget that needs to respond dynamically to user actions. You can query tables, run server-side logic, and funnel the results back to the user, all in a matter of milliseconds.
Recap of the Data Flow
Let’s recap the journey of our data:
-
Page Load
-
The server script runs, initializing the
data
object. -
This
data
object travels to the browser asc.data
.
-
-
Client Updates
-
The user interacts with the widget (e.g., types in a text box).
-
The client sets
c.data
properties and callsc.server.update()
.
-
-
Server Re-run
-
The server script runs again, reading what the client sent via
input.*
. -
The script updates the
data
object based on that new input.
-
-
Data Returns
-
The updated
data
object is sent back to the browser, refreshingc.data
.
-
Real example
Server Script
(function() {
var incidentGr = new GlideRecord('task');
if (input && input.textQuery){
data.textQuery = input.textQuery || 'no query provided';
incidentGr.addEncodedQuery('short_descriptionLIKE' + input.textQuery);
}
incidentGr.setLimit(10);
incidentGr.query();
data.tasks = [];
while(incidentGr.next()){
var task = {
number: incidentGr.getValue('number'),
short_description: incidentGr.getValue('short_description')
}
data.tasks.push(task);
}
})();
Client Controller
api.controller=function() {
var c = this;
c.clientText = "";
c.searchForText = function() {
c.data.textQuery = c.clientText;
c.server.update().then(function() {
console.log(c.data.tasks)
});
};
}
HTML Template
<div>
<h3>List of tasks</h3>
<p>Text query: {{data.textQuery}}</p>
<div>
<input type="text" ng-model="c.clientText" placeholder="Text query..." />
<button class="btn btn-primary" ng-click="c.searchForText()">Query</button>
</div>
<ul ng-repeat="task in data.tasks">
<li>
{{task.number}} - {{task.short_description}}
</li>
</ul>
</div>
Putting It into Practice
Next time you’re building a widget:
-
Start simple: set default data in your server script and confirm you can display it in the template.
-
Introduce user input: accept some text or a choice in the client script.
-
Communicate: use
c.server.update()
to send data back, and have the server script respond accordingly.
Final Thoughts
You’ve just learned how the data
object is formed on the server, delivered to the client, and then travels back again via input
on updates. In the real example, you can see the common use-case of taking a user’s input (text, clicks, etc.) and affect the server script of the widget itself.
That’s all for today. For most of us Service Portal veterans, this data/input interaction is second nature to use at this point and hopefully that’s you now too!
- 5,746 Views
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.