2

I have decided to use the message service angular package in my current project. Within its directive there is a var = templateString which I would like to edit to a template of my choosing.

I would like to know how to edit this string without messing with the original code. I read a series of similar answers but closes I can find is to create a directive that completely overrides it. I just wish to edit the template string and leave in the existing code.

I'm using Angular 1.3.15

The directive

MessageCenterModule.
  directive('mcMessages', ['$rootScope', 'messageCenterService', function ($rootScope, messageCenterService) {
    /*jshint multistr: true */
    var templateString = '\
    <div id="mc-messages-wrapper">\
      <div class="alert alert-{{ message.type }} {{ animation }}" ng-repeat="message in mcMessages">\
        <a class="close" ng-click="message.close();" data-dismiss="alert" aria-hidden="true">&times;</a>\
        <span ng-switch on="message.html">\
        <span ng-switch-when="true">\
          <span ng-bind-html="message.message"></span>\
        </span>\
        <span ng-switch-default>\
          {{ message.message }}\
        </span>\
      </div>\
    </div>\
    ';
    return {
      restrict: 'EA',
      template: templateString,
      link: function(scope, element, attrs) {
        // Bind the messages from the service to the root scope.
        messageCenterService.flush();
        var changeReaction = function (event, to, from) {
          // Update 'unseen' messages to be marked as 'shown'.
          messageCenterService.markShown();
          // Remove the messages that have been shown.
          messageCenterService.removeShown();
          $rootScope.mcMessages = messageCenterService.mcMessages;
          messageCenterService.flush();
        };
        if (messageCenterService.offlistener === undefined) {
          messageCenterService.offlistener = $rootScope.$on('$locationChangeSuccess', changeReaction);
        }
        scope.animation = attrs.animation || 'fade in';
      }
    };
  }]);

Is it possible? What is the best way?

Shane
  • 2,375
  • 4
  • 22
  • 31

2 Answers2

2

I'm so very sorry friend, you are going to have to override it.

YOINK! You're going to decorate it. Sounds a little posh but, hey. It works.


Whenever you use $provide#decorator, you get the original instance as a local injectable by the name $delegate.

As such, you can keep what you want, and throw away what you don't.

First thing you're going to have to do is figure out how the original implementation makes use of what you want to modify, so as to not break the entire thing.

Luckily, the templateString you are looking to modify is only used as the directive.template, so it should be a fairly easy decoration.

It would go something like this:

app.config(function ($provide) {
  /**
   * note the use of 'directivename' + 'Directive'.
   * Whenever you decorate a directive, you have to apply the 'Directive' suffix due to this: 
   * https://github.com/angular/angular.js/blob/master/src/ng/compile.js#L727
   */
  $provide.decorator('mcMessagesDirective', function ($delegate) {
    /**
     * We're getting the item at the first index here, 
     * because the $delegate of a directive isn't quite an 'instance' - 
     * it's a collection of DDO's (directive definition objects) that
     * go by the same name. 
     *
     * Yes, the $compileProvider allows multiple directives with the same name. 
     * We're interested in the first one in this case.
     */
    var dir = $delegate[0]; 

    /**
     * Modify the template of the directive. 
     * You can either hardcode this, or:
     * - Decorate the directive so as to pass the template in. 
     * - Fetch a template with $http. 
     * - Inject a string from a service, constant, provider, you name it.
     */
    dir.template = 'your_own_custom_templateString';

    /**
     * Return the full collection of directives (or rather, 'the $delegate'). 
     */ 
    return $delegate; 
  });
});

And there you go, whenever you use mcMessages again, it'll make use of the hardcoded template you just gave it.


Moar links!

Community
  • 1
  • 1
0

You can modify the directive using AngularJS decorators, like this:

MessageCenterModule
  .config(function ($provide) {
    $provide.decorator('mcMessagesDirective', function ($delegate) {

      var directive = $delegate[0];
      //assign a new value to the template
      directive.template = 'My template';
      return $delegate;

    });
  });
yvesmancera
  • 2,915
  • 5
  • 24
  • 33
  • 1
    Sheesh, looks like someone beat me to it! :+1: to you sir. –  Jul 20 '15 at 18:16
  • Only by a few seconds, I +1d yours too since you provide links. – yvesmancera Jul 20 '15 at 18:17
  • Throws an error. `Error: [$injector:modulerr] Failed to instantiate module favoriteEats due to: [$injector:modulerr] Failed to instantiate module MessageCenterModule due to: [$injector:unpr] Unknown provider: mcMessagesProvider` Only when including the above code. – Shane Jul 20 '15 at 18:29
  • From my understanding this approach no longer works in Angular 1.3 + – Shane Jul 20 '15 at 18:31
  • Oops, made a mistake: It's $provide.decorator('mcMessagesDirective', it needs a suffix per https://github.com/angular/angular.js/blob/master/src/ng/compile.js#L727. I edited my answer. – yvesmancera Jul 20 '15 at 18:33
  • I'm using a decorator to modify angular-ui's uiSelectDirective in a 1.3.15 project, the decorator is working for me. Try adding the 'Directive' suffix and see if it works. – yvesmancera Jul 20 '15 at 18:35