Доступ к службе AngularJS из службы внутри другого фрейма

В приложении AngularJS (main) у меня есть iframe, внутри которого также находится другое приложение AngularJS (iframe). Я хотел бы поделиться данными между двумя службами, один в основном приложении и один в приложении iframe. Оба они должны читать и писать на один и тот же объект.

// 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
}
// ...

Когда внутри контроллера в приложении iframe мне удалось ссылаться на serviceA.sharedData следующим образом:

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

Можно ли это сделать и как?

Я прочитал следующее, но не смог превратить его в решение, но:

Ответ 1

Итак, вот мое решение, я надеюсь, что это то, что вы имели в виду.

в контроллере родительского приложения:

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

в контроллере приложения iframe:

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 (файл js для общей службы должен быть включен только 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 }
    }
});

Материал с iframe не работает на jsfiddle (возможно, кросс-происхождение), поэтому я разместил свой более обширный пример на странице github:

https://github.com/sammax/angulariframe (код)

http://sammax.github.io/angulariframe/main/ (результат)

Ответ 2

Мне удалось сделать то, что работает, и может быть полезно для вас. Это не идеальное, но хорошее начало. Вот код:

Родительская страница:

<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>

Страница для детей:

<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);
}]);

Весь этот код приводит к следующему:

Working code

Важной частью является $scope.data = $window.parent.DataService.getScope(); в дочернем контроллере. Это, когда он извлекает общую область $scope.

Конечно, все это работает только в том случае, если родительский и iframe находятся в одном домене. Иначе это станет еще одной сложной историей...

Надеюсь, это поможет вам.