Stuart Harding
Stuart Harding

Reputation: 123

Script added from directive into dom not firing

I have created a simple directive that generates an svg indeterminate spinner. The script when included on the page works beautifully. The svg and script are generated perfectly. The script does not fire.

I have this plunk of the generated output

<svg width="64" height="64" viewBox="0 0 64 64">
  <g transform="translate(32, 32)"> 
    <circle id="myId" 
      fill="none" 
      stroke="#000000" 
      opacity=".8" 
      stroke-width="5" 
      stroke-linecap="round" 
      stroke-miterlimit="10" 
      cx="0" cy="0" r="29.5"
      >
    </circle>
  </g>
</svg>
<script>
  var line = document.getElementById("myId");
  var initialTheta = 0;
  var thetaDelta = 1;
  myId.currentTheta = initialTheta;
  var requestAnimationFrameID = requestAnimationFrame(doCircAnim);
  function doCircAnim() {
    myId.setAttribute("transform", "rotate(" + myId.currentTheta + ")");
    myId.setAttribute("stroke-dasharray", myId.currentTheta);
    myId.currentTheta += thetaDelta;
    if(myId.currentTheta > 180){ myId.currentTheta = 0}
    requestAnimationFrameID = requestAnimationFrame(doCircAnim);
  }
</script>

demonstrating the correct behaviour and it should work as a standalone element - which it does. This is generated by the directive which is not linked to the plunk, but it should be fairly self-explanatory. The animating script was lifted from Microsoft's svg animation starter page as it is all that's required (with a couple of tweaks).

I have tried a whole bunch of things with either no success or a new batch of errors and then failure. I'm sure this has been answered before in some shape so if it has please accept my apologies, but I have failed to find a solution.

Thanks in advance.

NEW plunk based on original showing behaviour Running directive with options

(function(){
  'use strict';
  angular.module('loader', [])
  .directive('loader', ['$window', function($window){
    return {
      restrict: 'E',
      link: function (scope, element, attrs){
         ...
      }
    }
  }
})();

Upvotes: 0

Views: 78

Answers (1)

Stuart Harding
Stuart Harding

Reputation: 123

Thanks to Mark Keats for this one, and all other suggestions both from this board and my colleagues.

Although there was nothing intrinsically wrong with the scripts, the way Angular handles injecting through using jqLite is where the issue arises in that it is input to the DOM and just left there as it uses the innerHTML method which cannot run scripts.

So, removing the script from the DOM is the only way to go. Happily, as you can use vanilla js with angular stuff with no ill effect, moving the script to the directive body did the trick. Each instance of the loader is given a unique id and there is only one version of the script so they all access the same one - instead of having one script per instance. I have updated the plunk above to reflect this so that there is never a non-working version. The broken script is still in the first referenced plunk.

(function(){

  'use strict';

  angular.module('loader', [])

  .directive('loader', ['$window', function($window){

    var animationIndex = 0;

  return {
    restrict: 'E',
    link: function (scope, element, attrs){

      var shape = attrs.ccId;
      var type = attrs.ccType;
      var width = attrs.ccStrokeWidth || 6;
      var diameter = attrs.ccDiameter || 24;
      var stroke = attrs.ccStroke;
      var opacity = attrs.ccOpacity || 1/5;
      var elemWidth = element[0].clientWidth;
      var originOffset = (diameter === false)? 32 : diameter / 2;
      var radius = originOffset - ((width / 2) + 2);
      var reset = (type === 'line')? elemWidth : diameter * Math.PI;
      var animationTarget;
      var thetaDelta = 1;

      function doAnim() {
        if(type === 'circle') {
          animationTarget.setAttribute("transform", "rotate(" + animationTarget.currentTheta + ")");
          animationTarget.currentTheta += .1
        }
        else {animationTarget.currentTheta += .6}
        animationTarget.setAttribute("stroke-dasharray", animationTarget.currentTheta);
        animationTarget.currentTheta += thetaDelta
        if(animationTarget.currentTheta > reset){ animationTarget.currentTheta = 0}
        requestAnimationFrame(doAnim);
      }

      // <cc-loader cc-id="myId" cc-type="[circle,line]" cc-opacity="[default = .2]" cc-diameter="[int - circle type only]" cc-stroke="[# colour value]" cc-stroke-width="[default = 5]"></cc-loader>

      if (type == 'line') {

        element.html('<svg' +
          ' width="'+elemWidth+'" height="'+width+'" viewBox="0 0 '+elemWidth+' '+width+'">' +
            '<g transform="translate('+ -width * 2+', '+width / 2+')">' +
            '<line' +
              ' id="'+shape + animationIndex +'"' +
              ' fill="none"' + 
              ' stroke="'+stroke+'"' + 
              ' opacity="'+opacity+'"' +
              ' stroke-width="'+width+'"' + 
              ' stroke-linecap="round"' + 
              ' stroke-miterlimit="10"' + 
              ' x1="'+width+'"' + 
              ' y1="0"' + 
              ' x2="'+elemWidth+'"' + 
              ' y2="0"' +
              '>' +
            '</line>' +
            '</g>' +
          '</svg>');

        animationTarget = document.getElementById(shape+animationIndex);
        animationTarget.currentTheta = 0;
        doAnim();
        animationIndex++;

      }

      else if (type == 'circle') {

        element.html('<svg' +
          ' width="'+diameter+'" height="'+diameter+'" viewBox="0 0 '+diameter+' '+diameter+'">' +
            '<g transform="translate('+originOffset+', '+originOffset+')">' +
              ' <circle' +
              ' id="'+shape + animationIndex +'"' +
              ' fill="none"' +
              ' stroke="'+stroke+'"' +
              ' opacity="'+opacity+'"' +
              ' stroke-width="'+width+'"' +
              ' stroke-linecap="round"' +
              ' stroke-miterlimit="10"' +
              ' cx="0"' +
              ' cy="0"' +
              ' r="'+radius+'">' +
            '</circle>' +
          '</g>' +
        '</svg>');

        animationTarget = document.getElementById(shape+animationIndex);
        animationTarget.currentTheta = 0;
        doAnim();
        animationIndex++;
      }

      else {
        element.html('Types allowed for this element are \'line\' and \'circle\'');
      }
    },
  };
}]);

})();

Now on GitHub. Feel free to play and send a pull request if you want to make it better

Upvotes: 1

Related Questions