Archive | Angular RSS for this section

Initializing Global Variables and Synchronous AJAX Calls in an Angular XPages Application

In a recent project (an XPages application with an Angular front end), I had a need to (a) run a script to determine user access and (b) store that value globally. In this post, I’ll show one way to achieve this with a synchronous AJAX call and a module run block.

Use Case

In this application, the front end pages need to know whether the user has Admin rights in the application in order to determine whether to display advanced options such as configuration links.

The security check needs to be run before the page is loaded and it needs to be stored globally so that the security check does not have to run constantly as many page elements are loaded.

Initial Implementation

I created a custom REST service that checks the user’s roles and returns a boolean value for whether the user is an Admin in the application. I called it via AJAX and stored the value in the $rootScope.

I then set up the logic from each admin-controlled feature call a function to check whether the value existed in $rootScope. If not, the function would make the AJAX call to look up the value and store it.

The code to call the REST service looks like this:

$http.get('rest.xsp/isUserAdmin').success(function (data) {
  $rootScope.isAdmin = (data == 'true') ? true : false;
})

The logic seemed to be set up properly. However, it didn’t initially work as I expected.

The Problem

Watching the Net panel in the browser tools made it clear that the AJAX call was being made many times as each page loaded.

The AJAX calls were fired off so rapidly that numerous calls were made before any of them had a return value available in the $rootScope.

It turns out that Angular’s AJAX calls are asynchronous by design. For the most part, this is great because it keeps the application more responsive. However, in this case, I needed the security check to complete before loading the rest of the page.

The Solution

Ultimately, I solved the problem by making two changes.

  1. Moved the role-checking code to a module run block so it would run before page load
  2. Replaced the Angular AJAX call with a standard jQuery AJAX call so I could add a parameter to force the call to be synchronous.

As the application loads, it will call the REST service and wait for the response. When done, it will store the value in the $rootScope.

Here is a link to the Angular.js module documentation.

Run blocks are the closest thing in Angular to the main method. A run block is the code which needs to run to kickstart the application. It is executed after all of the service have been configured and the injector has been created.

A run block generally goes in the app.js file rather than in a controller. Just add a run() method to the application and include the required directive and code.

This code sets up a function in the run block that includes a synchronous jQuery AJAX call to an XPages REST service, and stores the return value in the $rootScope for global access in the application. This runs immediately and the global variable is initialized and available when the rest of the page loads.

// Make the user access check available globally
PSApps.run(['$rootScope',                    
  function($rootScope) {
	 
      // Use jQuery for a synchronous http request -- angular hard-codes all requests to be asynchronous
      $.ajax('rest.xsp/isUserAdmin', {async: false})
        .success(function (data) {
          $rootScope.isAdmin = (data == 'true') ? true : false;
          console.log('is user admin? ' + data);
      });

  }
]);

Then, all places that need to check the value in order to determine whether to display an element can just reference the global property.

ng-show="$rootScope.isAdmin"
Advertisements

Formatting Select2 Options in Angular

One of the great features of Select2 is that you can format the choices beyond just a list of text values. In this post, I’ll show how you can format the display of values in a Select2 in Angular.

Before we dig in…

These examples were adapted from an XPages application that uses Angular, but there’s nothing related to XPages in the code shown here. (In reality, you’d likely call an XPages REST service to dynamically populate the list of choices, but it’s not necessary in these examples.)

If you’ve used a Select2 in any other context, then the solution here may not be surprising. If you haven’t, then you’ll see how Select2 choices can be formatted.

The examples in this post use the ui-select2 for Angular. (There’s a note at the top of the page that says this control has been deprecated in favor of a newer version.) The code needs to be added to the application, but that is outside of the scope of this post.

Getting up to Speed on Angular

If you don’t have any background in Angular, the angularjs.org tutorial is a great place to start.

Also check out Marky Roden’s great series on using Angular in XPages.

Basic Select2

These examples will all create a Select2 control in Angular that will display a list of fruit and allow the user to choose one.

Here is the basic Select2:

Angular Select2 1a

Once a value is selected, it is shown in the field and separately below the Select2.

Angular Select2 1b

The basic HTML for the template is as follows:

<select ui-select2="select2Config" ng-model="fruit"
        data-placeholder="Select a fruit..." style="min-width:150px">
    <option value=""></option>
    <option ng-repeat="option in options" value="{{option}}">{{option}}</option>
</select>

<br><br>
Selected Value: {{fruit}}

Lines 1-2 start the select tag.

  • The ui-select2 attribute references the configuration object in the Angular controller that sets properties for the control.
  • The ng-model attribute defines that the selected value will be bound to a “field” (if you will) named fruit in the model.
  • The data-placeholder attribute sets the placeholder text when no value has been selected.
  • The style attribute sets a minimum width for the control. (This is not necessary)

Line 3 includes an empty value at the start of the list.

Line 4 uses an ng-repeat directive to build the list of options to choose from. The options list is set in the controller (code shown below).

Line 8 is simply displays the selected value. It is not necessary, but it shows off the two-way binding feature of Angular.

The controller used with the template on this page is as follows:

//Select2 controller - basic
MyAppControllers.controller('select2Ctrl_basic', ['$scope', '$http', '$routeParams',
  function ($scope, $http, $routeParams) {
    $scope.options = ['apple', 'banana', 'grape', 'lime', 'orange'];
  }
]);

That’s it! All I did was define the controller and define an options property in the scope and set it to an array. The template reads the array and generates an option for each value in the array.

Text Formatting

In this example, I’ll show how to change the color of each option based on the value. We’ll use the same HTML template for the Select2.

Angular Select2 2

Here’s the controller for this example:

MyAppControllers.controller('select2Ctrl_formatted', ['$scope', '$http', '$routeParams',
  function ($scope, $http, $routeParams) {
    $scope.options = ['apple', 'banana', 'grape', 'lime', 'orange'];
    
    // Format the control to display a span with the text changed to the color of the fruit
    $scope.format = function (data) {
      var fontColor = 'black';
        	
      switch (data.id) {
        case 'apple':
          fontColor = 'DarkRed';
          break;
        case 'banana':
          fontColor = 'Gold';
          break;
        case 'grape':
          fontColor = 'Purple';
          break;
        case 'lime':
          fontColor = 'LimeGreen';
          break;
        case 'orange':
          fontColor = 'Orange';
          break;
      }
        	
      return  "<span style='color:" + fontColor + "'>" + data.text + "</span>";
    };

    $scope.select2Config = {
      formatResult: $scope.format,
      formatSelection: $scope.format
    };

}]);

In this controller, lines 6-28 create a function that is used to format the options in the list. The function accepts a data object that is the current option in the select2. (The function will be called once for each option as the list is built.) The data object contains a text property that is the actual display value for that option.

Lines 9-25 define a switch statement that sets the fontColor variable based on the fruit.

Line 27 is the key — it returns a span that includes a style attribute to set the font color and display the option.

Lines 30-33 define the configuration object used for the Select2. (This is specified in the ui-select2 attribute of the HTML template.) It tells the control to format each selection and the result using the format() function defined in the controller.

The difference between formatSelection and formatResult

The formatSelection function formats the values in the drop-down list. The formatResult function formats the selected value, shown at the top of the list when the rest of the list is dropped-down. (The example below shows ‘orange’ selected.)

Angular Select2 3

You can use different formatting functions, but it seems common to use the same formatting function for both.

Adding an Image

In this example, I’ll show how to display an image next to each option, based on the value. We’ll use the same HTML template for the Select2.

Angular Select2 4

Here’s the controller for this example:

MyAppControllers.controller('select2Ctrl_image', ['$scope', '$http', '$routeParams',
  function ($scope, $http, $routeParams) {

    $scope.options = ['apple', 'banana', 'grape', 'lime', 'orange'];

    // Format the control to display an image corresponding with the name of the fruit
    $scope.format = function (data) {
      	
      var url = '';
        	
      switch (data.text) {
        case 'apple':
          url = 'apple.png';
          break;
        case 'banana':
          url = 'banana.png';
          break;
        case 'grape':
          url = 'grapes.gif';
          break;
        case 'lime':
          url = 'lime.png';
          break;
        case 'orange':
          url = 'orange.png';
          break;
      }
        	
      return  "<img src='" + url +"' />&nbsp;&nbsp;" + data.text;
    };           
        
    $scope.select2Config = {
      formatResult: $scope.format,
      formatSelection: $scope.format
    };

}]);

This is very similar to the last example, with the exception that it determines the url and returns an img tag to display next to the text value. This adds a very nice touch to the UI of the control. (Note: The images referenced in this example are all icons that I added to the NSF as image resources.)