Application widgets with angular.js

 

Behind the scenes with angular.injector and angular.element

Single page app?

Content driven

A lot of content, tons of pages, very SEO relevant...

web application

Less content, tons of interactions, less SEO relevant...

Let's mix it Up!

Content driven website

with application widgets

angular
bootstrap

no... not the twitter thingy!

Auto-bootstrap

<html>
<body ng-app="myApp">
  <p>Hello {{name}}!</p>
</body>
</html>

do-it-yourself

<html>
<body data-ng-boot="myApp">
  <p>Hello {{name}}!</p>
</body>
</html>
$(function() {
  $('[data-ng-boot]').each(function() {
    angular.bootstrap(this, [
      $(this).data('ngBoot')
    ]);
  });
});

Multiple Apps on one page

Why one if we can have many???

<html>
<body>
  <section data-ng-boot="widgetOne">
    <p>Hello {{name}}!</p>
  </section>
  <section data-ng-boot="widgetTwo">
    <p>Hello {{name}}!</p>
  </section>
</body>
</html>
angular.module('widgetOne', [])
  .run(function($rootScope) {
    $rootScope.name = 'Angular';
  });

angular.module('widgetTwo', [])
  .run(function($rootScope) {
    $rootScope.name = 'Multi-boot';
  });

$(function() {
  $('[data-ng-boot]').each(function() {
    angular.bootstrap(this, [
      $(this).data('ngBoot')
    ]);
  });
});

150 KB JavascRipt

for A widget that might not Even exists on the Page?!

Lazy loading of Angular.Js and widgets

<html>
<body>
  <section data-ng-boot="widgetOne:widget-one.js">
    <p>Hello {{name}}!</p>
  </section>
  <section data-ng-boot="widgetTwo:widget-two.js">
    <p>Hello {{name}}!</p>
  </section>
</body>
</html>
$(function() {
  $('[data-ng-boot]').each(function() {
    var $element = $(this);
    var split = $element.data('ngBoot').split(':');
    
    $LAB.script(function loadAngular() {
      if(!window.angular) {
        return '//ajax.googleapis.com/ajax/libs/angularjs/1.3.2/angular.min.js';
      }
    }).wait(function() {
      $LAB.script(function loadModules() {
        if(split.length > 1) {
          return split.splice(1);
        }
      }).wait(function() {
        angular.bootstrap($element, [
          split[0]
        ]);
      });
    });
  });
});

Access
Angular
from
the outside

element & injector

<html>
<body ng-app="myApp">
  Message: {{message}}
</body>
</html>
angular.module('myApp', [])
  .value('name', 'World');

var element = angular.element($('[ng-app="myApp"]'));

setTimeout(function() {
  element.scope().message = 'Test';
  element.scope().$digest();
}, 2000);

setTimeout(function() {
  var name = element.injector().get('name');
  var reversed = name.split('').reverse().join('');
  element.scope().message = 'Welcome ' + reversed + '!';
  element.scope().$digest();
}, 4000);

LET THEM
COMMUNICATE!

Runtime Architecture

with multiple apps

Direct communication

by addressing elements

function AlienMessageController($scope) {
  $scope.sendMessage = function(alienRootElementId) {
    var alienRootScope = 
        angular.element('#' + alienRootElementId).scope();
    
    alienRootScope.remoteMessages = alienRootScope.remoteMessages || [];
    alienRootScope.remoteMessages.push($scope.message);
    alienRootScope.$evalAsync();
  };
}

angular.module('widgetOne', [])
  .controller('AlienMessageController', AlienMessageController);

angular.module('widgetTwo', [])
  .controller('AlienMessageController', AlienMessageController);

$(function() {
  $('[data-ng-boot]').each(function() {
    angular.bootstrap(this, [
      $(this).data('ngBoot')
    ]);
  });
});

PUB / SUB with message hub

function MessageHub() {
  this.subscriptions = {};
  
  this.subscribe = function(type, scope) {
    if(type instanceof Array) {
      type.forEach(function(typeElement){
        this.subscribe.call(this, typeElement, scope);
      }.bind(this));
    }
    
    this.subscriptions[type] = this.subscriptions[type] || [];
    if(this.subscriptions[type].indexOf(scope) === -1) {
      this.subscriptions[type].push(scope);
    }
  };
  
  this.publish = function(senderScope, type, message) {
    if(this.subscriptions[type]) {
      this.subscriptions[type].forEach(function(scope) {
        scope.messages = scope.messages || [];
        scope.messages.push({
          sender: senderScope,
          type: type,
          message: message
        });
        scope.$evalAsync();
      });
    }
  };
}
function MessageHub() {
  this.subscriptions = {};
  
  this.subscribe = function(type, scope) {
    if(type instanceof Array) {
      type.forEach(function(typeElement){
        this.subscribe.call(this, typeElement, scope);
      }.bind(this));
    }
    
    this.subscriptions[type] = this.subscriptions[type] || [];
    if(this.subscriptions[type].indexOf(scope) === -1) {
      this.subscriptions[type].push(scope);
    }
  };
  
  this.publish = function(senderScope, type, message) {
    if(this.subscriptions[type]) {
      this.subscriptions[type].forEach(function(scope) {
        scope.messages = scope.messages || [];
        scope.messages.push({
          sender: senderScope,
          type: type,
          message: message
        });
        scope.$evalAsync();
      });
    }
  };
}

Thanks!

@GionKunz

 

Frontend Developer & Consultant

Application Widgets with Angular.js - Behind the scenes with angular.injector and angular.element

By Gion Kunz

Application Widgets with Angular.js - Behind the scenes with angular.injector and angular.element

This presentation will give you an insight into some options how to handle application widgets on a content driven website with Angular.js and shows you some basic internal concepts of Angular.js.

  • 2,916