13

I am trying to create a two-way data binding between two isolated controllers and a shared service (which provides another isolated scope):

app.factory("sharedScope", function($rootScope) {
    var scope = $rootScope.$new(true);
    scope.data = "init text from factory";
    return scope;
});

app.controller("first", function($scope, sharedScope) {
    $scope.data1 = sharedScope.data;
});

app.controller("second", function($scope, sharedScope) {
    $scope.data2 = sharedScope.data;
});

Fiddle: http://jsfiddle.net/akashivskyy/MLuJA/

When the application launches, data1 and data2 are correctly updated to the init text from factory, but later, if I change any of them, those changes are not reflected throughout those three scopes.

How can I bind them?

P.S. If there's a better way than returning a scope and still having access to event and observing functionalities (without basically re-writing them), let me know. :)

John Slegers
  • 45,213
  • 22
  • 199
  • 169
akashivskyy
  • 44,342
  • 16
  • 106
  • 116

3 Answers3

18

Fixed it. References will be lost if you are using primitives, as in your fiddle.

Check this:

Updated fiddle

app.factory("sharedScope", function($rootScope) {
    var scope = $rootScope.$new(true);
    scope.data = {text: "init text from factory"};
    return scope;
});

app.controller("first", function($scope, sharedScope) {
    $scope.data1 = sharedScope.data;
});

app.controller("second", function($scope, sharedScope) {
    $scope.data2 = sharedScope.data;
});
link
  • 1,676
  • 1
  • 13
  • 21
  • 1
    I thought it didn't matter whether I use objects or primitives. Thanks! :) – akashivskyy May 20 '14 at 10:08
  • Well, this is not really an AngularJS matter, it's more of a generic Javascript programming issue. Anyway, you are welcome :) – link May 20 '14 at 10:09
  • 1
    what's the advantage of going the extra mile of creating a new scope? Wont that eventually hit performance? why not just have the factory return an object that is then shared? so exact same code, just return {text: "init text from factory"} instead of wrapping it in a scope? – stefan Jul 22 '14 at 15:04
  • 1
    @stefan Sure, you are right. OP could achieve the same by using a service and avoiding to create a new scope. Anyway, I didn't point it out because it is out of the scope (lol) of this question. – link Jul 22 '14 at 15:27
  • 2
    @stefan, not only is it a performance hit, but constantly defining new scopes can lead to a world of trouble. As I mentioned in the comment to the answer below, a child scope will inherit from a parent scope. Defining a controller within a controller leads to the parent/child relationship. If I'm not mistaken, even defining variables in the view creates a child scope to the parent scope of the controller. Take a look at Josh Carroll's article on "Scope Soup" http://www.technofattie.com/2014/03/21/five-guidelines-for-avoiding-scope-soup-in-angular.html – Owen Jul 23 '14 at 14:23
6

Yet another fun bit: In this case, you don't need to inject $scope or $rootScope. The following code works if you utilize Controller As. Check the Fiddle

var app = angular.module("app", []);

app.factory("sharedScope", function() {
    var _this = this;
    _this.data = {text: "init text from factory"};
    return _this;
});

app.controller("first", function(sharedScope) {
    var _this = this;
    _this.data1 = sharedScope.data;
});

app.controller("second", function(sharedScope) {
    var _this = this;
    _this.data2 = sharedScope.data;
});

For even more fun, consider controllers, services, and factories as classes. More Fiddles

var app = angular.module("app", []);

var SharedScope = function(){
    var _this = this;
    _this.data = {text: "init text from factory"};
    return _this;
};

app.factory("sharedScope", SharedScope);

var First = function(sharedScope){
    var _this = this;
    _this.data1 = sharedScope.data;
};

var Second = function(sharedScope){
    var _this = this;
    _this.data2 = sharedScope.data;
};

First.$inject = ['sharedScope'];
Second.$inject = ['sharedScope'];

app.controller("first", First);              
app.controller("second", Second);

I've been playing at implementing Josh Carroll's Guidelines to Avoid "Scope Soup"

Owen
  • 728
  • 7
  • 17
  • 1
    Thank You! This is exactly what I wanted to hear, this isn't talked about much it seems. You don't need $scope as of v1.2.x it was mentioned in a few recent books they are trying to get away from it. Thanks for the link to "Scope Soup". This answer deserves more up votes than it currently shows. – mtpultz Oct 01 '14 at 07:32
  • IMO you should better use `.service()` for your `SharedScope` instead of `.factory()`, because you're using it like a Constructor. I'm not really sure, but I think it's possible that you bind your `_this.data` to the global object (`window`). – hgoebl Dec 20 '14 at 14:29
4

JavaScript passes objects by reference, so all scopes will point to the same object. Why not just do this?

app.factory("sharedData", function() {
    return {data: "init text from factory"};
});

app.controller("first", function($scope, sharedData) {
    $scope.sharedData = sharedData;
});

app.controller("second", function($scope, sharedData) {
    $scope.sharedData = sharedData;
});

and in your view:

<p>{{sharedData.data}}</p>
stefan
  • 427
  • 5
  • 12
  • 1
    In Angular, all $scopes do not point to the same object. $rootScope is the same throughout, but a $scope created in one controller is different from the $scope in a different controller. Also, to muddy the waters even more, a controller(2) within a controller(1) in a view can inherit the properties of the parent controller(1), but the parent controller(1) cannot see the properties of the child controller (2). Read more: http://modernweb.com/2014/07/14/leverage-scope-creep-depth-tutorial-angular-js-scope/ – Owen Jul 22 '14 at 20:17
  • 1
    @Owen, it's not the point that $scopes are pointing to the same object, but the service `sharedData` is a singleton, so all scopes referencing it point to the same data. – hgoebl Dec 20 '14 at 14:33