When we left Part 2 of this series, I had added an inbound email action to create GTD actions from forwarded emails. As this development project was always intended to be a demo tool as well, when it came time for us to do demos developing Angular Service Portal widgets I looked around for functionality to add. I had already done some experimentation with using ngMaterial to create a card interface for this Helsinki feature webinar so I decided to bring a similar interface into DoNow.
Having created that prerequisite piece, it was time to actually code the widget.B First a very quick bit of background in how ServiceNow has incorporated Angular into Service Portal. (Docs here for more reading and more resources here.) You’ll see on creation of widget that you have an HTML piece, a client side controller script, and a server script. This breaks down very cleanly into thinking in MVC terms where the server script maintains the Model, the HTML the View and the client script runs the Controller. The Service Portal environment creates a variable for you automatically called “data” which is available in the server scripts. It works much like g_scratchpad. Any data you want available to the front end can be packaged in here and will transfer to the UI. The server script has a full Glide scripting environment and can do anything you expect from script fields (subject to any applicable scoping rules, of course.) This data variable is automatically in the $scope variable in the controller script and can be acted upon by addressing $scope.data . The $scope variable is implicit in the HTML front end so you can then present your values by referencing the data variable.B Enough background, let’s look at code!
Although you could edit any of these components from the Widget form, you will want to use the Widget Editor. It gives you a nice interface that allows you to show, hide or edit any of these scripts as well as a live preview of your widget. This is actually operating on the data of your instance so you can see your code in action immediately. One thing to remember is that although you have a live preview and can act on the data instantly you have to save the code with the button before you see any code changes take effect. It can sometimes feel like so much is happening automatically that you can forget to hit the button and get confused as to why you aren’t seeing updated code. Remember to save.
To start this widget out, I do some Glide scripting. I add the current user’s name and sys_id to the data object because it is very simple to get here and less so from the front end. I also create a hash map with 6 empty arrays mapping my 6 GTD priorities to the empty array. As we loop over the data the GlideRecord is converted to a simple JSON object and added to the appropriate array by dereferencing the hash map. This may seem like overkill if you know that Angular can filter. This may actually get factored out in the future but for now it helps with the hiding and showing of the records. One thing to be aware of is that this script is called each time data is packaged and pushed to the front. Although you have the ability to write any code you want in here, minimize side effects and expensive computation because this code can potentially run frequently.
Next I built the UI portion. Part of the beauty of Angular is that you write your interface in HTML peppered with things called “directives” which can add extra functionality to the rendering of that tag. Those dependencies that were added previously all bring in their own set of directives, which is how they allow you to do different work on the interface. You’ll note that there are tags called “md-checkbox” and “md-content” and “md-card”. These were brought in from the ngMaterial dependency and allow for the creation of the swim lanes full of cards. By organizing into the six md-content buckets each filled with their individual sets of data this allows for some easy showing and hiding. I’m not going to delve too deep into the workings of Angular itself (if you need remedial work, there is a lot to read at the official site) but suffice it to say that ng-repeat is the engine of the looping and ng-model is used to bind various pieces of the data to the user interface. If you look at this you’ll see that we basically fill out a data card for each of the actions that we packed into the data object. If you notice some of the ng-models, you’ll notice that the md-checkbox tags are built by looping over data.priorities and set their ng-model to the priority.model for each priority as it comes up. This is a boolean value (if it wasn’t before, md-checkbox would force it to be one).B B The md-content containers are built by looping over the same array and each has an ng-if associated with the same boolean. This means that when that value is true, the element shows and when it is false it hides. Let’s see that in operation.
Now by checking or unchecking those Priority checkboxes, each of those columns shows or hides. This is the core of Angular in operation, binding various elements to the same data and having the interface respond to it real time. One of the concepts that it took me a while to internalize is that even though we are referencing things that look like strings in these HTML tags of the directives, everything in there is binding variables unless you make it do something else. Almost everything in here is operating by reference so setting ng-if=“priority.model” means that the tag is now bound to the state of the model field of the object contained in the priority variable. I have seen people have problems thinking they are passing values around when actually they are mutually binding user interface elements to the same underlying model and sharing a single data store.
You’ll note that there is a little magic in here to make the layout respond to how many of these columns are showing. I’ll go into more detail in the next post in this series when I dig into some of the code that lives in the controller script. For now, the take home lessons are that you can do the typical Glide scripting and queries on the server side and typical Angular coding in the HTML section. It is a nice compromise for the complexity of bringing these technologies together. There is a little secret sauce that configures some things for you that you would have to do for yourself in pure Angular which is definitely a thing to be aware of should you learn on Service Portal and later build a pure Angular app.
For those who are getting interested in this application (which I hope is at least some of you) the GitHub repository for it is public. You are welcome to fork and examine the code at will. Be aware if you do that this is not a final product and still very much a work in progress. It seems to work but is not guaranteed to be bug free or complete. IOW, caveat emptor and no warrantee exists on this. Note that because of the way ServiceNow integrates with Git we can’t accept pull requests so we lose a little of the power of the platform that way. It is a shame but that is the way the internals work. Feel free to reach out if you do something interesting and we can go look at your repo to see how you have moved the ball down the field.
In the next section I will dig into the controller script and show how REST APIs can be integrated into Service Portal code. Keep watching the skies for that!
It is simple and straightforward to bring in external dependencies into your Service Portal; you can easily query data and build an operational user interface with just a few HTML tags and Angular directives.
Series so far: