AngularJS using factory method and shared objects with broadcast

Angular JS and Codeigniter

My RND with Angular JS in recent time has increased so I thought it would be a good idea to share a few more Angular JS tutorials. In this tutorial, we will be using the factory method of Angular JS to centrally handle shared objects. Divided again in two parts – this one deals with getting the data and setting up the broadcasts.

So, at first I was not sure about how to centrally handle the collection of objects when I was working with Angular JS and the documentation on their site didn’t help much. But thanks to Stackoverflow, I finally got what I wanted and so it’s better that I share that knowledge.

I am building on top of the learning-ci installation on which I have worked for the last 4 tutorials.

Let’s talk about the Code Igniter part first: I have added a new module “singlepage”, and a controller “singlepage” which will control the pages. The main page is “dashboard” which is getting all the css, js and other data resources. I have two more function one for the view and the edit page – both of them are just loading the view and doing nothing else.

Also I have included another controller inside the controller folder directly – json.php. Here I will have all the json calls (one reason for a central json controller is to control security issues at one instance – for example don’t display info even some one is not making an ajax call).

/**
   * This is the dashboard for the single page app
   */
  public function dashboard() {
//    meta data for the page
    $data['header']['title'] = 'Single page app for the Books sections';
      
//    adding the script
    $data['footer']['scripts']['angular.min.js'] = '';
    $data['footer']['scripts']['singlepage_module.js'] = 'singlepage';
    $data['footer']['scripts']['singlepage_app.js'] = 'singlepage';
      
//    supplying the data for the view
    $data['view_name'] = 'singlepage/singlepage_dashboard_view';
    $data['view_data'] = array(1,2,3);
  
    $this->load->view('page_view', $data);
  }
    
  /**
   * This page is taken by the view route
   */
  public function view() {
    $this->load->view('singlepage/singlepage_view_view');
  }
    
  /**
   * This page is taken by the edit route
   */
  public function edit() {
    $this->load->view('singlepage/singlepage_edit_view');
  }

Complete code can be found here

public function get_books_json() {
  $this->load->model('books/books_model','books');
  $data = $this->books->get();
  print json_encode($data);
  exit();
}

The two views are:

<div ng-app="singlePageModule">
 
<div class="row-fluid">
 
<div class="span12">
 
<div ng-view></div>
 
    </div>
 
  </div>
 
</div>
 
 
<div ng-controller="bookViewCtrl">
 
<h3>View the list of Books</h3>
 
 
<div class="row-fluid">
 
<div class="span12">
 
<div class="span7">
 
<table class="table table-bordered table-striped table-hover">
 
<tr>
 
<th>Book name</th>
 
 
<th>Price</th>
 
          </tr>
 
 
<tr ng-repeat="book in books">
 
<td>{{book.name}}</td>
 
 
<td>{{book.price}}</td>
 
          </tr>
 
        </table>
 
      </div>
 
    </div>
 
  </div>
 
</div>

Ok, now that the Code Igniter part is done, let us dive into the Angular JS part. I created two js files – one for the app and the other for the module. I find it best to keep the module code separate from the controller code just like we do in CI.

Here is the code for the module file:

var singlePageModule = angular.module('singlePageModule', []);
  
singlePageModule.config(['$routeProvider', function($routeProvider) {
    $routeProvider
    .when('/view',{templateUrl: base_url + 'singlepage/view', controller: singlePageModule.bookViewCtrl})
    .when('/edit/:id',{templateUrl: base_url + 'singlepage/edit', controller: singlePageModule.bookEditCtrl});
}]);
  
singlePageModule.factory('sharedBooks', ['$http', '$rootScope', function($http, $rootScope) {
  var books = [];
  
  return {
    getBooks: function() {
      return $http.get(base_url + 'json/get_books_json').then(function(response) {
        books = response.data;
        $rootScope.$broadcast('handleSharedBooks',books);
        return books;
      })
    },
    saveBooks: function() {}
  };
}]);

First we declare the module and set up the routes with the templates that we would be using and the controllers which will handle them. It’s the routes urls which are generated through the controller and that’s where I have only loaded the views.

Then it’s the factory method from Angular JS. The name of the shared object will be “sharedBooks” and this method is using the $http service and the $rootScope.

The whole function is returning two functions getBooks() and saveBooks(). Right now I have only written the getBooks function which is first making the http get call to the url specified and on response, we are populating the books object with the response data.

Once this is done, we use the broadcast functionality to all over that the books object has changed. This is very important for all the models inside the app to know that the main object has changed. The broadcast has a name and the final object.

singlePageModule.controller('bookViewCtrl', function($scope,$routeParams,sharedBooks) {
  $scope.name = 'bookViewCtrl';
  sharedBooks.getBooks().then(function(books) {
    $scope.books = books;
  });
  
  $scope.$on('handleSharedBooks', function(events, books) {
    $scope.books = books;
  })
});

Now let’s talk about the application js or the controller. Here right now I have only added the code to the “bookViewCtrl” controller. A very simple one as you can see… the main thing is that I am passing $scope, $routeParams and the sharedBooks object to the controller. Yes, sharedBooks is the same object which we created in the module file and that’s the one which is talking the json response using the http service.

Calling the getBooks function from the sharedBooks object we assigned the result to the $scope.books so that it’s available inside the view. And in the end, we assigned the broadcast event to $scope so that the scope object changes as soon as the object change anywhere using the factory method.

Angular baroadcast