0

Background

I'm about to hook up my angular project to my first API endpoint. I've always created factories that are filled with fake JSON data, so this is new for me.

HTML

First Question: Lets say we have a two scopes created. 1) by a parent Controller and 2) by a directive's controller. If I inject both scopes with the same Page Service will the $GET request fire twice?

angular.module('yoangApp')
 .directive('searchbar', function(){
  return {
    restrict: 'A',
    templateUrl: 'template1.html',
    controller: function($scope, Pages) {
      $scope.adPageData = Pages;
      $scope.adpageLength = $scope.adPageData.length;
    },
    link: function postLink(scope) {}
   }
});
angular.module('yoangApp')
 .controller('PageCtrl', function ($scope, Pages) {
  $scope.adPageData = Pages;
 }

Page Factory:

Second Question Is this Factory properly written? I dont have access to the api yet I feel like there's a syntax error.

var app = angular.module('yoangApp');
app.factory('Pages', function($http) {

    var Pages = {};
    $http.get('/api/page').success(function(pages) {
      Pages = pages;
    });
   return Pages;
});

Sample Object from /api/page

   'pages': [
     {name: 'wow1', imageurl: 'images/image1.jpg'}, 
     {name: 'wow2', imageurl: 'images/image2.jpg'}
     ]
Armeen Moon
  • 18,061
  • 35
  • 120
  • 233

3 Answers3

1

I recommend that your Pages factory return an object with a single method making the http GET call:

var app = angular.module('yoangApp');
app.factory('Pages', function($http) {

    return {
        callApi: function(url, callback) {
            $http.get(url, callback);
        }
    };
});

It can then be used to set the adPageData scope property like so:

 angular.module('yoangApp')
     .controller('PageCtrl', function ($scope, Pages) {
        Pages.callApi('/api/page/', function(pagesResult) {
            $scope.adPageData = pagesResult;
        }
     });

This will resolve #1 because the GET call isn't called immediately, and cleans up the Pages factory for point #2.

Hayes
  • 848
  • 4
  • 10
  • could I store the callback object after callApi has been request once inside of the factory? – Armeen Moon Jun 19 '14 at 19:07
  • If I understand the purpose, you're just trying to get data from the REST API. If that is the case, you're passing a function to be called _when_ the api call is complete. By passing `function(pagesResult) {}` you're giving it a function to be called when the api call is complete. pagesResult will be populated with the data from the api. – Hayes Jun 19 '14 at 19:37
  • Here's a working example: http://plnkr.co/edit/lBFiC5G2gPuVSaoSBn4H I realized I forgot `.success` in my answer. – Hayes Jun 19 '14 at 19:55
0

Interesting Question.

For 1

The factory function will be called only once. From ng docs,

The service factory function generates the single object or function that represents the service to the rest of the application. The object or function returned by the service is injected into any component (controller, service, filter or directive) that specifies a dependency on the service.

So, the generated object/function will be then injected into the controllers. So it will not fire twice. All Services are singletons, they get instantiated once per app

For 2 - Ideally the function will return Pages even before the http returns a response. So the Pages will be always empty. I guess not the correct way to intantiate Pages.

Here is a demo which illustrates both the points.

Solution: And as a workaround, if you want the response to be updated automatically return an object from factory and access the pages from returned object as below.

var pages = {data:null};
$http.get(/url).success(function(data){
pages.data = data;
});
return pages;

Here is the updated demo to illustrate the same. The http call will still be made only once.

guru
  • 4,002
  • 1
  • 28
  • 33
  • Even with two way binding when the Pages factory updates, wont it update the DI too..? also how would I do a $get request and pass that one object between many controllers properly, in particular to a directive? – Armeen Moon Jun 19 '14 at 18:45
  • http://stackoverflow.com/questions/16227644/angularjs-factory-http-service does this question accually work as intended? – Armeen Moon Jun 19 '14 at 18:50
  • If you are returning an object(not primitive) from the factory, this will be shared across the controllers. so any update to the object will be reflected across the controllers as in here - http://plnkr.co/edit/pA4Fx6dyPWwoe2esr7Hx?p=preview – guru Jun 19 '14 at 18:52
  • Hey Guru can you tell me why the other answer isn't the same/better/worse than yours? I feel like they both have a pros and cons. – Armeen Moon Jun 19 '14 at 19:11
  • As you want to intantiate the Pages data only once the only problem with the other answer is that the http call will be called whenever you are invoking the function callapi in it. There are other ways to do it like using `$cachefactory` or even the http cache which angular provides just depends on what you need actually. As you are just starting with angularjs I would recomment to have a look at this - http://stackoverflow.com/questions/15666048/angular-js-service-vs-provider-vs-factory – guru Jun 19 '14 at 19:29
0

1 - Yes anything inside your Directive , that is bind to the link property , will fire once the page loads , If you haven't defiend any event like click OR mouseover OR ... to the element property of your directive .

** auto fire function : 

     link: function postLink(scope) {}
         console.log('I'm fired when page loads');
      }

** Binded to event function 

     app.directive('yourdirective',function(){
       return{
        restrict:"A" //OR "E"...
        link:function(scope,element,attributes){
          element.bind('click',function(){
            console.log('I'm fired when Someone clicks on this directive');
          });
        } 
       }

     })

2- I think that might work , BUT the conventional and preferred way to write a factory is like this :

       app.factory('Pages', function($http) {
           var Pages = {};
            return{
                getPages:function(){
                   $http.get('/api/page').success(function(pages) {
                       Pages = pages;
                        return Pages; 
                     });
                }
              }
       });
Milad
  • 27,506
  • 11
  • 76
  • 85
  • I'm just planning ahead I will have muliple directives having access to the Pages data. Some routes will not include all of these directives. I just want the Pages $http.get to only fire if the data has been updated or is null – Armeen Moon Jun 19 '14 at 19:16
  • I cant get your question I've defiend a factory , you can use it whenever you like , Eg: You can inject this factory to a directive , and say that directive how to use this factory . – Milad Jun 19 '14 at 19:20