Ask the Expert: Understanding Angular Providers in Service Portal

Lisa Latour
Administrator
Administrator

This session will explore why, when, and how to use Angular Providers in Service Portal. We’ll take a look at the anatomy of services and directives, analyze some out-of-box examples, and build a few from scratch.

FEATURED SPEAKER

Jeff Pierce is an eight year (2010) veteran of building portals and custom applications on the ServiceNow platform. He has a passion for user experience, front-end development, and agile methodology. He is the Portal Practice Manager at Cerna Solutions, and currently resides in Honolulu where he plays with ukuleles, clay, and bonsai trees.

Join us HERE to watch the on demand video and post your questions  below - No Registration Required.

And Please Let our Experts know how they've helped! Comment Below!

Like, Share, Mark Helpful.   Find More Events on the Community!

 
originally recorded - Tuesday May 1, 2018

1 ACCEPTED SOLUTION

jpierce_cerna
Tera Contributor

For those looking for the scripts used in this session, here they are:

Free Angular Provider Templates

Aloha,
Jeff

View solution in original post

17 REPLIES 17

Hi Jeff,

 

Thank you for putting this together. Unfortunately, the link to the templates isn't working.

Koji Yanase
Tera Contributor

I'm looking for a way that portal users are able to sort the widgets by themselves so that they have their own personalized portal pages. Do you have any hints?

I mean it's like Windows 10.

find_real_file.png

Thanks.

dale_
Tera Guru

Hi all,

The link everyone's chasing has been archived here: http://web.archive.org/web/20200810074117/https://www.cernasolutions.com/post/free-angular-provider-...

Pasting the contents below:

SERVICE

A service can be used to share scripts and data across the client side of widgets. Multiple widgets can all create references to objects initialized by the service, therefore many widgets can share the same data and functions.

 

For your copy/paste pleasure:

//Inject whatever services you want to use
function($location) {

  //Initialize some properties if you like
  var myData = {};
 
  return {
    //These functions, can be accessed by any widget that injects this service into its controller script

    getMyData: function() {
      return myData;
    },

    someFunction: function(input) {
      //Do some stuff. Really go crazy man.
      var someStuff = !input;
      $location.get('stuff', someStuff);

      return someStuff;
    }
  };

}

 

WIDGET CONTROLLER

To include the above service in a widget, simply inject it into the controller function by name. Then remember to add this service to the widget’s Angular Providers related list.

 

See how I make use of the service’s functions and make local references to them? Very handy.

Copypasta:

function(myMenuService) {
  var c = this;

  //Map a local property to your service
  c.myMenu = myMenuService;
  
  //Call the service's functions to reference objects and do stuff
  c.myData = c.myMenu.getMyData();
  c.someValue = c.myMenu.someFunction(someInput);
}

 

DIRECTIVE

Now say that you wanted to enhance your widget’s HTML with powerful mini templates. This example directive will create an HTML element with a link that calls a function provided by the directive’s own link function.

Just like the service, be sure to add this to your widget’s Angular Providers related list.

 

C/P:

function myMenuItem() {
  return {
    restrict: 'E',
      /*
      controls how you intend to reference (call) the directive
      A: by attribute: <div your-directive></div>
      E: by element name: <your-directive></your-directive>
      C: by class name: <div class="your-directive"></div>
      M: by comment <!-- directive: your-directive -->
      */
    scope: true,
      /*
      false: Use the parent's scope. Making changes to the scope in the directive will update the scope of the widget.
      true: Clone the parent's scope. The directive gets a copy of the parent's scope, but making changes to the scope in the directive will not be reflected in the parent's scope.
      {}: By providing an object you isolate the scope, meaning this directive has it's own empty scope, not related to the parent's scope. You can define the directive's scope via attribute:
        Example - scope: { =someProperty }
          In this case, someProperty gets passed through to this directive via the directive's attribute:
          <my-menu-item some-property="someValue"></my-menu-item>
        There are many ways to do this. Google it for more examples and info.
      */
    replace: true,
      /*
      true: the entire HTML element that references this directive will be replaced by this template
      */
    link: function(scope) {
      /*
      Provide your own client controller for this directive.
      Inject the parent's scope and get access to everything the widget does.
      */
      scope.toggleSomething = function(item) {
        scope.server.get({'action': 'toggle', 'item': item}).then(function(response){
          item.property = !item.property; 
        });
      };
    },
    template: '<div>' +
      '<a href="javascript&colon;void(0)" ng-click="toggleSomething(item)">{{item.label}}</a>' +
    '</div>',
      /*
      Write your HTML in string form.
      Be sure to use \ to escape apostrophes inside your strings.
      Directives can have only one parent HTML element.
      */
    templateURL: 'someTemplateURL.html'
      /*
      Or provide a template in the sp_ng_template table. That's much easier than writing the HTML in script form, but remember to attach them to your widget via the Templates related list.
      */
 };
}


This directive is used in your widget’s HTML just like this:

<my-menu-item ng-repeat="item in c.Items"></my-menu-item>


FILTER MODULE

I also showed you an example of how to use a filter module. The filter I showed would remove duplicate entries from an array when using ng-repeat.

 

Copypasta:

(function() {
  angular.module('myFilter', []).filter('myUniqueFilter', function() {
    //This filter will remove any duplicates from the input array based on the provided filterBy property
     // *input* is the array of objects being iterated.
     // *filterBy* is the name of the property you want to filter by

    //Use this in your HTML template like this:
     //<div ng-repeat=" item in c.Items | uniqueFilter : 'label' " >{{ item.label }}</div>
    
    //This is a UI Script included in the portal via portal dependencies

    return function(input, filterBy) {
      if(input) {
        if (filterBy) {
          var output = [];
          //start with an empty output array
          for (var i = 0; i < input.length; i++) {
            //for each item in the input array, see if there's a matching item in the output
            var unique = true;
            for (var o = 0; o < output.length; o++) {
              //Look for an existing item in the output array.
              if(output[o][filterBy] === input[i][filterBy]) {
                unique = false;
              }
            }
            if(unique) {
              //If item doesn't already exist in the output array, put it there
              output.push(input[i]);
            }
          }
          return output;
        } else {
          //If no filterBy property has been provided, return the entire input array
          return input;
        }
      } else {
        console.log('there is no input');
        input = [];
        return input;
      }
    };
  });
})();

To use this, first add this script as a dependency to your widget via the Dependencies related list.

 

Now you can reference the filter in an ng-repeat expression:

<div ng-repeat=" item in c.Items | myUniqueFilter : 'label' " >{{ item.label }}</div>