2

I'm making a first foray into customizing the mailing compose screen.

I want to add a third option to the options in BlockSchedule.html (schedule.mode = regular) which will open up a few more options to allow you to send mailings on a regular basis.

The template code I want to add is along the lines of

<div>
  <input ng-model="schedule.mode" type="radio" name="send" value="recur" id="schedule-send-recur"/>
  <label for="schedule-send-recur">{{ts('Send on a regular basis')}}</label>
</div>

Selecting this will open up a new directive with options for sending on a regular basis. It will also swap the submit mailing button for a schedule mailings button and possibly do other stuff.

But right now, I am stuck on a good way to add this option.

This answer (adapted below) makes me think that something along the lines of the following would work, but what do I do with the directive at this point?!

angular.module('crmMailing').config(function($provide){
   // intercept crmMailingBlockMailing directive, note the 'Directive' suffix
   $provide.decorator('crmMailingBlockScheduleDirective', function($delegate){
        var directive = $delegate[0];
1

What you want to do now is decorate the directive ;)

The decorator method allows you to decorate/modify an angular directive/service/factory/controller before its usage/instantiated.

You have two options (that I know of) to manipulate the directives DOM, one is the compile function, used for template DOM manipulation (is where the template is actually compiled), and the second is the link function, is where the data/scope is attached to the instantiated element/elements, and usaully used for event listeneres, like $watch

The angular mailing app uses a boilerplate template to declare directives, see the source here.

I'll try to explain with inline comments:

angular.module( 'crmMailing' ).config( function( $provide ){

        // the callback recieves the directive when the Mailing app bootsraps
        $provide.decorator( 'crmMailingBlockScheduleDirective', function( $delegate ){

            // $delegate is an array, we grab and store the directive into the 'directive' variable
            var directive = $delegate[0];

            // we hold the original link function 
            var link = directive.link;

            // as the link function is already defined in Civi's boilerplate
            // we create the compile function and return a new link function calling apply on the old link function
            directive.compile = function( Element, Attrs ){

                // you can manipulate the directive's template here, like adding your own DOM elements/directives
                Element.append( '<h1>Text added from compile</h1>' );

                // return new link function
                return function( scope, elem, attr ){

                    // call apply to get original functionality
                    link.apply( this, arguments );

                    // add new functionality here
                    elem.append( '<h1>Text added from new link function</h1>' );

                };
            };

            // always return $delegate
            return $delegate;
        }); 
    });

In order for this to work what I do is declare a fake/empty angular module like crmMailSubjectReview = angular.module( 'crmMailSubjectReview', [] );, load it via hook_civicrm_angularModules and then add the crmMailing config block, see here another small example (have a look at the comments to see the template and the end result).

| improve this answer | |
  • Thanks for this, Andrei. I have your code example above working. I'm trying to work out when I might use the first example Element.append() and when I might try and use the last Elem.append. Any ideas? Or should I not worry about it. – michaelmcandrew Apr 10 '17 at 13:29
  • You usually want to use compile (Element.append()), specially if you are appending directives, if you try to append a directive in link it won't work, as the current directive/element and its children at that point are already instantiated. link will render "normal" DOM elements but not angular elements. You could use the $compile service injecting it in link and then do something like var myDirective = '<div my-directive></div>'; var compiledEl = $compile(myDirective)($scope); elem.append(compiledEl); but I think you'll probably run into issues with this approach – Andrei Apr 10 '17 at 22:04
  • In summary, when using compile you are dealing with template DOM elements (angular elements) and when using link you are dealing with jQuery-ish elements that can be manipulated based on scope and attributes, if that makes sense – Andrei Apr 10 '17 at 22:31
  • Thanks - getting there now - will post a link to what i did soon (just polishing slightly) in case that is useful for people... – michaelmcandrew Apr 12 '17 at 10:42
  • Hey Andrei - in case you're interested, I posted my angular code above. – michaelmcandrew Apr 13 '17 at 13:52

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.