<!-- CERN title -->
<!-- Styles are global for the slide deck -->
<style>
mark {background: #f7d40f;}
li strong {color: white;}
code {color: hotpink;}
.subitem {color: DarkSeaGreen;}
.titlemoji {font-size: 0.75em !important;}
.titlemoji-sm {font-size: 0.55em !important;}
.danger-text {color:coral;font-weight:bold!important;}
.side-note {color:DarkSeaGreen; font-style:italic!important;font-size:0.50em;}
.white {color: white;}
.alert-warning {
color: #8a6d3b;
background-color: Moccasin;
border-color: #faebcc;
border-radius: 15px;
}
</style>
---
# ⚡️
## Service Portal
### *Development Workshop*
---
#### *Jorge García Cuervo*
##### *IT-CF-SM*
##### <span class="titlemoji">📧</span> *jorge.garcia@cern.ch*
---
# AngularJS

---
# AngularJS
#### Key concepts
* AngularJS is a **Model-View-Controller** framework
* Higher level of **abstraction**
* Decouples DOM manipulation from app logic
* improve testability
----
# AngularJS
#### MVC Interaction

---
# AngularJS
#### Key concepts
* **Template**
* HTML with additional AngularJS markup
* **View**
* What the user sees (the `DOM`)
* **Model**
* Values stored in variables in the `Scope`
* **Controller**
* <span class="white">++Business logic++</span> behind views
* <span class="white">++Expose++</span> variables and functionality
----
# AngularJS
#### Key concepts
* **Scope**
* <span class="white">++Binding++</span> between `View` and `Controller` _(context)_
* **Expressions**
* Access variables and functions located in the Scope
* **NOT** JS, but similar
```javascript
{{ expression | filter }}
```
----
# AngularJS
#### Key concepts
* **Services**
* <span class="white">++Reusable++</span> business logic independent of views
* **Filter**
* Format value of an expression for display to the user
---
# AngularJS
#### Data Binding
* **Automatic sync** between Model and View
* ==Two↔️way== Data Binding
* <span class="white">++Change in the Model -++</span> <span class="side-note">reflected on the view</span>
* _An exposed variable value changes in your `Controller`_
* _It changes automatically in the HTML the user sees_
* <span class="white">++Change in the View -++</span> <span class="side-note">reflected in the model</span>
* _User modifies/selects something in the `View`_
* _If bound with something in the Controller, it will change as well_
* ++**Simplifies coding**++
----
# AngularJS
#### Data Binding

---
# AngularJS
#### Directives
* OOB & **Custom** directives
* Types
* **Element** _(`E`)_, **Attribute** _(`A`)_, Class Names _( C)_, Comments _(M)_
```html
<my-dir></my-dir>
<span my-dir="exp"></span>
<!-- directive: my-dir exp -->
<span class="my-dir: exp;"></span>
```
:::warning
:warning::eyes: **Normalization:** Directive names in *camelCase* are specified in ++lower case with dashes++.
E.g `ngModel` <-> `ng-model`
:::
---
# AngularJS
#### Directives - OOB
* AngularJS provides a set of built-in directives
* They begin with the <span class="white">++prefix++</span> **`ng-`**
* `ng-click`, `ng-if`, `ng-repeat`, `ng-hide`, `ng-show`
* Consider beginning each directive <span class="white">++on a separate line++</span>
* _Improves readability_
```html=
<div>
Enter your name: <input type="text"
ng-model="c.data.sometext"
ng-change="c.display()"/>
</div>
```
----
# AngularJS
#### Directives - ngModel
* **Binds** a form control (e.g. input, select…) on the View **to a property** on the Model
* Input from an `ng-model` (when user changes the value) <span class="white">++overrides the value in the Controller++</span>
----
# AngularJS
#### Directives - ngModel example


<!-- .element: class="fragment" data-fragment-index="1" -->
----
# AngularJS
#### Directives - ngClick
* Specify custom behaviour when an element is selected
* Parameters can be passed
----
# AngularJS
#### Directives - ngClick example


<!-- .element: class="fragment" data-fragment-index="1" -->
----
# AngularJS
#### Directives - ngIf
* Removes the HTML element completely from the `DOM`


<!-- .element: class="fragment" data-fragment-index="1" -->
---
# AngularJS
#### Directives - Custom
* Create your own directives
* <span class="white">prefix</span> them to avoid future collisions
* `cernTable` (`cern-table`)
* <span class="white">++Anatomy++</span>
* Controller
* Template
* `$scope`
----
# AngularJS
#### Directives - Custom
* Directives are defined returning a JS object
* that will be interpreted ty the `$compile` service
```javascript
return {
controllerAs: 'c',
controller: function(){var c = this;},
//controller: myController,
template: '<div> Hello, I am directive :)</div>',
//templateUrl: 'myTemplate.html'
}
```
----
# AngularJS
#### Directives - Custom
###### Properties when defining a Directive
* `restrict`
* By default a directive is only recognized as <span class="white">_Element_</span>
* `scope` :star:
* <span class="white">++Isolate and initialize++</span> the directive's `$scope`
* Pass parameters into the directive
* One way (`@`) / two way (`=`) binding and functions (`&`)
* <span class="side-note">pass parameters from its parent</span>
* `bindToController`
* Bind scoped inherited properties to the Controller instead of the `$scope` object. <span style="font-size:0.75em">[_more info_ :link:](https://ultimatecourses.com/blog/no-scope-soup-bind-to-controller-angularjs)</span>
----
# AngularJS
#### Directives - Custom
```javascript
return {
restrict: 'EA',
controllerAs: 'c',
controller: myController,
scope: {
myParam: '=',
myReadOnly: '=myTitle',
submit: '&'
},
bindToController: true,
templateUrl: 'myTemplate.html'
}
```
```html
<div>
<myDirective my-param="hello" my-title="world" submit="c.updateRecord()" />
</div>
```
---
# AngularJS
#### Controllers
* Initial state of `$scope` and add behaviour
* Contain **business logic** for a single view
* Don't do too much. No heavy lifting
* Dependency injection
* **DON'T**
* ++Manipulate the DOM++ (:no_entry: `jQuery`)
* <span class="side-note">Link Function in `Widgets` for this</span>
* Format Input (AngularJS form controls instead)
* Filter Output (AngularJS filters)
* Share code or state (Services)
----
# AngularJS
#### ControllerAs Syntax
* Hides complexity of `$scope`
* Best practice
* **DO NOT** assign properties to `$scope`
* ++Assign them to the controller++ instead
* _which is an object **inside** the scope_
```javascript
function() {
/* ControllerAs syntax */
var c = this;
c.data.msg1 = "Hello";
c.data.msg2 = "World";
}
```
----
# AngularJS
#### Controllers
```javascript
function cernCustomTableCtrl(cTableService, $rootScope, $window) {
// "Controller as" syntax
var c = this;
// Exposed variables
c.tableOptions;
c.table;
c.showAddNewRow = false;
// Exposed methods - available from the view
c.editCell = editCell;
c.createRow = createRow;
c.deleteRow = deleteRow;
c.saveRow = saveRow;
c.saveEdit = saveEdit;
// CONTROLLER IMPLEMENTATION
_init();
// ...
// FUNCTIONS DEFINITION
function _init() = {...}
function editCell() = {...}
// ...
```
---
# AngularJS
#### Scope
* Context for expressions and functions to be evaluated
* Contains the Application Model
* Glue between controller and view
* Scope Inheritance and Hierarchy
* `$rootScope`
* `$parentScope`
* `$scope`
* **Isolated Scopes** :star:
* Initialized "empty" to isolate directives
* Components outside cannot change the Model
----
# AngularJS
#### Scope
* <span class="white">++Watch Expressions++</span>
* `$watch`
* <span class="white">++Propagate Events++</span>
* `$emit` to parent (or root) scope
* `$broadcast` to children scopes
* _<span class="white">Communication</span> amongst directives_
---
# AngularJS
#### Services
> A <span class="white">**Service**</span> is an object containing a set of functions to perform a task.
* Organize and **share** code across your application
* Share data between directives
* MUST be ++injected++ into the `Controller`
* [OOB Services](https://docs.angularjs.org/api/ng/service)
* `$` prefix
* `$http`, `$location`,...
----
# AngularJS
#### Services
Injecting a Service in the Controller
```javascript
function($http) {
/* widget controller */
var c = this;
}
```
---
# Service Portal

---
# Service Portal Anatomy
* **Pages** `[sp_page]`
* **Widgets** `[sp_widget]`
* **Widget Instances** `[sp_widget_instance]`
* *Specific, parametrized*
----
# Service Portal Anatomy
#### Pages, Widgets and Widget Instances

----
# Service Portal Anatomy
#### Pages, Widgets and Widget Instances

---
# Editing the Portal
* Service Portal Configuration
* [/sp_config :link:](https://cerndev.service-now.com/sp_config)
----
# Editing the Portal
#### *SP Config*

---
# Editing the Portal
#### Pages
* **Alternatives**
* **Designer** 🌟
* *preferred way of editing a page*
* **Page editor**
* *useful to see ++dependencies++ and ++structure++*
* Always clone our CERN page template to start
* breadcrumbs
* search
* footer
---
# Editing the Portal
#### Widgets
---
# Widgets
#### What is a Widget?
* Building blocks of Service Portal Pages
* Performs one task
* and does it good :v:
* :recycle: Reusable components
* 🛠 Configurable
----
# Widgets
#### What is **REALLY** a Widget?
* AngularJS 'Directive'
* Teach HTML some new tricks :dog:
* Extend HTML vocabulary
* Anatomy
* HTML `[view]`
* CSS-SCSS
* Client Script (controller)
* Client & Server side execution
* No need for `GlideAjax` magic :eyes:
* [OOB Widgets available :link:](https://docs.servicenow.com/bundle/london-servicenow-platform/page/build/service-portal/concept/widget-showcase.html)
---
# Widgets
#### Lifecycle
1. **Server Script** executes first
* <span class="white">++Available:++</span> `options`
* <span class="white">++Initializes:++</span> `data` and `input` <span class="side-note">(`undefined` in first execution)</span>
2. **Client Script** executes, receiving the `data` object
3. The Widget is **rendered** in the Portal
4. **User interacts** with it and ++generates input++
5. The Widget **sends** the `input` object ++back to the server++ with `server.update()`
6. The ++Server Script is executed again++ and **updates** the `data` sent to the client
7. …
----
# Widgets
#### Lifecycle

---
# Widgets
#### Server Script
* Populate the `data` object
* `GlideRecord`, `GlideAggregate`
* `GlideSPScriptable API` <span class="side-note">(Portal Context)</span>
* `$sp.getRecord()` <span class="side-note">gets a GR for the current record</span>
* `$sp.canReadRecord(gr)` <span class="side-note">user can read?</span>
* Syntax **data.**_variable_name_
* `data` is JSON serialized and sent to the client
----
# Widgets
#### Server Script
```javascript
(function() {
// `input` is not null if coming from the CLIENT
if (!gs.nil(input.keywords))
data.items = getCatalogItems(input.keywords);
function getCatalogItems(keywords) {
var sc = new GlideRecord('sc_cat_item');
sc.addActiveQuery();
/*
...
*/
sc.setLimit(100);
sc.orderByDesc("ir_query_score");
sc.query();
var results = [];
while (sc.next()) {
if (!$sp.canReadRecord(sc))
continue;
var item = {};
$sp.getRecordDisplayValues(item, sc, 'name,price,sys_id');
item.category = sc.getValue('category');
results.push(item);
}
return results;
}
})();
```
---
# Widgets
#### Client Side
* We got the `data` object from the Server
* Time to show the user something :sunglasses:
* HTML + CSS
* Client Script
* `AngularJS`
----
# Widgets
#### Client Side - AngularJS
* Framework
* Extends HTML with new attributes: ==Directives==
* Binds data to HTML with Expressions
* _“““Replaces”””_ `Jelly`
* ServiceNow uses Angular Version 1 (AngularJS)
* AngularJS ≠ Angular
* Take this into consideration when searching online for documentation
---
# Widgets
#### Service Portal Utilities
* **Client-side** APIs
* <span class="white">`spModal`</span>
* Alerts, prompts, confirmation dialogs
* complexity needed? <span class="side-note">`$uibModal`</span> directive
* <span class="white">`spUtil`</span>
* Common ServiceNow functions for the Portal
* `recordWatch()`
* Monitors a table for changes to its records
* No page refresh needed to update lists, counts, etc
* Impacts performance: use with care
---
# Widgets
#### Reuse Code
* **Angular Providers**
* Script Includes for AngularJS Code
* Different posibilities
* Services :star:
* Directives :+1:
* ~~Factories~~ :no_entry:
* <span class="white">Angular ng-templates</span> `sp_ng_template`
* Reuse HTML for Directives and Widgets
---
# Widgets
#### Debugging
* <span class="white">++Server Script++</span>
* `sp.log()`
* `gs.debug()`
* `gs.info()`
* <span class="white">++Client Script++</span>
* `console.log()`
* `spUtil`
* `debugger` instruction
* <span class="white">++HTML++</span>
* `{{ data | json }}`
---
# Service Portal
#### Not covered topics
###### :warning: Remember they exist too
* Themes
* CSS includes (`SCSS`)
* remember we can use **variables**!
* Page Route Maps
* "swap" contents of a requested page server side
* E.g: Page Route map from `page1` to `page2`
* _http://..../page1 -> ServiceNow returns page2_
---
# Congratulations! 🐱👤
`Now you're an AngularJS and Service Portal ninja`
---
# Any questions? 🤔🤷♂️
---
{"subheadline":"workshop","title":"Service Portal Development Workshop","tags":"presentation, AngularJS, ServiceNow, Workshop","type":"slide","slideOptions":{"transition":"slide","theme":"cern3"}}