Multiple Angularjs Applications (Driving Portlets)

I have a use case that requires the loading of separate angular applications.

Based on several stack overflow questions and this google thread, it’s doable. However, I can’t get it to work.

  • AngularJS data-ng-controller doesn't work
  • I have an onClick angularjs function has a for loop that does not work in first click
  • textAngular - How to insert text/html into editor at the Cursor
  • How to display information inside modal from url?
  • AngularJS validation, binding, etc. not working when using jQuery plugins (eg. autoNumeric)
  • Routing error with angular.js and express
  • Looking at the documentation:

    http://docs.angularjs.org/api/angular.bootstrap

    It looks like you need to provide the element (what is the right way to get a handle on the element?), and then how to tie it back to config, controllers, etc. And how would this work with routes? IE how does collision work, ie app a and app b map /foo to /fooa.html and /foob.html respectively… or each app describes its own .otherwise?

    Thanks!

  • How to disable submit in AngularJS in following scenario?
  • AngularJS smart-table strange behavior with nested objects and st-search
  • Change AngularJS input text value using javascript
  • Binding keyboard events in AngularJS
  • ngOptions “track by” expression
  • How to remove unwanted space from Ionic popover
  • 4 Solutions collect form web for “Multiple Angularjs Applications (Driving Portlets)”

    So given the requirement that this be a service driven content the only way I can see to do this is kind of a mix between angular and standard html practices. Effectively you’ll want to take a page from the plunker book and use Iframes to contain each individual portlet.

    <!doctype html> <html lang="en">
    
    <body ng-app="plunker" ng-controller="MainCtrl">
    
    <!-- define foo -->
    
    <div>
        <ul class="menu">
            <li><a href="#" ng-click="fooRoute='#/foo1'">foo1</a></li>
            <li><a href="#" ng-click="fooRoute='#/foo2'">foo2</a></li>
        </ul>
        <iframe seamless="true" ng-src="foo.index.html{{fooRoute}}"></iframe> </div>
    
    <div>
        <ul class="menu">
            <li><a href="#" ng-click="barRoute='#/bar1'">bar1</a></li>
            <li><a href="#" ng-click="barRoute='#/bar2'">bar2</a></li>
        </ul>
        <iframe seamless="true" ng-src="bar.index.html{{barRoute}}"></iframe> </div>
    
    
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.6/angular.min.js"></script> <script src="app.js"></script> </body> </html>
    

    Then on each of these portlets you’ll want to have a completely separate application (including the loading of resources).

    <!doctype html>
    <html lang="en">
    
    <body ng-app="fooApp">
    
    <div ng-view></div>
    
    
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.6/angular.min.js"></script>
    <script>
      var app = angular.module('fooApp', ['fooApp.controllers']);
    
        // Configure the app
        app.config(['$routeProvider', function ($routeProvider) {
            $routeProvider.when('/foo1', {template: '<h1>Foo</h1><h2>foo1</h2>', controller: 'MyCtrl1'});
            $routeProvider.when('/foo2', {template: '<h1>Foo</h1><h2>foo2</h2>', controller: 'MyCtrl2'});
        }]);
    
        angular.module('fooApp.controllers', []).
                controller('MyCtrl1', [function () {
                    console.log("fooApp.MyCtrl1 invoked.");
                }])
                .controller('MyCtrl2', [function () {
                    console.log("fooApp.MyCtrl2 invoked.");
                }]);
    </script>
    </body>
    </html>
    

    This is a little less efficient for loading than utilizing a common application base but at the moment this isn’t feasible. There is talk at the angular-ui’s ui-router team about controlling independent views which may be a workable solution for you but it is currently not implemented, you can follow the discussion at https://github.com/angular-ui/ui-router/issues/84 and chime in with your need. There is also now an issue specifically for this on the ui-router issues list at https://github.com/angular-ui/ui-router/issues/160.

    Working plunker of this design: http://plnkr.co/edit/sPoK3I?p=preview

    Ok so I figured out how to do this using the angular ui-router the key comes down to the ability of the angular ui-router to transition states without effecting the URL.

    The steps to get this working

    • First instantiate each application as a stand alone application using
      a manual bootstrap to an ID’d element.
    • Attach the ui-router $stateProvider to each application to drive the internal state transitions (routes).
      • You must leave off the url key here for each defined state or you’ll reset the page by changing the url on each state transition.
    • Setup a state function in a main controller to drive state changes.

    The following is the code to get this working:

    <!DOCTYPE html>
    <html ng-app="plunker">
    
      <head>
        <meta charset="utf-8" />
        <title>AngularJS Plunker</title>
        <script>document.write('<base href="' + document.location + '" />');</script>
        <link rel="stylesheet" href="style.css" />
        <script data-require="angular.js@1.0.x" src="http://code.angularjs.org/1.0.7/angular.min.js" data-semver="1.0.7"></script>
        <script src="angular-ui-states.min.js"></script>
        <script src="app.js"></script>
      </head>
    <!-- define foo -->
    
    <div id="fooApp" ng-controller="MainCtrl">
    
        <ul class="menu">
            <li><a href="#" ng-click="state('foo1')">foo1</a></li>
            <li><a href="#" ng-click="state('foo2')">foo2</a></li>
        </ul>
        <div ui-view>
        </div>
    
    </div>
    
    <script>
        // Declare app level module which depends on filters, and services
        var app = angular.module('fooApp', ['fooApp.controllers', 'ui.state']);
    
        // Configure the app
        app.config(['$stateProvider', function ($stateProvider) {
            $stateProvider
              .state('foo1',
              {
                template: '<h1>Foo</h1><h2>foo1</h2>', 
                controller: 'MyCtrl1'
              })
              .state('foo2',
              {
                template: '<h1>Foo</h1><h2>foo2</h2>', 
                controller: 'MyCtrl2'
              });
        }]);
    
        angular.module('fooApp.controllers', [])
                .controller('MainCtrl', ['$scope', '$state', function($scope, $state){
                  $scope.state = function(name){
                    console.log('Transition to state ' + name);
                    $state.transitionTo(name);
                  }
                }])
                .controller('MyCtrl1', [function () {
                    console.log("fooApp.MyCtrl1 invoked.");
                }])
                .controller('MyCtrl2', [function () {
                    console.log("fooApp.MyCtrl2 invoked.");
                }]);
    
        // manually bootstrap
        var div = document.getElementById('fooApp');
        console.log(div);
        angular.bootstrap(div, ['fooApp']);
    
    
    </script>
    
    
    <!-- define bar -->
    
    <div id="barApp" ng-controller="MainCtrl">
    
        <ul class="menu">
            <li><a href="#" ng-click="state('bar1')">bar1</a></li>
            <li><a href="#" ng-click="state('bar2')">bar2</a></li>
        </ul>
        <div ui-view>
        </div>
    
    </div>
    
    <script>
        // Declare app level module which depends on filters, and services
        var app = angular.module('barApp', ['barApp.controllers', 'ui.state']);
    
    
        app.config(['$stateProvider', function ($stateProvider) {
            $stateProvider
              .state('bar1',
              {
                template: '<h1>Bar</h1><h2>bar1</h2>', 
                controller: 'MyCtrl1'
              })
              .state('bar2',
              {
                template: '<h1>Bar</h1><h2>bar2</h2>', 
                controller: 'MyCtrl2'
              });
        }]);
    
        angular.module('barApp.controllers', [])
                .controller('MainCtrl', ['$scope', '$state', function($scope, $state){
                  $scope.state = function(name){
                    console.log('Transition to state ' + name);
                    $state.transitionTo(name);
                  }
                }])
                .controller('MyCtrl1', [function () {
                    console.log("barApp.MyCtrl1 invoked.");
                }])
                .controller('MyCtrl2', [function () {
                    console.log("barApp.MyCtrl2 invoked.");
                }]);
    
        // manually bootstrap
        var div = document.getElementById('barApp');
        console.log(div);
        angular.bootstrap(div, ['barApp']);
    </script>
    
    
    </body>
    
    </html>
    

    Working plunker of this solution at http://plnkr.co/edit/bXSN8qSMdioZJLYs2zyk?p=preview

    Please see my previous answer for a discussion currently occurring to make portlet support more intrinsic in the ui-router.

    Figured it out. Here’s how to successfully load two angular applications in parallel. Also see that I named the controllers the same for each app to show that dependencies will not collide (since they are scoped within each respective app via module):

    <!doctype html>
    <html lang="en">
    
    <body>
    
    <script src="lib/angular/angular.js"></script>
    
    <!-- define foo -->
    
    <div id="fooApp">
    
        <ul class="menu">
            <li><a href="#/foo1">foo1</a></li>
            <li><a href="#/foo2">foo2</a></li>
        </ul>
        <div ng-view>
        </div>
    
    </div>
    
    <script>
        // Declare app level module which depends on filters, and services
        var app = angular.module('fooApp', ['fooApp.controllers']);
    
        // Configure the app
        app.config(['$routeProvider', function ($routeProvider) {
            $routeProvider.when('/foo1', {template: '<h1>Foo</h1><h2>foo1</h2>', controller: 'MyCtrl1'});
            $routeProvider.when('/foo2', {template: '<h1>Foo</h1><h2>foo2</h2>', controller: 'MyCtrl2'});
        }]);
    
        angular.module('fooApp.controllers', []).
                controller('MyCtrl1', [function () {
                    console.log("fooApp.MyCtrl1 invoked.");
                }])
                .controller('MyCtrl2', [function () {
                    console.log("fooApp.MyCtrl2 invoked.");
                }]);
    
        // manually bootstrap
        var div = document.getElementById('fooApp');
        console.log(div);
        angular.bootstrap(div, ['fooApp']);
    
    
    </script>
    
    
    <!-- define bar -->
    
    <div id="barApp">
    
        <ul class="menu">
            <li><a href="#/bar1">bar1</a></li>
            <li><a href="#/bar2">bar2</a></li>
        </ul>
        <div ng-view>
        </div>
    
    </div>
    
    <script>
        // Declare app level module which depends on filters, and services
        var app = angular.module('barApp', ['barApp.controllers']);
    
        // Configure the app
        app.config(['$routeProvider', function ($routeProvider) {
            $routeProvider.when('/bar1', {template: '<h1>Bar</h1><h2>bar1</h2>', controller: 'MyCtrl1'});
            $routeProvider.when('/bar2', {template: '<h1>Bar</h1><h2>bar2</h2>', controller: 'MyCtrl2'});
        }]);
    
        angular.module('barApp.controllers', []).
                controller('MyCtrl1', [function () {
                    console.log("barApp.MyCtrl1 invoked.");
                }])
                .controller('MyCtrl2', [function () {
                    console.log("barApp.MyCtrl2 invoked.");
                }]);
    
    
        // manually bootstrap
        var div = document.getElementById('barApp');
        console.log(div);
        angular.bootstrap(div, ['barApp']);
    </script>
    
    
    </body>
    
    </html>
    

    The only remaining question is how to deal with routing collisions.

    Well you have 2 choices here:
    if you create them as angular.module() there would not be a way atm to connect the modules with each other.

    if you create directives with a templateURL to lazyload your components you could broadcast shared attributes and listen to them and you could use the same services in your app.
    Probably that would be the best for you.