uPortal

Enterprise open source portal built by and for the higher education community.

This project is maintained by uPortal-Project

Strategy for employing Angular.js in portal skins and/or portlets.

Definitions

The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in RFC 2119.

Abstract

AngularJS has become a very popular framework for developing user-facing functionality. Because of the way Angular prohibits nested bootstrapping of modules, special care is required for ensuring that multiple users of Angular do not conflict, which would degrade usability, regardless of how the page skin or page fragments are combined.

This document lays out a strategy in which Portal and/or portlets can both use Angular, and may coexist without conflicting.

Practically this means that:

Skins/Portal

angular.module('foo').config(function getLazyLoaders($compileProvider,
$controllerProvider, $provide) {
  //Register controller helper
  window.up.ngApp.controller = function(name, ctrl) {
    $controllerProvider.register(name, ctrl);
      return window.up.ngApp;
  };

  //Register $provide helpers
  ['service', 'factory', 'value'].forEach(function(t) {
    window.up.ngApp[t] = function(name, thing) {
      $provide[t](name, thing);
      return window.up.ngApp;
    };
  });

  //Register directive helper
  window.up.ngApp.directive = function(name, dirFactory) {
    $compileProvider.directive(name, dirFactory);
    return window.up.ngApp;
  };

Portlets

Behavior for Angular checks

External Script Files

Boilerplate Portal Code

Inline script

<%@ page contentType="text/html" isELIgnored="false" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="portlet" uri="http://java.sun.com/portlet_2_0" %>

<portlet:defineObjects/>

<c:set var="nc"><portlet:namespace/></c:set>
<c:set var="lc" value="${fn:toLowerCase(nc)}" />
<c:set var="n" value="${fn:replace(lc, '_', '')}"/>



<script type="text/javascript">
  (function(window, $) {
    if (typeof window.Angular === 'undefined') {
      //No matter what, check Angular and load if needed.
      var Angular_SCRIPT_ID = 'Angular-uportal-script';

      var scr = document.getElementById(Angular_SCRIPT_ID);

      if (!scr) {
        scr = document.createElement('script');
        scr.type = 'text/javascript';
        scr.id = Angular_SCRIPT_ID;
        scr.async = true;
        scr.charset = 'utf-8';
        scr.src = 'https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.4/angular.js';
        document.body.appendChild(scr);
      }
      //Working inline, so call our bootstrap function.
      $(window).load(bootstrap);
    } else {
      if (window.up.ngApp) {
        register(window.up.ngApp);
      } else {
        bootstrap();
      }
    }

    function bootstrap() {
      var app = angular.module('${n}-test2', []);
      register(app);
      angular.bootstrap(document.getElementById('${n}-test2'), ['${n}-test2']);
    }

    function register(app) {
      app.controller('test2Controller', function($scope) {
        $scope.awesomeThings = ['AngularJS', 'Bower', 'Grunt', 'Yeoman', 'uPortal', 'Open Source!'];
      });
    }
  })(window, up.jQuery);
</script>

<style>
  #${n}-test2[ng-cloak] {
    display: none;
  }
</style>

<div id="${n}-test2" ng-cloak ng-controller="test2Controller">
  <h1>Awesome Things</h1>
  <ul>
    <li ng-repeat="thing in awesomeThings"> .  </li>
  </ul>
</div>

External scripts

<%@ page contentType="text/html" isELIgnored="false" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="portlet" uri="http://java.sun.com/portlet_2_0" %>

<portlet:defineObjects/>

<c:set var="nc"><portlet:namespace/></c:set>
<c:set var="lc" value="${fn:toLowerCase(nc)}" />
<c:set var="n" value="${fn:replace(lc, '_', '')}"/>

<script type="text/javascript" src="/jasig-widget-portlets/test/scripts/module.js"></script>


<script type="text/javascript">
  (function(window, $) {
    if (typeof window.Angular === 'undefined') {
      //No matter what, check Angular and load if needed.
      var Angular_SCRIPT_ID = 'Angular-uportal-script';

      var scr = document.getElementById(Angular_SCRIPT_ID);

      if (!scr) {
        scr = document.createElement('script');
        scr.type = 'text/javascript';
        scr.id = Angular_SCRIPT_ID;
        scr.async = true;
        scr.charset = 'utf-8';
        scr.src = 'https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.4/angular.js';
        document.body.appendChild(scr);
      }
    }

    $(window).load(function() {
      if ( up.ngBootstrap ) {
        //Once the needed scripts ready, bootstrap if needed.
        up.ngBootstrap.test('${n}');
      }
    });

  })(window, up.jQuery);
</script>

<style>
  #${n}-test[ng-cloak] {
    display: none;
  }
</style>

<div id="${n}-test" ng-cloak ng-controller="testController">
  <h1>Awesome Things</h1>
  <ul>
    <li ng-repeat="thing in awesomeThings"> .  </li>
  </ul>
</div>
(function(window, _) {
  'use strict';

  if (window.up.ngApp) {
    //If loaded, register right away.
    register(window.up.ngApp);
  } else {
    //Otherwise, let jsp call your bootstrapper once Angular is loaded.
    window.up = window.up || {};
    window.up.ngBootstrap = window.up.ngBootstrap || {};

    window.up.ngBootstrap.test = function(n) {
      var app = angular.module(n + '-test', []);
      register(app);

      var bootEle = document.getElementById(n + '-test');
      angular.bootstrap(bootEle, [n + '-test']);
    }
  }


  function register(app) {
    app.controller('testController', function($scope) {
      $scope.awesomeThings = ['AngularJS', 'Bower', 'Grunt', 'Yeoman', 'uPortal', 'Open Source!'];
    });
  }
})(window, up.underscore);

Administrators