Archive | jQuery 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"

Set Focus on a Field when Showing a Bootstrap 3 Modal

While testing an application that I’m working on, I noticed that the focus is not set on a field within a Bootstrap modal when it’s shown. This is annoying to the user because it requires to tab a number of times (since the focus stays on the button clicked to show the modal) or click into a field in order to start typing. In this post, I’ll show how to force the focus to be set on a specific field when a modal is shown.

Event Delegation

One simple approach is to use jQuery event delegation (here’s a great explanation from Mark Roden). An event handler can be added to the body tag to fire whenever a modal is shown. Within that handler, we can set the focus on a specific field.

$('body').on('shown.bs.modal', '.modal', function () {
  $('[id$=myField]').focus();
})

The first line sets up the event delegation by adding a listener to the body of the page for the event of showing a modal (shown.bs.modal). The second line locates the field named myField and sets focus on it.

The selector in the second line may look a bit strange, but it’s effect is to find the element where the id attribute ends with myField. This is because XPages adds a number of colon-separated prefixes to each element, depending on it’s depth in the page structure. In order to not worry about all of that, the code looks for the element that ends with the name that I assigned to the field.

Here are two other options that you have for achieving the same effect:

  1. Add a unique class name to the field and use $(‘.yourClassName’) as the selector
  2. Use Mark Roden’s x$() function to select an element

Modals and Partial Refreshes

As if there haven’t already been enough (too many?) references to Marky, take a look at this post if you have a modal that needs to perform partial refreshes.

Ultimately, the code I’m actually using in my application looks like this:

$('body').on('shown.bs.modal', '.modal', function () {
  $('FORM').append($('.modal'));
  $('[id$=myField]').focus();
})

Multiple Modals

This method works fine on a page that only has one modal. If you have multiple modals, you’d need to adjust the logic in order to set the field focus as desired. One more generic option would be to change the jQuery selector to just find the first input field on the modal.

Getting Awesome Category Icons in Data Views – A jQuery Variation

In a Twitter exchange with Paul Withers regarding his great post on replacing category expand/collapse icons in a Data View with Font Awesome icons, he mentioned that it would be interesting to see a jQuery alternative. Since I’m working on improving my proficiency with jQuery, I thought it would be an interesting challenge. In this post, I’ll show how I implemented it and describe the main challenge that I faced.

This code will use jQuery to replace the expand/collapse icons with related font awesome icons.

As in Paul’s post, this code depends on the category columns having a class of catColumn (although it can be done without it).

The expand and collapse icons are within an <h4> tag and an <a> link. The image source name will be collapse.gif or expand.gif, so this code reads the source to determine which type of image to replace. Since Font Awesome uses <i> tags, it adds the new icon tag and then removes the <img> tag.

This code has been placed in a client-side JavaScript library so I can call it from two different places.

Note: The application I’m testing on has Font Awesome v3, so the icon tags look different than the one in Paul’s post, where he’s using version 4.

// Replace the standard Data View expand/collapse icons with font-awesome icons (v3) using jQuery
function replaceCategoryIcons() {
  $('.catColumn a img').each(function(){
    var src=$(this).attr('src');
    if (src.indexOf('collapse') > -1) {
      $(this).after('<i class="icon-collapse"></i>&nbsp');
    } else {
      $(this).after('<i class="icon-expand"></i>&nbsp');
    }
    $(this).remove();
  });
}

This code was pretty straightforward to create; the real trick is firing it as the data view is refreshed when categories are expanded and collapsed. There’s no pure DOM event that I’m aware of that I can listen to with jQuery in order to re-execute after each partial refresh. jQuery cannot subscribe to a stream that’s published by dojo, so I can’t use code similar to Paul’s post.

I tried to use event delegation (check out Marky’s great explanation here) on the click event of the images (since that’s what triggers the update). This kind of worked, but the problem was that it replaced the icons and then, when the page refreshed, the old icons were right back in place.

So, it all came back to Tommy Valand’s great post about hijacking partial refresh events to solve this problem.

I trimmed down original code and updated the onComplete event to trigger my jQuery function to replace the icons (line 18).

function hijackPartialRefresh() {
  // Hijack the partial refresh
  XSP._inheritedPartialRefresh = XSP._partialRefresh;
  XSP._partialRefresh = function( method, form, refreshId, options ){
    // Publish init
    dojo.publish( 'partialrefresh-init', [ method, form, refreshId, options ]);
    this._inheritedPartialRefresh( method, form, refreshId, options );
  }

  // Publish start, complete and error states
   dojo.subscribe( 'partialrefresh-init', function( method, form, refreshId, options ){
    if( options ){ // Store original event handlers
      var eventOnComplete = options.onComplete;
    }

    options = options || {};
    options.onComplete = function(){
      replaceCategoryIcons();
      if( eventOnComplete ){
        if( typeof eventOnComplete === 'string' ){
          eval( eventOnComplete );
        } else {
          eventOnComplete();
        }
      }
    };
  });
}

Now, to run the code both when the page loads and then again on each partial refresh, I added this code to the onClientLoad event of the page:

// Replace the category icons with font-awesome icons
replaceCategoryIcons();

// Set up ongoing replacement of icons
hijackPartialRefresh();

Now, it replaces the default icons…

DataViewExpandCollapseIcons_Before

… with Font Awesome icons…

DataViewExpandCollapseIcons_After