When we are all done, we should have an UI Page that looks something like this:
If you haven't read the first post about this subject I recommend you read this post first: Part 1: AngularJS, JSON & public UI Pages. How do I get it all together?
First thing I did was to create a new table. I made it extend from task so I could take advantage of some functions/fields task already has. Not that I found them useful in the first version of this, but I hate to paint myself into a corner in the future when realizing I should have extended it.
I found some reasons what happens when you extend and I think it pretty much sums it up: What you get by extending the Task table
What it doesn't mention is that you need to keep in your head that if you put a Business rule on the table task, it will also hit this table, since this table is extended from task.
Here is the table:
I created an table named Service Status which extends from task.
I also choose to have check the "auto-number" so these records gets a own prefix, but it is a personal preference and has nothing do do with this experiment.
The Form:
After this I go to the new module that was created and press the "new" to get to the form for some adjustments.
Here I remove all fields that I don't have any need to see and I also add the following fields:
* Order - I use the field that comes from "task". Use to decide in what order it shall be presented on page.
* Created - I use the field that comes from "task". Just so I can easy when it was created.
* Update - I use the field that comes from "task". Just so I can easy when it been updated.
* Valid to - This is a normal "Date/time". Use to decide if a record should be on the history or Active tab.
* Type - This is a choice field with the following choices:
- "Information" with value "primary" - This is use to for records that comes from a planned changed
- "Active Incident" with value "danger" - This is use for records that shows an active incident
- "Resolved Incident" with value "success" - This is use for records that now is a resolved incident
It is important that you have the correct values. These values will be used on later to decide what colors each status message will have on the UI Page.
Next thing is to make the field order mandatory. Since we will be using order to decide the order on the UI page, it's quite important that this field is mandatory and we do this by choosing the "configure dictionary" and then we check the "mandatory" square.
And for last we choose "configure dictionary" on "type" and choose "Dropdown with --None--". This because we need a value here so the colors will be correct on the UI Page.
The you will have a form that looks something like this.
Next up is to create a few test cases:
Now I create a few test examples so I can see when I'm finished that it works as intended and I haven't forgotten anything.
I need to create the following:
These three shall be visible on the "active tab".
- Type: Information. Valid to: Date that is in the future. Order: 100
- Type: Active incident. Valid to: Empty. Order 50
- Type: Resolved incident. Valid to: Date that is in the future. Order: 200
These two shall be visible on the "history tab".
- Type: Information. Valid to: Date that has passed. Order: 300
- Type: Resolved incident. Valid to: Date that has passed. Order: 250
And it would look like this:
As you can see I have also adjusted the list layout to fit the new fields.
Create UI Scripts:
Now that we are done with the new table and it's looks we are heading to the UI Scripts. We are going to create 4 UI Scripts. 3 of them are just imports and I choose to save the code inside ServiceNow so I have control over the version and what it will show.
These are the ones I import from their CDN:
AngularJS 1.5.0-RC.0:
jQuery 1.11.3:
Bootstrap 3.3.6:
What you do is that you press "new" on the UI Scripts list. You fill in a API name. below you will se my recommendations of name standard. Make sure "global" is empty. And then you go the correct link of above and take the code that is shown on that page and paste it into the script field.
Then you get something like this:
After you done those three you have one left and it's own script which if you have a UI Page only can put the code in the "client Script" editor instead. I'm doing like this to be able to reuse the UI Page code in for example a Dynamic block context on the CMS.
This code contains the module & controller for the angular and a few lines in the end to be able to click on the different tabs.
Create a UI script with the name: servicestatuscode
Put the following code in the script-editor:
var myApp = angular.module('servStat', []);
myApp.controller('MainController', ['$scope', function($scope) {
//Function to call the function getActive within the Script Include
$scope.getAllActive = function () {
var ga = new GlideAjax('getServiceStatus');
ga.addParam('sysparm_name','getActive');
ga.getXML(showStatus);
};
//Function to call the function getHistory within the Script Include
$scope.getAllHistory = function () {
var ga = new GlideAjax('getServiceStatus');
ga.addParam('sysparm_name','getHistory');
ga.getXML(showStatus);
};
//This is the function to handle the JSON-response from the script include and put it in the angular variable
function showStatus(response) {
var answer = response.responseXML.documentElement.getAttribute('answer');
$scope.$apply(function () {
$scope.numbers= angular.fromJson(answer);
});
}
}
] );
//This is only to made the tabs clickable.
$(document).ready(function(){
$(".nav-tabs a").click(function(){
$(this).tab('show');
});
});
Now your UI Script list would look like this:
Create the Script include:
Now it's time to create the script include (getServiceStatus) that the code above is calling.
So venture into script include and press "new". Give it the name getServiceStatus. Don't forget to check the square "Client Callable". If you forget this little setting, it will not work.
Put the following code in the script-editor:
var getServiceStatus = Class.create();
getServiceStatus.prototype = Object.extendsObject(AbstractAjaxProcessor, {
//Function that angular calls to get all records that shall be shown on active tab
getActive: function() {
//Variable that will contain all the objects that we find
var recact = [];
//Create query to fetch the records where valid date hasnt passed or valid date is empty
var queryString = "u_valid_to>=javascript:gs.daysAgoStart(0)^ORu_valid_toISEMPTY";
var gr = new GlideRecord('u_service_status');
gr.addEncodedQuery(queryString);
gr.query();
//Go through all records and take those fields that I want and put them in a variable that I push into recact variable.
while(gr.next()) {
var svar = {};
svar.number = gr.number.toString();
svar.short_description = gr.short_description.toString();
svar.u_type = gr.u_type.toString();
svar.description = gr.description.toString();
svar.sys_updated_on = gr.sys_updated_on.toString();
svar.sys_created_on = gr.sys_created_on.toString();
svar.order = gr.order.toString();
recact.push(svar);
}
//After we are done with all the records we throw in JSON and send it back.
return new global.JSON().encodeArray(recact);
},
//Function that angular calls to get all records that shall be shown on history tab
getHistory: function() {
//Variable that will contain all the objects that we find
var rechist = [];
//Create query to fetch the records where valid date has passed
var queryString = "u_valid_to<javascript:gs.daysAgoStart(0)";
var gr = new GlideRecord('u_service_status');
gr.addEncodedQuery(queryString);
gr.query();
//Go through all records and take those fields that I want and put them in a variable that I push into rechist variable.
while(gr.next()) {
var svar = {};
svar.number = gr.number.toString();
svar.short_description = gr.short_description.toString();
svar.u_type = gr.u_type.toString();
svar.description = gr.description.toString();
svar.sys_updated_on = gr.sys_updated_on.toString();
svar.sys_created_on = gr.sys_created_on.toString();
svar.order = gr.order.toString();
rechist.push(svar);
}
//After we are done with all the records we throw in JSON and send it back.
return new global.JSON().encodeArray(rechist);
},
//This we need to have otherwise this script include don't work on public UI Pages.
isPublic: function() {
return true;
}
});
Last Part, UI Page:
Now we are seeing the end. Now we are going to use all of the above code inside a UI Page.
There isn't so much to say about it. It is pretty straight forward and I haven't spent a lot of time making the design. That will come later when I know where and exactly how I want it, if we now ever use it =). I just named it "showServiceStatus".
<?xml version="1.0" encoding="utf-8" ?>
<j:jelly trim="false" xmlns:j="jelly:core" xmlns:g="glide" xmlns:j2="null" xmlns:g2="null">
<!-- Import UI Scripts -->
<!-- Remember that the jquery must be above the bootstrap otherwise it won't work. -->
<g:requires name="angular.min.jsdbx" />
<g:requires name="jquery.min.jsdbx" />
<g:requires name="bootstrap.min.jsdbx" />
<g:requires name="servicestatuscode.jsdbx" />
<!-- The User Interface -->
<div class="container">
<h2>Service Status</h2>
<p>Here you can choose between active information and look at the history. Click on the subject on each serviceinformation to read more about it.</p>
<!-- Code for the two tabs -->
<ul class="nav nav-tabs" role="tablist">
<li class="active"><a href="#activeinfo"><span class="glyphicon glyphicon-alert"></span>$[SP]$[SP]Active</a></li>
<li><a href="#history"><span class="glyphicon glyphicon-folder-open"></span>$[SP]$[SP]History</a></li>
</ul>
</div>
<!-- Build up the display of records with angular and tabs -->
<div id="Serviceinfo" ng-app="servStat">
<div class="tab-content">
<!-- First comes the Active Tab -->
<div id="activeinfo" class="tab-pane fade in active">
<!-- Point to the controller and the data-ng-init is needed to load data in when page loads-->
<div ng-controller="MainController" data-ng-init="getAllActive()">
<!-- Using Bootstrap Panel and Collapse to create the panels -->
<div class="panel-group" id="accordion">
<div class="panel panel-{{num.u_type}}" ng-repeat="num in numbers | orderBy: '-order'">
<div class="panel-heading" style="color:black">
<h4 class="panel-title">
<a data-toggle="collapse" data-parent="#accordion" href="#{{num.number}}">
<u>{{num.short_description}}</u></a>
</h4>
<p><b>Updated:</b> {{" " + num.sys_updated_on}}</p>
<p><b>Created:</b> {{" " + num.sys_created_on}}</p>
</div>
<div id="{{num.number}}" class="panel-collapse collapse">
<div class="panel-body" style='padding:10px;'><pre>{{num.description}}</pre></div>
</div>
</div>
</div>
</div>
</div>
<!-- Here starts the code for the history tab -->
<div id="history" class="tab-pane fade">
<div ng-controller="MainController" data-ng-init="getAllHistory()">
<div class="panel-group" id="accordion2" ng-repeat="num in numbers | orderBy: '-sys_updated_on'">
<div class="panel panel-{{num.u_type}}">
<div class="panel-heading" style="color:black">
<h4 id="{{num.u_type}}" class="panel-title">
<a data-toggle="collapse" data-parent="#accordion2" href="#{{num.number}}">
<u>{{num.short_description}} </u></a>
</h4>
<p><b>Uppdaterat:</b> {{" " + num.sys_updated_on}}</p>
<p><b>Skapat:</b> {{" " + num.sys_created_on}}</p>
</div>
<div id="{{num.number}}" class="panel-collapse collapse">
<div class="panel-body" style='padding:10px;'><pre>{{num.description}}</pre> </div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</j:jelly>
So now your are done. That is about it. Remember that if you want to have a public UI Page, you need to put the pagename (showServiceStatus in this example) in sys_public as a new record. Like this:
Now this still is pretty basic. I have done a Part 3 now to trim it few notches and give it a better look:
Part 3 - Giving the service status a nicer look.
Part 3 - Giving the service status a nicer look.
I hope this has helped you into the world of angular and ServiceNow.
Inga kommentarer:
Skicka en kommentar