10

In an AngularJS application (main) I have an iframe inside which there is another AngularJS application (iframe) also under my control. I would like to share data between two services, one in the main application and one in the iframe application. They both need to read and write to the same object.

// main
// ... routes ...
views: { main: {
  controller: function ($scope, serviceA) {
    $scope.serviceA = serviceA;
  },
  templateUrl: 'iframe.html'
}
// ...
function ServiceA () {
  this.sharedData; // exposed to controllers in main app
}
// ...

// iframe
// ...
function ServiceB () {
  this.sharedData; // exposed to controllers in iframe app
}
// ...

When inside a controller in iframe application I managed to reference serviceA.sharedData like this:

var self = this;
var parentScope = $window.parent.angular.element($window.frameElement).scope();
parentScope.$watch('serviceA.sharedData', function (newValue, oldValue) {
  self.sharedData = newValue;
}

Can this be achieved and how?

I have read the following, but could not turn it into a solution, yet:

3
  • The answer to the second question you referenced says that you can access the angular object of an iframe with theIFrameElement.contentWindow.angular. Does that work for you? Commented Feb 8, 2015 at 12:54
  • Yes, it is possible to access the angular object like this. But how can I then access a service registered on it? Commented Feb 11, 2015 at 9:08
  • you can add the service to your scope in the controller, then you can access it from outside (i.e. the parent frame). Commented Feb 12, 2015 at 23:21

2 Answers 2

7
+50

I managed to do something that works, and could be useful for you. It's not perfect but is a good start. Here is the code:

Parent page:

<div ng-controller="ParentController">
    <h1>Hello, parent page!</h1>

    <p><strong>Parent model:</strong></p>
    <p>
        <input type="text"
           ng-model="data.foo"
           placeholder="Enter the thing you want to share"/>
    </p>

    <p><strong>Parent result:</strong></p>
    <p>{{ data.foo }}</p>

    <iframe src="child-page.html" frameborder="0"></iframe>
</div>

Child page:

<div ng-controller="ChildController">
    <h1>Hello, child page!</h1>

    <p><strong>Child result:</strong></p>
    <p>{{ data.foo }}</p>
</div>

app.js

var app = ng.module('myApp', []);

app.factory('DataService', ['$rootScope', '$window', function ($rootScope, $window) {
    var // Variables
        dataScope,

        // Functions
        getScope;

    dataScope = $rootScope.$new(true);

    getScope = function () {
        return dataScope;
    };

    $window.DataService = {
        getScope: getScope
    };

    return {
        getScope: getScope
    };
}]);

app.controller('ParentController', ['$scope', 'DataService', function ($scope, DataService) {
    $scope.data = DataService.getScope();
}]);

app.controller('ChildController', ['$scope', '$window', '$interval', function (
    $scope,
    $window,
    $interval
) {
    $scope.data = $window.parent.DataService.getScope();

    // makes a $scope.$apply() every 500ms. Without it, data doesn't change
    $interval(ng.noop, 500);
}]);

All of this code leads to this:

Working code

The important part is $scope.data = $window.parent.DataService.getScope(); in the child controller. That's where it fetches the shared $scope.

Of course, all this works only if the parent & the iframe are under the same domain. Else, it becomes a whole other complicated story...

Hope this will help you.

Sign up to request clarification or add additional context in comments.

Comments

6

Ok, so here is my solution, I hope this is what you had in mind.

in the controller of the parent application:

mainApp = angular.module('mainApp', []);
mainApp.controller('mainCtrl', ['$scope', 'sharedData', function($scope, sharedData){
    $scope.sharedData = sharedData;
    //your controller logic goes here ...
}]);

in the controller of the iframe application:

iframeApp = angular.module('iframeApp', []);
iframeApp.controller('iFrameCtrl', function($scope){
    //here we get the service instance from the parent application, if you 
    //need it in other controllers in the iframe app as well, either get it 
    //there the same way or pass it along via $scope or $rootScope
    var sharedData = window.parent.angular.element(window.frameElement).scope().sharedData;
    //now we can use sharedData the same way as in the parent application controller

});

sharedData.js (the js file for the shared service, needs only be included by parent.html)

mainApp.factory('sharedData', function(){
    var list = [];
    var mainScope;
    var iframeScope;

    //this function needs to be called in all setter methods to update the data in both applications
    function update(){
        if(!mainScope){
            mainScope = angular.element(document.body).scope();
        }
        //$apply() causes errors in the dev console, $applyAsync doesn't, I don't know why
        mainScope.$applyAsync(); 
        if(!iframeScope){
            //the update function might be called before angular is initialized in the iframe application
            if(document.getElementById('iframe').contentWindow.angular){
                iframeScope = document.getElementById('iframe').contentWindow.angular.element(document.body).scope();
                iframeScope.$applyAsync();
            }
        } else {
            iframeScope.$applyAsync();
        }
    }
    return {
        append: function(item) {
            list.push(item); 
            //don't forget to add this at the end of all setter functions in the service
            update();
        },
        getAll: function() { return list }
    }
});

The stuff with the iframes doesn't work on jsfiddle (cross-origin maybe) so I put my more extensive example on a github page:

https://github.com/sammax/angulariframe (code)

http://sammax.github.io/angulariframe/main/ (result)

Comments

Your Answer

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.