3

I have an iframe that has an ng-src attribute on it, and I change it frequently. Each time that the iframe's src changes, I want to execute a function in my controller AFTER the iframe is fully loaded. Additionaly, I want to have the iframe DOM element passed into the function.

Right now I'm using a directive from this StackOverflow post. The callback fires when the iframe is loaded, and the function in my controller executes; but I can't get the iframe DOM element passed in as a parameter.

Here is a demo Plunkr

THE HTML

<div ng-controller='home as main'>
    <h2>My Content Up Here</h2>

    <button ng-click="main.setIframeSource()">Load iFrame Src</button>
    <iframe iframe-onload="main.onIframeLoad(element)" 
      ng-src="{{main.currentIframeSource}}"></iframe>
</div>

THE Javascript

(function() {
  angular.module('app', [])
    .controller('home', home)
    .directive('iframeOnload', iframeOnload);

  function home() {
    var vm = this;
    vm.currentIframeSource = '';
    vm.setIframeSource = setIframeSource;
    vm.onIframeLoad = onIframeLoad;

    function onIframeLoad(element) {
      console.log(element);
    }

    function setIframeSource() {
      if (vm.currentIframeSource === '' || vm.currentIframeSource === 'iframe2.html')
        vm.currentIframeSource = 'iframe.html';
      else
        vm.currentIframeSource = 'iframe2.html';
    }

  }

  function iframeOnload() {
    var directive = {
      scope: {
        callBack: '&iframeOnload'
      },
      restrict: 'A',
      link: function(scope, element, attrs) {
        element.on('load', function() {
          return scope.callBack(element);
        });
      }
    };

    return directive;
  }
})();

I've tried using $event and passing in this as a parameter to the onIframeLoad function in my HTML, but I can never get a reference to the iframe DOM element.

Thoughts?

Community
  • 1
  • 1
rwisch45
  • 3,692
  • 2
  • 25
  • 36

1 Answers1

1

When you want to pass data to an expression defined in an isolate scope, you need to pass them via an object with named parameters. See this documentation page.

So theoretically, all you would have to do is change your directive scope.callback(element) to:

return scope.callBack({element:element});

BUT, Angular is trying to enforce separation of concerns between what you can do in directives and what you can do in controllers. You aren't supposed to do anything with raw elements in your controllers so Angular will prevent you from doing what I just wrote and point you to a page like this: Error: error:isecdom Referencing a DOM node in Expression

If you want to deliberately break the rules and do a workaround, what you can do is wrap the element in your own object and then it will pass through fine (but remember, this is not recommended)

  link: function(scope, element, attrs) {
    element.on('load', function() {
      console.log('in directive iframe loaded',element);
      // bad bad bad!
      var elemWrapper = {theElem:element};
      return scope.callBack({element:elemWrapper});
    });
  }

I created a plunker showing that this works (uncomment the bad part to see the element being passed)

mustaccio
  • 18,234
  • 16
  • 48
  • 57
JoseM
  • 4,302
  • 2
  • 24
  • 35
  • So, if I wanted to make an `iframe` directive that allowed me to pass a generic function TO the iframe once it is loaded (in it's onLoad event), what would be the recommended approach? – rwisch45 Nov 06 '14 at 04:34
  • That's a different question but you can't really pass a function to an iframe, you need to use postMessage to communicate with the iframe. – JoseM Nov 06 '14 at 04:44
  • thank you for the feedback. I understand that this approach is NOT recommended. BUT, if i wanted to have an iframe onLoad directive to pass generic functions to an iframe's onLoad event, how should I approach this? Is there a recommended way? – rwisch45 Nov 06 '14 at 05:11
  • specifically, I want to find a DOM element within the loaded iFrame and change it's value/class. I hate the idea of using jQuery in my Angular, but I'm not sure if there is another *pure* Angular way – rwisch45 Nov 06 '14 at 13:54
  • In your directive you can add the neccessary logic about what needs to be done (like how to find the element, what to do with it, etc). BUT the ability for what you can actually do to the content in the iFrame will depend on if it's on the same domain or if you have access to change the website itself. See this http://stackoverflow.com/questions/164085/javascript-callback-when-iframe-is-finished-loading – JoseM Nov 06 '14 at 16:35