Skip navigation

Developer Community

2 Posts authored by: Dylan Lindgren Employee

Apart from the basic concepts of HTML, CSS, and JavaScript, one of the most powerful things you can learn to really feel comfortable in Service Portal is AngularJS scopes.

 

There are many resources online which do a great job of explaining AngularJS scopes; there's really no better than what can be found in the official AngularJS documentation. However, there's nothing I'm aware of which is targeted at someone who's getting started in Service Portal, has the basic knowledge of creating a widget, but still feels confused about what's actually going on behind the scenes. Hopefully after reading this article some of that fog of confusion will have lifted.

 

Once you understand scopes, the amount of road blocks you'll hit when developing widgets will dramatically lower, and you'll be well on your way to becoming a Service Portal master!

 

This is my first post in what will hopefully become a series of articles helping to explain the basic concepts of Service Portal. If you have any suggestions for topics, feel free to leave a comment below, or reach out to me on twitter with my handle @dylanlindgren.

 

The DOM

 

Given that you're reading this article, it's likely that you've ventured into the developer tools of your web browser on at least a few occasions. Perhaps after a sudden fit of anxiety at the overwhelming amount of information in there, the first thing you'll have noticed is a tree-like structure of elements, within elements, within elements, and so on. This structure is a live, visual representation of the current state of something called the Document Object Model of the page you are on; for short it's called the DOM.

 

1.png

 

The description of the DOM from the Mozilla Developer Network is as follows:

The Document Object Model (DOM) [...] represents the page so that programs can change the document structure, style and content. The DOM represents the document as nodes and objects.

Shown in another way, you can really see how this parent & child relationship that each DOM element has goes in to building what you see in your browser window. Note that the "stacking" of elements shows the parent & child relationships of the elements on the page.

 

2.png

 

The key point to understand about the DOM is that it is a "tree" of elements, like in this hypothetical DOM tree:

3.png

 

Directives

 

A comprehensive definition of an directive can be found on the AngularJS documentation:

At a high level, directives are markers on a DOM element [...] that tell AngularJS [...] to attach a specified behavior to that DOM element [...], or even to transform the DOM element and its children.

To rephrase that, any element within the DOM can have a directive attached to it, and you can use the directive to give the element custom behaviour.

This element you're targeting could be anything, such as an image, a header, a list, or even a container div element (which may have many elements within it). And the custom behaviour you give to it could be an animation, to load some text into it from an external website, or even hide it from the page completely; it's up to the creator of the directive. This allows developers to build their own reusable components which do not exist natively in HTML, such as a login button that has a particular style/functionality, or a list of records.

 

So in the case of the hypothetical DOM tree I showed earlier, perhaps the elements with a green border below might have directives associated with them.

 

4.png

 

Note that the element with the blue border is the element on the page with the attribute ng-app. In AngularJS the element with this attribute is considered the root element of the application. It's only the elements below this which Angular treats as part of the application.

 

Let's just show the AngularJS-related elements of the DOM, and rotate it so it looks more like a "tree" (albeit, one that's upside down!)

 

5.png

 

Directives, a.k.a Widgets

 

You might now be asking yourself...

Why am I reading about directives? How does this relate to Service Portal?

 

Well, Service Portal widgets are directives. When you load a Service Portal page, a new AngularJS directive is created for each different widget on the page. This directive is then placed in the DOM wherever the widget was put on the page via Service Portal Designer or Page Editor. As you know from the previous section, a directive adds custom behaviour, and in the case of a widget this behaviour is to insert the HTML from the widget, and perform the actions you define within the client script and/or link function of the widget.

 

So why are there two names for the same thing? Well, there's lots of functionality that's been added to widgets to make them easier to work with, such as options, the addition of the server-side behaviour that happens in it's server script, and the passing of the data variable generated in it to the client script.

 

So in the case of widgets, the hypothetical structure of the directives from the previous section could be like so:

 

6.png

 

You'll notice above there's an "embedded" Widget B, and is not added via Page Designer. This widget is embedded in Widget A as described on the official ServiceNow Documentation. This is actually the only way that you'd end up having a widget sitting below another widget in the DOM.

 

Scopes

 

One characteristic that's just as important to a widget as it is to a directive is scopes. A scope can be thought of as a space where application functionality is contained to. By default a directive will share the scope of it's parent directive, however they can also be set to have a new, isolated scope.

In Service Portal a widget always has an isolated scope. This is something that has been set by the developers of Service Portal to ensure widgets play nicely together, and that you can have multiple instances of the same widget on a single page as they all have their own isolated space to play.

 

The scope tree

 

Not just is the DOM a tree, but scopes sit in a tree as well. You can think of the scope tree as a cut-down version of the DOM tree, with all the things that didn't create a new scope removed. As all widgets create a new scope then the scope tree in the example above would look like so:

 

7.png

 

Local scope vs Root scope

 

You'll have noticed in the above diagram that at the top of the tree is a thing called $rootScope. You can think of this as an overarching scope that is available to your whole application, from any widget/directive. It also allows you to listen for/broadcast events, but more on that in the next section.

 

Cross-scope communication

 

All widgets have isolated scopes, so how can we access data from one widget in another widget? There's two ways:

 

Events

 

Similar to the concept of events in ServiceNow, AngularJS events can be announced from any scope in your application. These events can be listened from any scope as well, however which method you use to announce the event will have an impact on which scopes will hear it.

 

There are two methods you can use to announce events:

 

  • With $broadcast(), the event will travel down in the scope tree from wherever it was announced, until it hits the bottom. The event will not be heard by sibling scopes. See more on the official AngularJS documentation.
  • Using $emit(), the event will travel up in the scope tree from wherever it was announced, until it hits the $rootScope. This event will not be heard by sibling scopes. See more on the official AngularJS documentation.

 

8.png

 

So now that we know how announce an event, how do we listen for it? For that, we can use the $on() method. When you use this method, you supply it with a function and whenever that event is heard by the scope the function will be called. See more about this on the official Angular JS documentation.

 

The $broadcast() and $on() functions are also available on $rootScope, which gives you another place to listen for/announce an event to ensure the right scopes will hear it.

 

Angular Providers

 

Another means of communication between widgets is a service or a factory, which in Service Portal are both types of Angular Providers. Services and factories in AngularJS basically offer a place where you can define functions and variables, and any scope which has access to it can use them.

 

The diagram below visualises where these sit in relation to the scopes, if for all 3 widgets in our example the Angular Provider was added to their related lists.Given the overwhelming amount of information in this article already, I'll cover in detail how one would use an Angular Provider in a future installment.

 

9.png

 

Conclusion

 

In the next installment in this series, we will look into how to put this theory into practice by making a series of widgets which communicate with each other. Specifically, how to create a widget showing a list, that reacts and updates with new information when you change a field on the out-of-box form widget.

 

If you'd like to read more about AngularJS scopes, I've put together some further reading links below:

 

Well, that's it for scopes! Feel free to leave feedback in the comments below, or reach out to me on twitter with my handle @dylanlindgren if you'd like to buy me a beer! 

With release of Geneva came a new interface. We sometimes refer to this interface internally at ServiceNow as Concourse, however you may know it better as UI16. Concourse makes use of modern web technologies to make ServiceNow look better, and be more useable than ever! At the time of writing, UI16 is the default interface for the Geneva, Helsinki, and Istanbul releases.

 

One of the many changes in the new interface is the way that banner images are retrieved in the code, and once retrieved, how they are placed in the header. Let's talk about how this works, and some tips which will make working with UI16 banner images seem like a walk in the park!

ui16 banner image.png

 

Banner images in UI15 vs. Banner images in UI16

The UI16 banner image is configured in the same way that UI15 was - via system properties, and values in the user’s Company records. However, by default the UI15 header has a light background, and the UI16 header has a dark background. This means we can’t use the same logo for these interfaces as it won’t look right. This has lead to the addition of a new field on the Company record, as well as a new system property for storing a logo which will look good on a dark background. I’ve highlighted the old and new settings in the tables below:

 

Company [core_company] fieldDescription
banner_imageUI15 banner image
banner_image_light (NEW)UI16 banner image

 

System Property
Description
glide.banner.image.titleBanner mouse-over text
glide.banner.image.url_targetTarget frame used when clicking the banner image
glide.banner.image.urlURL used when clicking the banner image
glide.product.descriptionPage header caption
glide.product.imageUI15 banner image (fallback)
glide.product.image.light (NEW)UI16 banner image (fallback)

 

All of the above are used to build the header, which makes use of the out of box MyCompany script include to retrieve the values. When the UI16 header loads, it will check the values defined in the banner_image_light field of the current user’s Company record. If it’s not set, and there’s no parent company, then the fallback banner image defined in the glide.product.image.light system property will be used. If the user’s company has a parent, then the banner_image_light field will be checked on that. This process of checking the parent companies will continue for a maximum of 10 parent companies, and if it’s not defined on any of them then again it will fall back to image defined in the glide.product.image.light system property.

 

UI16 banner image sizing

When UI16 was initially released the maximum height of a banner image was 20px. After some user feedback this was increased to 32px. The maximum height can almost never be reached due to the way the image is placed on the page. This will be improved in a future release, at which point you can use these measurements:

  • The maximum width of a banner image will be 230px.
  • The maximum height of a banner image will be 32px.

 

This is an aspect ratio of 115:16. It would be best to design your logo with this aspect ratio in mind.

 

It’s not an official workaround, but I’ve found that defining a global UI Script with the below value in the script field will mean that the banner image can hit the maximum height of 32px most of the time.

 

var bannerImage = top.document.getElementById('mainBannerImage16');

if (bannerImage != null) {

  bannerImage.style.height = "32px";

}

PRB709492-before-after.jpg

Make sure that if you decide to use something like the above, that you fully test it, and that you remove it once you upgrade to a release which contains a fix for PRB709492.

 

Viewing your banner images on Retina Displays

The above maximum width and height is true when dealing with regular displays, but what about high pixel density displays like the Retina displays found in most Apple computers and iOS devices?

 

Retina displays have a pixel density 4 times that of a regular display. This seems like a simple thing to implement, but in reality it actually brings up a lot of issues, especially with the parts of the page which are bitmap-based (e.g. images). If you have a screen which has 4 times the pixel density, there are two things that may happen as a result.

  1. Images on a web page will be very small on the screen. This will happen if you just display the images pixel-for-pixel on the screen.
  2. Alternatively, they will be the same size as they were, but will be blurry (as they are “scaled-up”) and won't make use of the benefits of a Retina display.

 

Retina displays work around the above two issues so that if you were to specify an image to be 10px high and 10px tall, on a regular display it will show as exactly that, however on a Retina display it will actually be 20px high and 20px tall.

 

To show how this impacts the UI16 banner image, I took a photo of each of my screens and zoomed in on the “now” part of the ServiceNow logo:

banner-image-ui-16.jpg

You can really see the difference here between the amount of pixels used to display images on a Retina display, and that are used on a non-Retina display. Due to the way that web page measurements and images scale on Retina displays, the maximum size of the banner image on a Retina display are:

  • The maximum width of a banner image is 460px.
  • The maximum height of a banner image is 64px.

 

These measurements are double that of a non-Retina display. Most importantly, it maintains the same aspect ratio of 115:16. This means that the image will appear the same size to users regardless of whether they're using a Retina display or not.

 

3 tips to banner image sizing in UI16

We can take this information and create a few simple points to keep in mind when setting your UI16 banner image:

  • Keep the aspect ratio of your banner image to 115:16 to ensure you use the entire space available to you
  • Ensure your banner image is designed for Retina displays. If it’s viewed on a non-Retina display, it will be scaled down and will not lose quality.
  • Consider implementing my unofficial workaround, ensuring you remove the workaround once you upgrade to a release with the fix included.

 

Feel free to contact me on Twitter with my handle @dylanlindgren or post reply in the comments below.

 

 

I repeat, if you use my workaround, please remove it once upgraded or it can interfere with the potential fix.

Filter Blog

By date: By tag: