$rootScope.$broadcast in factory cannot change a value in other $scope

620 views Asked by At

I have a home template (ionic) has a tab like this:

<ion-tab title="ACCOUNT" icon="ion-trophy" href="#/home/account" badge="levelBadge" badge-style="badge-assertive" on-select="enteringAccount()" on-deselect="leavingAccount()" ng-controller="homeTabCtrl">
            <ion-nav-view name="tab-account" animation="slide-left-right"></ion-nav-view>
        </ion-tab>

I want the levelBadge value change when there is something happen in other scope.

In other scope, I have this controller (lessonCtrl). When click a button on this view, the controller call this function inside this controller:

$scope.testBoardcast = function() {
        MyFirebaseService.testBoardcast();
}

And inside MyFirebaseService (a factory), I have this function:

function testBoardcast() {
        $rootScope.$broadcast('level-up', 2);
}

And inside my home template homeTabCtrl, I am listen to level-up event like this:

$rootScope.$on('level-up', function(event, data) {
        console.log ("App received level up boardcast: " + data);
        $scope.levelBadge = parseInt(data, 10); 
    });

But the problem is, when I click the button on lessonCtrl, levelBadge did not get update and the badge will not show even the console log "App received level up boardcast: " is shown right after I clicked the button.

If I listen on just the homeTabCtrl scope like this:

$scope.$on('level-up', function(event, data) {
        console.log ("App received level up boardcast: " + data);
        $scope.levelBadge = parseInt(data, 10); 
    });

Nothing happen when I click the button on lessonCtrl.

I have no clue how to make the levelBadge dynamically change when I click the button in a completely different state in lessonCtrl.

UPDATE: Here is my ui-route:

.state('home', {
  cache: false,
    abstract: true,
    url: "/home",
    templateUrl: "app/home/home.html",
  controller: 'homeTabCtrl',
  onEnter: function($state, MyFirebaseService) {
    var userId = MyFirebaseService.LoginUserId();
    if (!userId) {
        $state.go('auth.signin');
    };
  }
})

.state('home.courses', {
  cache: false,
  url: "/courses",
  views: {
    "tab-courses": {
      templateUrl: "app/home/courses.html"
    }
  }
})

.state('app', {
    abstract: true,
    url: "/app",
    templateUrl: "app/layout/menu-layout.html",
  controller: 'AppCtrl'
})

.state('app.lesson', {
  cache: false,
  url: "/lesson/:id",
  views: {
    'mainContent': {
      templateUrl: "app/all/lesson-detail.html",
      controller: "lessonCtrl"
    },
  }
})

So home is an abstract state. The tabs is in the home template. The lessonCtrl is actually in another abstract state called App. They are not partner or child scope. Just 2 different scopes.

1

There are 1 answers

2
soote On BEST ANSWER

I would need to see your ui-router config ($stateProvider) to further debug why your code isn't working as expected. It is most likely due to the homeTabCtrl not being instantiated at the time the $broadcast is called.

One solution is to use a service. Make your changes to the service, and in your homeTabCtrl, have a watch on that service.

In your lesson controller call

Level.levelUp(1);

Then in your homeTab controller watch the Level service for change

$scope.$watch(
  function () {
    return Level.currentLevel();
  },
  function(newVal, oldVal) {
    $scope.level = newVal;
  }
);

Here is a plunker setting up this structure: http://plnkr.co/edit/mAASNGNqr37k9n0RR89X?p=preview


Edit

Look at this plunker which sets up a similar view hierarchy: http://plnkr.co/edit/hyKECbyYAG8XzUh8x58K?p=preview. Notice how the console log prints the init message when you navigate to and from the app and home states.

Everytime you change from the home to the app state, the home controller get uninstantiated and the app controller gets instantiated (this answer somewhat explains the flow: angularjs controller instantiation, ui-router).

So when you go to the app states, the home states cannot be listening for events. The singleton service will work as you want, so I would suggest using that instead of the pub/sub approach.