4

I have developed an extension which adds the possibility to add attachments to a message template. When a user select the template in CiviMail the linked attachments are also sent. This is working well.

But now I want to show which attachments are linked to a template when a user selects a template in the CiviMail UI.

In the old way I would add jQuery and an event listener to the drop down with templates and then fetch with Ajax the linked attachments and inject them somewhere in the screen. However CiviMail is now working with Angular and my understanding is that this should be much and much easier in Angular. However I have played around and can't get it working.

My question is how do I use angular to listen for a template change and then inject some data into the angular template?

4
  • Not an answer, just a note, you can access the angular scope with javascript as angular is already in the page, create/edit new/existing mailing and try this in the browser console: var scope = angular.element('[ng-controller="MsgTemplateCtrl"]').scope(); scope.$watch('mailing.msg_template_id', function(){ console.log(scope.mailing.msg_template_id); }); with scope.mailing you can access and listen to the mailing params, I guess you already know it but if you enable debug and add ?angularDebug at the end of the url you will see a new accordion with the params, hope that helps somehow
    – Andrei
    Mar 15, 2016 at 22:42
  • Thanks Andrei. However it does not work because the element the scope() function is not available during execution of my script. Mar 16, 2016 at 10:57
  • how do you inject the script? I just tried it myself, and indeed it failed but enclosing the mentioned javascript in a function and delaying the function with setTimeout() it does work as expected, the mailing page always takes a 2-4 seconds to completely load all the elements in my case
    – Andrei
    Mar 16, 2016 at 12:12
  • Yes I have discovered that with a setTimeout it is working but I don't think setTimeout is reliable because on my computer it could be 2-3 seconds on another one it could 4-5 seconds before the page is ready. My script is injected with the angularModules hook from civicrm Mar 16, 2016 at 12:17

1 Answer 1

7

I've been recently digging into Angularjs, as I'm trying to make some customization, and this is what I figured out after a reading loads of blog posts covering the subject. I'm not an angular expert so I don't know how this affects performance.

The following script is from an extension using hook_civicrm_angularModules and Angular's $provide service in conjunction with the decorator function to intercept a CiviCRM directive and 'decorate' it to add/replace/extend behaviour, more info here.

Demonstration:

// myModule.js  
  (function(angular, $, _) {
      // declare your module
      angular.module('myModule', []);

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

              // Original compile function
              var compile = directive.compile;
              // New compile function
              directive.compile = function(Element, Attrs){
                  var link = compile.apply(this, arguments);
                  // Append your custom directive
                  Element.append('<div crm-my-directive></div>');
                  return function(scope, elem, attrs){
                      link.apply(this, arguments);
                  };
              };
              return $delegate;
         });
      });

      /*
      * Extending crmMailing
      */

      // Your simple custom directive
      angular.module('crmMailing').directive('crmMyDirective', function(){
         return {templateUrl: CRM.resourceUrls['uk.co.myextension'] + '/ang/directives/crmMyDirective.html',
                controller: 'ExampleCtrl',
         };
      });

      // Your controller
      angular.module('crmMailing').controller('ExampleCtrl', function($scope, crmMsgTemplates){
        $scope.$watchCollection('mailing', function(mailing){
            crmMsgTemplates.get(mailing.msg_template_id)
            .then(function(value){
                 mailing.msg_template_title = value.msg_title;
            });
        });
      });

    })(angular, CRM.$, CRM._);

Custom directive template

<div id="my-directive">
    <div crm-ui-accordion="{title: ts('My Custom Directive')}">
        <p>Template Id: {{mailing.msg_template_id}} | Template Title: {{mailing.msg_template_title}}</p>
    </div>
</div>

enter image description here

You can pretty much create your own services/factories, directives, etc or change the behavior of existing ones and hook it into the Mailing app.

Hope it helps.

1
  • Thanks for sharing this! It looks like a pretty thorough example and lines up with ideas from Angular upstream. (For reference, there are some relevant docs like $provide, but they're pretty thin. This answer seems more comprehensive.)
    – Tim Otten
    May 27, 2016 at 20:10

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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