Skip navigation

Developer Community

2 Posts authored by: Arthur Oliveira Employee

"Deep linking consists of using a uniform resource identifier (URI) that links to a specific location within an app rather than simply launching the app."

Wikipedia

 

 

 

 

DISCLAIMER This functionality isn't officially supported by service portal. Use at your own risk.

 

In this post we will continue from where we left off on Single page app on Service Portal  Part 1

 

Ui Router supports url history out of the box by appending hashes to the url but unfortunately it isn't possible within service portal.

 

For this demo we will implement our own workaround to accomplish the unique url functionality in our application by using get parameters (?) instead of hash (#).

 

Let's get to it!  All the functionality will live inside the client script of helloworld-uiview widget defined on the blog post part 1, lets see where we left off.

 

 

function ($state,$location) {

      if ($location.path().indexOf('editor') != -1) {

        return;

    }

    $state.go('homepage');

}

 

 

 

Let's start by loading all the parameters from the url, then removing $state.go('homepage') as we want to read from the url what state to load. Your code should look like this:

 

function ($state,$location) {

  if ($location.path().indexOf('editor') != -1) {

    return;

  }

  // returns an object with the url parameters

  var urlParams = $location.search();

 

 

  // Check if we have a variable called state from the url, if there is we load that state passing all the parameters

  if (urlParams.state) {

    $state.go(urlParams.state, urlParams);

    // If state isn't present in the parameters send user to default state

  } else {

    $state.go('homepage');

  }

}

 

 

Lets save our widget and go to /helloworld?state=detail and you should see the detail view

pasted image 0.png

 

But if you click on Go To Homepage View you are going to notice the url doesn't change. That's because we are not done yet. Let's go back to our uiview and add that functionality.

 

Next we are going to listen to a UI Router event called $stateChangeSuccess fired once the state transition is complete, every time it happens we are going to check it new state is different than old state (this can be improved to support same state with different parameters) if its different we are going to append state to parameters, in case the state wasnt specified in the url, and we manipulate  the url with angular $location service

 

 

function ($state,$location,$rootScope) {

  if ($location.path().indexOf('editor') != -1) {

    return;

  }

  // returns an object with the url parameters

  var urlParams = $location.search();

 

  // Check if we have a variable called state from the url, if there is we load that state passing all the parameters

  if (urlParams.state) {

    $state.go(urlParams.state, urlParams);

    // If state isn't present in the parameters send user to default state

  } else {

    $state.go('homepage');

  }

 

    $rootScope.$on('$stateChangeSuccess',

        function (event, toState, toParams, fromState, fromParams) {

            if (toState.name != fromState.name) {

                toParams.state = toState.name;

                $location.search(toParams);

            }

        }

    );

 

}

 

IMPORTANT: Please be careful when choosing your state parameters as they can conflict with service portal . e.g. Never use ID as SP relies on to define what page to load 

 

Save, test your portal and Voilà!

 

gif-generated.gif

 

 

NOTE: Your widget will reload every time the url changes (not 100% why other than $rootScope.$apply() being called somewhere else). There is a work around that, but this is topic for another time.

 

Thank you

 

Arthur Oliveira

"Single-Page Applications are Web apps that load a single HTML page and dynamically update that page as the user interacts with the app. "

Microsoft Magazine

 

 

 

 

For this example we will be using ui-router to manage state and what is displayed to the user but with this approach you could use any state manager.

 

Let's call our project Hello World

 

Some information about our project

Portal:

Title: Hello World

Url suffix: helloworld

Homepage: helloworld_homepage (an empty page that we creating the portal)

 

 

 

 

Step 1

First let's download the library (https://github.com/angular-ui/ui-router) and create a UI Script with its content . The minified version is recommended.

 

 

 

Step 2

Let's create a UI Script that our application will rely on. That UI Script can be used to create factories, services, filter, etc. Basically any angular 1 component.

I'll name our UI Script helloworld-uiscript. I find that appending uiscript to the name makes it easier to identify it later on when we start working with widgets as well.

 

In our UIScript I'll start creating an angular module called helloworld and adding ui.router as dependency. This is pure angular code and it should look  very familiar to angular developers

 

(function (angular) {

    angular.module('helloworld', ['ui.router'])

})(angular);

 

 

Next let's define our states. For this demo we are creating 2 states. I'll try to keep it simple naming it homepage and detail but for your application make sure your state name reflect the state of your application.

 

IMPORTANT Please note some states are already being used by service portal and it will break if you try to create a state with the same name, that's why I avoided using state1 and state2 and used homepage and detail instead

 

 

Lets add a configuration block to our helloworld application. We will also inject $stateProvider which is provided by uiRouter and define our states.

 

(function(angular) {

    angular.module('helloworld', ['ui.router'])

        .config(function($stateProvider) {

            $stateProvider

                .state('homepage', {

                })

                .state('detail', {

                })

        })

}])(angular);

 

 

Next we need to define what content (template) we will display for each state. We have couple options here.

 

Option 1:

We can define our template inline using the template property. It would look somewhat like this:

 

.state('homepage', {

template: "<h1>Hello World Title - Homepage</h1>"

})

 

This template property could become massive depending on what our state would display, so let's take a look at some other options

 

 

Option 2:

UI Pages! As alternative to option 1, we could use the property templateUrl to point each state to a UI Page. It would look like this:

 

.state('homepage', {

templateUrl: "homepage.do"

})

 

 

Option 3:

TemplateCache! We can also use templateCache to point to a template ID that we will define later on. IT would look like this

.state('homepage', {

templateUrl: "homepage"

})

 

Since option 1 and 2 is pretty straightforward I'll implement our helloworld demo using Option 3.

 

Your UI Script should look like this at the end

 

(function (angular) {

    angular.module('helloworld', ['ui.router'])

        .config(function ($stateProvider) {

 

            $stateProvider

                .state('homepage', {

                    templateUrl: "homepage"

                })

                .state('detail', {

                    templateUrl: "detail"

                })

        })

})(angular);

 

UPDATE:  SP ng-template might be a better solution for a production application but the concept is the same

 

Step 3:

Now lets create our custom widget that will wrap our application, add the proper dependencies and add it to our homepage.

 

You can create the widget using the widget editor view or servicenow form. I'll use the widget editor by going to /sp_config?id=widget_editor but we will need to switch to the form view to add the dependencies

 

For this widget I'm going to name it helloworld-uiview

 

HTML

Let's start creating our cached templates defined in the uiscript (state1 and state2) then we will add a ui-view directive provided by ui router. Your code should look like this

 

 

<script type="text/ng-template" id="homepage">

  <h1>Homepage View</h1>

  <a ui-sref="detail" class="btn btn-primary">Go To Detail View</a>

</script>

 

<script type="text/ng-template" id="detail">

  <h1>Detail View</h1>

  <a ui-sref="homepage" class="btn btn-primary">Go To Homepage View</a>

</script>

 

<ui-view></ui-view>

 

 

 

CLIENT SCRIPT

In the client script we need to define our default state AKA home state. For this we need to inject $state, provided by ui router

 

IMPORTANT: Later in the process we are going to access a page called $spd.do . This page relies on $state to work, so we need to write some work around to stop $state manipulation  for when accessing this page

 

Your code should look like this:

 

function ($state,$location) {

  if ($location.path().indexOf('editor') != -1) {

    return;

  }

  $state.go('homepage');

}

 

 

 

No Server side script, SCSS and link function are needed for this example, now let's switch to the platform view by clicking on the hamburger menu on the top right

 

 

 

Setting up Dependencies

Scroll down to Dependencies and let's create a new dependency (sp_dependency)

Name: helloworld Dependency

Include on page load: yes

Portals for pageload: helloworld

Angular module name: helloworld (defined in our ui script)

 

Save instead of submitting so we stay on the same page and we can add the files dependency

 

Now lets create 2 new JS Includes. One for our helloworld-uiscript and another for ui router. It's important that the Order field for UI router is smaller than helloworld-uiscript since our uiscript injects uirouter as dependency

 

Display Name: UI Router

Source: UI Script

UI script: ui router

 

Display Name: UI Router

Source: UI Script

UI script: helloworld uiscript

 

 

 

Step 4

The alst step is adding our helloworld-uiview to the homepage.

 

We can do this by going to /$spd.do

 

 

Final Result

 

gif-generated.gif

You can view a live demo here:

https://empaoliveira.service-now.com/helloworld

 

I hope it helped!

 

Soon I'll post a part 2 where I'll explain how to develop single page app with deep linking so you can use the history tab to go back or send a user to a specific state.

 

Thank you

Filter Blog

By date: By tag: