Francisc
Francisc

Reputation: 80505

Send message between hierarchically equal controllers

This is the HTML (fragment):

<div class="header" ng-controller="Header" ng-hide="hideHeader"></div>
<div class="page" ng-view></div>

The .header has the same controller always, while the .page has different controllers based on the route.

The problem is that hideHeader is set in the controller for the current URL.

What would be the best way to tell the Header controller that the controller for the current route has changed the value of hideHeader?

I don't think setting it on the $routeScope is the right way. Also, most of the time, the header will be visible, there are very few pages that want to hide it.

Another idea is for that variable to be set in the config() method:

$routeProvider
.when('/',
{
    templateUrl:'views/hello.html',
    controller:'Hello',
    hideHeader:true
});

However, I am not sure that's a proper "AngularJS" way of doing that.

What's my best option?

Thank you.

Upvotes: 1

Views: 129

Answers (2)

m.e.conroy
m.e.conroy

Reputation: 3538

You could listen for the $locationChangeStart or $locationChangeSuccess events in your header controller and then hide it based on the change in the url.

http://docs.angularjs.org/api/ng.$location

From the AngularJS API Docs

$locationChangeStart

Broadcasted before a URL will change. This change can be prevented by calling   preventDefault method of the event. See ng.$rootScope.Scope#$on for more details about  event object. Upon successful change $locationChangeSuccess is fired.

Type:
broadcast

Target: 
root scope

Parameters
Param   Type    Details

angularEvent    Object    Synthetic event object.

newUrl: string    New URL

oldUrl:    (optional)    string    URL that was before it was changed.

EDIT

angular.module('myApp',[]).config(['$routeProvider',function($routeProvider){
    // application routes here
}).run(['$rootScope','$location',function($rootScope,$location){
    $rootScope.$on('$locationChangeStart',function(evt,newPath,oldPath){
        switch(newPath){
            case '/some/path':
            case '/some/other/path':
            case '/some/more/paths':
                $rootScope.$broadcast('header.hide');
                break;
            default:
                $rootScope.$broadcast('header.show');
                break;
        }
    });
}])
.controller('HeaderCtrlr',['$scope',function($scope){
    $scope.hideFlag = false;

    $scope.$on('header.hide',function(){
        $scope.hideFlag = true;
    });

    $scope.$on('header.show',function(){
        $scope.hideFlag = false;
    });
}]);

Upvotes: -1

KayakDave
KayakDave

Reputation: 24676

I'd lean towards a service since the Angular team states "having two controllers that want access to the same data is a classic sign that you want a service." (One of the places they mention this is in their Angular Best Practices discussion: http://www.youtube.com/watch?v=ZhfUv0spHCY). And they also discuss services being the right place for shared state (with thin controllers that are the "glue between views and services").

So, something either like this:

myApp.service('headerStatus', function () {
    var hideHeader = true;

    this.hideHeader = function() {
        return hideHeader;
    };

    this.setHeader = function(visibility) {
        hideHeader = visibility;
    };
});

Then there's a bunch of ways to tie into it, but here's a simple one:

myApp.controller('Header', function ($scope,headerStatus) {
   $scope.hideHeader = headerStatus.hideHeader();
});

And a fiddle of this: http://jsfiddle.net/Yxbsg/1/

Or potentially you could use a value:

myApp.value('headerStatus',true);

myApp.controller('Header', function ($scope,headerStatus) {
    $scope.hideHeader = headerStatus;
});

Upvotes: 2

Related Questions