- Post History
- Subscribe to RSS Feed
- Mark as New
- Mark as Read
- Bookmark
- Subscribe
- Printer Friendly Page
- Report Inappropriate Content
on 03-18-2021 01:01 PM
As we explored in a previous post, querying and displaying data is a fundamental part of most widgets. The original example I used simply displays a drop-down list of group names where the current user is the manager, and nothing more. To recap, our goal is to build a widget that can: Allow a user who is the manager of a group to easily add or remove group members in real-time via the self-service portal.
For our next exercise we're going to expand our widget a little so that we can react to dynamic user input and also display some additional information, based on that input. We've already setup some of the ground work for this when we created our widget, but we have a little work to do.
For our HTML Body we're going to use one of the most commonly used AngularJS directives in all of widgetdom: ng-repeat. This simple-at-first-glance directive allows us to iterate over an object or array and perform the actions, in this case, creating some simple html to display data.
Body HTML Template:
<div class="panel panel-primary">
<div class="panel-heading">Your Groups</div>
<div class="panel-body">
<sn-choice-list field="snGroup" sn-model="c.group" sn-items="data.groups" sn-value-field="sys_id" sn-text-field="name" sn-on-change="c.selectGroup(selectedValue)"></sn-choice-list>
</div>
<div>
<p ng-repeat="member in data.members">{{member.name}}</p>
</div>
</div>
If you look at the ng-repeat line we are basically specifying a for-loop. We've identified that within our data object we want to take the array of objects found in members and iterate over the array. Within the text area of the <p> tag we want to take the the current member of the array in the loop and populate the name property. Take note that inside the tag, you use object and property names the same way you would in a script, but outside of the tags you need to wrap the statement in {{}} so that you get the value of the expression, not just the text you typed.
Also, take note that we've added an sn-on-change to our sn-choice-list field - notice that the function has a value being passed to it - selectedValue - this is where the "magic" happens as, if you only defined a function without passing in this variable, you wouldn't have input to react to. We'll need to define the function that we're calling later in the client-controller to react to user input but first, we can setup the server script to return the information we're after; the list of members of the currently selected group.
Server Script:
(function() {
data.groups = []; //prepare an array for groups
data.members = []; //prepare an array for members
//query the server to populate the array
var groups = new GlideRecord('sys_user_group');
//build the query based on the logged in user as the group manager
groups.addQuery('manager=' + gs.getUserID());
groups.query();
while(groups.next()) {
//Because we are using a sn-choice-field we want to use an object
//This lets us define different values for the label and the value
var group = {};
group.sys_id = groups.getUniqueValue(); //we will use sys_id as the value to make referencing these groups easier later on
group.name = groups.name.toString(); //we will use name as the label that we display to the user
//Now we push the object to the array and repeat
data.groups.push(group);
}
//We have recieved input from the client
if(input.action == 'getMembers') {
//we want to return the names and sys_id's of the group members
var members = new GlideRecord('sys_user_grmember');
members.addQuery('group=' + input.group_id);
members.query();
while(members.next()) {
//For ease of use later, we will once again use an array of objects:
var member = {};
member.sys_id = members.user.sys_id.toString();
member.name = members.user.name.toString();
//Push the object to the array
data.members.push(member);
}
}
})();
Most of the code above is the same as our first iteration, but we've added a new section and defined a new element in the data object to hold some values - data.members.
The thing to take note of here is the statement "if(input.action == 'getMembers') {"
This statement allows us to check whether we have received any input from the client using the reserved object input. We'll look a little more closely at this in the client-controller, but here we can see that we are checking whether we have specifically received the action "getMembers" and are then also able to use the value of group_id that has been passed from the client to build our query. This block of code is not executed when the widget first loads, instead, we wait for the user to make a selection and then act on it, performing a query and returning data via the data object.
Now, on to the client side!
Client Controller:
api.controller = function() {
/* widget controller */
var c = this;
c.selectGroup = function(group_id) {
c.server.get({
action: 'getMembers',
group_id: group_id
}).then(function(r) {
c.data.members = r.data.members;
});
};
};
We'll take a moment to explore something that might, or might not be obvious at first. Notice the first line after /*widget controller */. That statement, var c = this; is kind of important. This is declaring that c is a reference to the object that is your client-side widget and it is through "c" that you will access much of your data and functionality.
Defining a client-side function can be a little confusing as the "typical" approach wont work. If you tried to add:
function myFunc() {
alert('hello');
}
You would get...nothing, not even an error.
In order to define a function that you can call from your HTML Template, you need to actually add it to the object that defines your client-side widget, hence the syntax of "c.selectGroup = function()..."
Back to our script.
This is a simple, but powerful function - note that is has the name we defined in the sn-on-change for our field and we've defined an input variable to accept what is being passed from the client-side. Now, in order to pass that value back to the server and process the response, we need to call the available function c.server.get(), to which we are passing an object that will become the input object on the server side. In this example we are defining action=getMembers and group_id=value passed from our field.
After the initial function call we append a .then statement, which allows us to define a callback function that will execute once we have a response from the server. In my example I'm using an anonymous function as my callback and passing in "r" to hold the response from the server, which will be be the entire data object from the server-side script.
The final step in this function is to take the server data and refresh the client data. Since the only values we expect will have changed would be the users in data.members, we set c.data.members (the local data object) to match the values returned in r.data.members (the data object contained in the server response.
Once we've saved our widget, if we've done everything right, we can refresh our test page and hopefully see something like this:
Congratulations! You just created a widget that can not only display data from the server to the user, it can react to input form the user and display more relevant data!
If you found this article useful or helpful, please be kind a click appropriately! If you really found it useful, maybe bookmark it for later reference 🙂
I hope this helps!
Michael Jones - Proud member of the CloudPires team!
- 2,217 Views
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
Very useful post.
I would like to do below one for ServiceNow Outlook Addin:
How we can pass the client controller data to Server Side and process the data and get the sys_id and pass that to client side and set the value on the form?
For instance: in Client controller i am getting the user email id and i am passing the email id to server side to get the user sys_id.
Once i have the user sys_id, i need to pass this to client side and set the sys_id of caller.
Any thoughts would be highly appreciated.
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
Hi
waiting for the next part (Learning to build widgets from scratch: Part 4)