Last Updated: July 15, 2016
·
3.996K
· sirtophamhatt

AngularJS Mediator Pattern

Synopsis

A standard Mediator Pattern applied to AngularJS which does not use $broadcasts or $emits.

  • Avoids unnecessary injecting and exposing of $scope onto one's view where controllerAs is preferred.
  • Circumvents using $rootScope to update viewmodels existing on sibling $scope paths.
  • Does not trigger additional $apply or $digest cycles.
  • Employs lodash for better cross-browser performance.
  • Mediator.subscribe returns a function to deregister the subscriber, similar to Angular's $scope.$on implementation.

( function( angular , _ ) {
    function MediatorService( $log ) {
        var Mediator          = {};

        var channels          = {};
        var subscriberId     = 0;


        Mediator.publish = function( channel , publisher , data ) {
            if ( ! hasSubscriber( channel ) ) { 
                return;
            }

            var subscribers = channels[ channel ];

            _.map( subscribers, function( subscriber ) {
                try {
                    subscriber.callback( data );
                 } catch ( e ) {
                     $log.error( e , publisher , subscriber.name )
                 }
            } );
        };


        Mediator.subscribe = function( channel , subscriber , callback ) {
            if ( ! hasSubscriber( channel ) ) {
                channels[ channel ] = [ ];
            }

            channels[ channel ].push( {
                "callback"           : callback ,
                "name"               : subscriber ,
                "subscriberId"     : ++subscriberId
            } );

            return function( ) {
                unsubscribe( subscriberId );
            };
        };


        function hasSubscriber( channel ) {
            return _.has( channels , channel );
        }


        function unsubscribe( subscriberId ) {
            channels = _.map( channels , function( channel ) {
                return _.filter( channel , function( subscriber ) {
                    return subscriber.subscriberId !== subscriberId;
                });
            });
        }

        return Mediator;
    }

    angular
        .module( "app" )
        .factory( "Mediator" , [ "$log" , MediatorService ] );

} ) ( angular, _ );

Implementation

Plnkr

HTML

<div data-first=""></div>
<div data-second=""></div>

JS

angular
    .module( "app" , [ ] )
    .factory( "Mediator" , [ "$log", MediatorService ] )
    .directive( "first" , function( ) {      
        return {
            controller: function( ) {
                this.name = "First";      
            },
            controllerAs: "first",
            template: "<p ng-bind='first.name'></p><div data-third></div>"
        }; 
    } )
    .directive( "second" , function( Mediator ) { 
        return {
            controller: function( ) {
                this.name = "Second"; 
                 this.publishNewName = Mediator.publish.bind( this , "NewName" , "second" , this.name );
            },
            controllerAs: "second",
            template: "<a ng-bind='second.name' ng-click='this.publishNewName( )'></a>"
        }; 
      } )
     .directive( "third" , function( Mediator ) {
          return {
              controller: function( ) {
                  this.name = "Third";
                  var unsubscribe = Mediator.subscribe( "NewName" , "third" , setName.bind( this ) );                  

                  function setName(name) {
                     this.name = name;
                     unsubscribe( );
                  }
              },
              controllerAs: "third",
              template: "<p ng-bind='third.name'></p>"
          };    
     } );

1 Response
Add your response

Why did you reinvent $broadcast/$emit and $on? What's better about this method than using events?

over 1 year ago ·

Have a fresh tip? Share with Coderwall community!

Post
Post a tip