Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

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!

like image 892
Robert Christian Avatar asked May 24 '13 00:05

Robert Christian


2 Answers

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="[email protected]" 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.

like image 60
Paul Ryan Avatar answered Nov 03 '22 08:11

Paul Ryan


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.

like image 45
Robert Christian Avatar answered Nov 03 '22 07:11

Robert Christian