25

In my test, given 2 document, A and B. In A document, there is an iframe, the iframe source is B document. My question is how to modify B document certain scope of variable?

Here is my code: A document

<html lang="en" ng-app="">
<head>
  <meta charset="utf-8">
  <title>Google Phone Gallery</title>
  <script type='text/javascript' src="js/jquery-1.10.2.js"></script>
  <script type='text/javascript' src="js/angular1.0.2.min.js"></script>
  <script>
  var g ;
function test($scope,$http,$compile)
{
    $scope.tryget = function(){

        var iframeContentWindow = $("#iframe")[0].contentWindow;
        var iframeDOM = $("#iframe")[0].contentWindow.document;
        var target = $(iframeDOM).find("#test2");
        var iframeAngular = iframeContentWindow.angular;
        var iframeScope = iframeAngular.element("#test2").scope();
        iframeScope.parentcall();
        iframeContentWindow.angular.element("#test2").scope().tempvalue = 66 ;
        iframeScope.tempvalue = 66;
        iframeContentWindow.tt = 22;
        iframeScope.parentcall();
        console.log(iframeScope.tempvalue);
        console.log(angular.element("#cont").scope());
    }
}

  </script>
</head>
<body>

<div ng-controller="test">
        <div id="cont" >
            <button ng-click="tryget()">try</button>
        </div>
</div>
<iframe src="test2.html" id="iframe"></iframe>

</body>
</html>

My B document:

<!doctype html>
<html lang="en" ng-app="">
<head>
  <meta charset="utf-8">
  <title>Google Phone Gallery</title>
  <script type='text/javascript' src="js/jquery-1.10.2.js"></script>
  <script type='text/javascript' src="js/angular1.0.2.min.js"></script>
  <script>
var tt =11;
function test2($scope,$http,$compile)
{
    console.log("test2 controller initialize");
    $scope.tempvalue=0;
    $scope.parentcall = function()
    {
        $scope.tempvalue = 99 ;
        console.log($scope.tempvalue);
        console.log(tt);
    }
}

  </script>
</head>
<body>

<div ng-controller="test2" id="test2">
        <div id="cont" >
            <button ng-click="parentcall()">get script</button>
        </div>
        {{tempvalue}}
</div>

</body>
</html>

Note: Actually there is some way to do it, which i feel it like a hack instead of proper way to get it done: that is create a button in b Document, and then bind with angularjs ng-click. After that A document jquery "trigger" click on button.

Stupidfrog
  • 2,042
  • 6
  • 25
  • 35
  • 2
    Just a quick note. Remember that you can't usually communicate with between an iFrame and the parent page unless they are on the same domain. There are ways to do it, but you can't just access them directly. Just so you don't run into this issue later :) – Erik Honn Aug 26 '13 at 09:33
  • Yes, there are same domain and same project. Just cant properly manipulate those JS object and variable. Wondering if anyone have a better idea. – Stupidfrog Aug 27 '13 at 06:08

4 Answers4

48

To access and communicate in two directions (parent to iFrame, iFrame to parent), in case they are both in the same domain, with access to the angular scope, try following those steps:

*You don’t need the parent to have reference to angularJS library…

Calling to child iFrame from parent

1.Get child iFrame element from the parent (link to answer):

document.getElementById("myIframe").contentWindow

2.Access the scope of the element:

document.getElementById("myIframe").contentWindow.angular.element("#someDiv").scope()

3.Call the scope’s function or property:

document.getElementById("myIframe").contentWindow.angular.element("#someDiv").scope().someAngularFunction(data);

4.Call $scope.$apply after running the logic of the function/updating the property (link to Mishko’s answer):

$scope.$apply(function () { });

  • Another solution is to share the scope between the iFrames, but then you need angular in both sides: (link to answer and example)

Calling parent from child iFrame

  1. Calling the parent function:

parent.someChildsFunction();

Will update also on how to do it cross domain if it is necessary..

Community
  • 1
  • 1
Urigo
  • 3,175
  • 21
  • 27
  • 6
    I'd like to add that you can grab the parent scope from the iframe like this: `var $scope = parent.angular.element('#element-id').scope();` – gak Jun 23 '14 at 01:39
  • Urigo, that's great and consolidated. How to do it cross domain? – rajat Jul 13 '15 at 09:40
  • Thanks for the great answer. Angular 1.2x introduced the $evalAsync() method which avoids having to use $timeout with $apply. Using $timeout was recommended in earlier versions of Angular to guarantee that the change would be picked up in the $digest cycle (could end up in a race condition otherwise). – pmont Sep 10 '15 at 22:00
  • 1
    WARNING! .scope() is not a production method. It is for debugging only and is removed in production mode! https://docs.angularjs.org/guide/production – Jamie Pate May 24 '16 at 17:50
  • Necro, sure, but wanted to give my 2cents on .scope(). I've worked with bastardized implementations of AngularJS. They were mostly JQuery and JSP with Angular tacked on for templating. In every JSP file we'd have a getScope() function and frequently abuse $apply(). It "worked" but obviously isn't ideal. I guess my point is .scope() may not be available in production, but don't be surprised if a production app relies on debug being enabled. – Jacob Barnes Jan 05 '21 at 16:55
14

You should be able to get parent scope from iFrame:

var parentScope = $window.parent.angular.element($window.frameElement).scope();

Then you can call parent method or change parent variable( but remember to call parentScope.$apply to sync the changes)

Tested on Angular 1.3.4

Yiding
  • 2,914
  • 3
  • 21
  • 16
3

The best way in my mind to communicate with the iframe is using window.top. If you want your iframe to get your parent's scope, you can set window.scopeToShare = $scope; within your controller and it becomes accessible for the iframe page at window.top.scopeToShare.

If you want your parent to get the iframe scope, you can use

window.receiveScope = function(scope) {
  scope.$on('event', function() {
    /* Treat the event */
  }
};

and within the iframe controller call window.top.giveRootScope($rootScope);

WARNING: If you are using this controller multiple times, make sure to use an additional ID to identify which scope you want.

Benoit Tremblay
  • 698
  • 5
  • 17
0

This one is quite simple and works for me:

in the controller code of iframe page:

$window.parent.window.updatedata($scope.data);

in the parent page controller code:

window.updatedata = function (data) {
 $scope.$apply(function () {
     $scope.data = data
   }
 }