Search code examples
data-bindingangularjslaravel-4angularjs-serviceangularjs-controller

AngularJS Binding, multiple controllers through a service, part of page rendered from php


I know this is long, but it appears something specific in what I'm doing in the more complex scenario is causing the issue. All simple examples I attempt work fine.

I have an application setup using angularjs and laravel four.

The home page is rendered via laravel routing:

Route::get('/', function() {
    return View::make('app');
});

app.php returns the website skeleton with the following structure:

<!doctype html>
<html lang="en" ng-app="app">
<head>


  <script src="/js/angular.js"></script>
  <script src="/js/angular-sanitize.js"></script>
  <script src="/js/angular-ui-router.js"></script>
  <script src="/js/app.js"></script>

</head>
<body>

    <div class="navbar navbar-inverse navbar-fixed-top" ng-controller="NavController">
        <div class="navbar-inner">
            <div class="container-fluid">
                    <button class="button pull-right" ng-click="logout()">Logout</button>
                    <p class="navbar-text pull-right" >
                        Logged in as <a href="#" class="navbar-link">{{ currentUser.email }} id is : {{ currentUser.userId }}</a>
                    </p>
                </div>
            </div>
        </div>
    </div>

  <div class="row-fluid offset05">
      <div id="view" ng-view></div>
    </div>
  </div>

</body>
</html>

basics of app.js:

var app = angular.module("app", ['ngSanitize','ui.state','ui.bootstrap']);

the user is initially routed to a login template, at which point a LoginController updates the user information which resides in UserService.

app.controller("LoginController", function($scope,$rootScope, $location, AuthenticationService, UserService) {

    $scope.credentials = { email: "", password: "" };

  $scope.login = function() {
    AuthenticationService.login($scope.credentials).success(function() {
      $location.path('/home');
    });
  };
});

Authentication Service updates the UserService variable appropriately:

app.factory("AuthenticationService", function($rootScope, $http, $sanitize, SessionService, FlashService, CSRF_TOKEN, UserService) {

  return {
    login: function(credentials) {
      var login = $http.post("/auth/login", sanitizeCredentials(credentials));
      login.success(function(data){

           UserService.currentUser = data.user;

    });

      return login;
    }
});

NavController (controller for navigation bar seen above) binds its scope to the UserService.currentUser.

app.controller("NavController",function($scope, UserService, AuthenticationService,$location){

    $scope.currentUser = UserService.getCurrentUser();

});

Relevant parts of UserService:

app.factory("UserService", function($http){

var _currentUser = {}

return{
    currentUser: _currentUser,

    getCurrentUser: function() {
        return _currentUser;}

    };

});

When the user logs in, their user email and userid should appear in the navigation bar.

If I create an example which strictly uses javascript/html there are no issues with the binding.

With the mechanics/structure mentioned above, the navbar does not respond to the changes in the UserService current user variable until the entire page is reloaded.

After the user logs in, I can verify that the UserController and UserService both update the currentUser appropriately. In spite of this, the NavController will not reflect the updated user unless I reload the whole page.

I assume this is becuase the NavController is now re-running with the updated information, but why isn't normal binding working?

I guess this has something to do with the fact that the navigation bar is loaded via php.

My question is how can I either: a) make the binding via the service work appropriately or b) reload the NavController when necessary (post login/logout)


Solution

  • There are a couple ways you can handle this.

    1. You can bind your $scope to the service itself, in which case any changes to that model will be picked up automatically
    2. You can observe changes to the service using $watch

    In this example, you can see both techniques: (http://plnkr.co/edit/bhBXMr?p=preview):

    var app = angular.module('plunker', []);
    
    app.controller('MainCtrl', function($scope, AuthenticationService, UserService) {
      $scope.user = UserService;
      $scope.login = function(){
        AuthenticationService.login();
      }
      // watch the service for changes to currentUser
      $scope.$watch(function(){
        return UserService.currentUser;
      }, function(currentUser){
        $scope.currentUser = currentUser;
      }, true);
    });
    
    app.service('AuthenticationService', function($http, UserService){
      return {
        login: function(){
          $http.get('data.json').success(function(data){
            UserService.currentUser = data;
          });
        }
      };
    });
    
    app.service('UserService', function($rootScope){
      return {
        currentUser: null
      };
    });
    

    HTML Markup:

    <body ng-controller="MainCtrl">
      <button ng-click="login()">Login</button>
      <div>Current User: {{user.currentUser}}</div>
      <!-- from the watch -->
      <div>Current User: {{currentUser}}</div>
    </body>