javascriptangularjsjavascript-databinding

Two way binding issue for Angular book app example


I am an Angular noob trying to work through some examples that I find here and there. I found an interesting example here: http://kirkbushell.me/when-to-use-directives-controllers-or-services-in-angular/ that I have attempted to actually get running. For the most part it works as I would expect.

The service is definitely storing the books and adding the new book as expected, however the list of books output in the view (DOM) is not updating. It would appear that the two-way binding is not set correctly. What might I be doing wrong?

Here is the code that (mostly) works:

var module = angular.module('bookModule', []);

module.service('Book', ['$rootScope', function($rootScope) {
  var service = {
    books: [
      { title: 'Magician', author: 'Raymond E. Feist' },
      { title: 'The Hobbit', author: 'J.R.R Tolkien' }
    ],

    addBook: function(book) {
      service.books.push(book);
      $rootScope.$broadcast('books.updated');
    }
  };

  return service;
}]);


// Ctrl fn
var ctrl = ['$scope', 'Book', function($scope, Book) {
  $scope.$on('books.updated', function(event) {
    console.log(Book.books);
    $scope.books = Book.books;
  });

  $scope.books = Book.books;
}];
// Initialize the controller
module.controller('books.list', ctrl);


// Directive to handle add book button
module.directive('addBookButton', ['Book', function(Book) {
  return {
    restrict: 'A',
    link: function(scope, element, attrs) {
      element.bind('click', function() {
        Book.addBook({title: 'Star Wars', author: 'George Lucas'});
      });
    }
  };
}]);
<!DOCTYPE html>
<html>
  <head>
    <title>Book example</title>
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.11/angular.min.js"></script>
    <script src="script.js"></script>
  </head>
  <body ng-app="bookModule">
    <h4>Book list</h4>
    <ul ng-controller="books.list">
      <li ng-repeat="book in books">
        Title: {{book.title}} | Author: {{book.author}}
      </li>
    </ul>
    <button add-book-button>Add book</button>
  </body>
</html>

Thank you for your input!


Solution

  • Your click event is manually bound, and you are not using angular click directive, Which means you would need to run the digest cycle manually by using scope.$apply().

     element.bind('click', function() {
        Book.addBook({title: 'Star Wars', author: 'George Lucas'});
        scope.$apply();
      });
    

    var module = angular.module('bookModule', []);
    
    module.service('Book', ['$rootScope', function($rootScope) {
      var service = {
        books: [
          { title: 'Magician', author: 'Raymond E. Feist' },
          { title: 'The Hobbit', author: 'J.R.R Tolkien' }
        ],
    
        addBook: function(book) {
          service.books.push(book);
          $rootScope.$broadcast('books.updated');
        }
      };
    
      return service;
    }]);
    
    
    // Ctrl fn
    var ctrl = ['$scope', 'Book', function($scope, Book) {
      $scope.$on('books.updated', function(event) {
        console.log(Book.books);
        $scope.books = Book.books;
      });
    
      $scope.books = Book.books;
    }];
    // Initialize the controller
    module.controller('books.list', ctrl);
    
    
    // Directive to handle add book button
    module.directive('addBookButton', ['Book', function(Book) {
      return {
        restrict: 'A',
        link: function(scope, element, attrs) {
          element.bind('click', function() {
            Book.addBook({title: 'Star Wars', author: 'George Lucas'});
            scope.$apply();
          });
        }
      };
    }]);
    <!DOCTYPE html>
    <html>
      <head>
        <title>Book example</title>
        <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.11/angular.min.js"></script>
        <script src="script.js"></script>
      </head>
      <body ng-app="bookModule">
        <h4>Book list</h4>
        <ul ng-controller="books.list">
          <li ng-repeat="book in books">
            Title: {{book.title}} | Author: {{book.author}}
          </li>
        </ul>
        <button add-book-button>Add book</button>
      </body>
    </html>