1

I am new to Angular. I have a simple web page, but the codes for controller are not working. It seems I do not call or inject service "ListLogsFactory" in the controller properly. Please help. Thank you.

My codes include a module, service, and controller that all declared/defined as follows:

    var myApp = angular.module("ListLogsModule", []);


    myApp.factory('ListLogsFactory', function ($http) {
            var thisPageNumber = 1;
            var thisPageSize = 10;
            var baseUrl = '../Api/LogApi/GetLogsByPage';

            var items = {};

            $http({
                method: 'GET',
                url: baseUrl,
                data: $.param({ pageNumber: thisPageNumber, pageSize: thisPageSize })
            })
            .success(function (data, status, headers, config) {
                items = data;
                console.log(items);
            })
            .error(function (data, status, headers, config) {
                alert('error: ' + status);
            });

            function getData() {
                return items;
            }
    });

    // The error is seen in FireFox and happens in the controller code:
    myApp.controllers.ListLogsController = function ($scope, ListLogsFactory) {
       $scope.logs = ListLogsFactory.getData(); // NOTE: this line throws error on running with something like "ListLogsFactory" is undefined
   }
CharlesB
  • 75,315
  • 26
  • 174
  • 199
Thomas.Benz
  • 7,303
  • 8
  • 33
  • 55

3 Answers3

3

When you use factory you have to return something. You're just defining a bunch of methods there but they aren't available to anybody.

It's also good to use a different naming convention. For example, instead of LogsController, use LogsCtrl. AngularJS appends "Controller" internally and you might end up, in exotic situations, handling names like "LogsControllerController".

A simplified approach of using factory and returning the service:

var ListLogsModule = angular.module("myApp", []);

ListLogsModule.factory('ListLogsSrv', function ($http) {
    // first define the service (you're using a factory)
    var getData = function() {
        return "hey";//items;
    };

    // then return it.
    // offer a public method "getData" that uses your internal getData()
    return {
        getData : getData
    }
});

ListLogsModule.controller("ListLogsCtrl", function ($scope, ListLogsSrv) {
    $scope.w = "world";
    $scope.logs = ListLogsSrv.getData(); 
});

You also have an $http request in the factory. That means that you'll trigger the async request when you instantiate the service (when it is used for the first time), so nobody will wait for it to finish and you'll be getting undefined. If you are using this service in a controller, you will probably need to resolve a promise.

An example of using a promise:

var promise = $q.defer();
var thisPageNumber = 1;
...
var baseUrl = '../Api/LogApi/GetLogsByPage';
...
promise = $http.get(...

Now you can use this promise in the controller, for example, or in the methods of your service.

I answered a related question two days ago Angular Service Definition: service or factory

Community
  • 1
  • 1
Eduard Gamonal
  • 7,883
  • 5
  • 37
  • 42
  • Thank you for your answer. I apply your approach of defining a public method in "service", and the error of "ListLogsFactory is undefined" has been gone. However, I cannot see the data bindings inside my view page. How can I use the "promise" in the controller so the data bindings are deferred until the data returned from the remote server is ready? On server side log, I did see the API RESTful method has been called and data return. – Thomas.Benz Jul 10 '13 at 14:18
  • probably like this http://markdalgleish.com/2013/06/using-promises-in-angularjs-views/ the easy way is having the controller in `$routeProvider` and using `resolve` – Eduard Gamonal Jul 10 '13 at 15:04
1
myApp.controllers.ListLogsController = function ($scope, ListLogsFactory) {
       $scope.logs = ListLogsFactory.getData();
}

should be

myApp.controller("ListLogsController", function ($scope, ListLogsFactory) {
       $scope.logs = ListLogsFactory.getData();
});
dnc253
  • 38,347
  • 36
  • 135
  • 148
  • Thank for your your reply. I tried to use your alternate approach, but I still get the same error in FireFox. The error says "Error: ListLogsFactory is undefined". Any idea? – Thomas.Benz Jul 09 '13 at 21:30
  • @Thomas.Benz would add a console.log() in there to be sure the updated code has been read (ctrl+F5 I believe forces a refresh too) – shaunhusain Jul 09 '13 at 21:49
1

The following is extended the suggestions from @Eduard Gamonal to make variables/methods of angular service or factory, so it is considered a trick to remember syntax of angular service or factory.

Trick to memorize syntax of "service" or "factory" in Angular

Service is tied with "service" key word; "this" key word to make function instance members public; and "=" to make assignments to "function instance members".

Factory is tied with "factory" key word; "return" keyword to make/return a public object; and ":" to all key/value pairs assignments.

Details.

Service deals with "variables" (or "instance members"), and to make them "public", I use the "this" keyword, and because a service deals with "variables" (or "instance members") to-be-public we use "=" after a "variable" name.

"getLogs" can be treated like a "public variable" or "instance member", and written (in "assignment" meaning) like this.getLogs = function() {...}.

And the whole service is defined with the "service" key word:

<script type="text/javascript">
    var myApp = angular.module("ListLogsModule", []);

    myApp.service('ListLogsService', function () {


            this.getLogs = function () {
                var logs = [
                            {"LogId":5405,"RecordedDate" : "2012-11-19T14:22:02.247", "Event" : "Log On"},
                             {"LogId":5416,"RecordedDate" : "2012-11-19T14:55:02.247", "Event" : "Log Out"}
                ];
                return logs;
            }
    });


    myApp.controller('ListLogsCtrl', function ($scope, ListLogsService) {
        $scope.logs = ListLogsService.getLogs();               
    });  

</script>

Factory deals with a returned "object" and to make them "public", I use the "return" keyword, and because a factory deals with "object" to-look-like-JSON-object I use ":" after each "property" name inside { } of the "return" statement.

"getLogs" can be treated like a property (or key) of the returned JSON object, and is written (in "key/value" pair) like getLogs : function() {...}.

And the whole factory is defined with the "factory" key word:

<script type="text/javascript">
    var myApp = angular.module("ListLogsModule", []);

    myApp.factory('ListLogsFactory', function () {


            return {
                getLogs: function () {
                    return[
                                {"LogId":5405,"RecordedDate" : "2012-11-19T14:22:02.247", "Event" : "Log On"},
                                 {"LogId":5416,"RecordedDate" : "2012-11-19T14:55:02.247", "Event" : "Log Out"}
                    ];
                }
            }
    });


    myApp.controller('ListLogsCtrl', function ($scope, ListLogsFactory) {
        $scope.logs = ListLogsFactory.getLogs();
    });  

</script>

In summary: To memorize syntax of "service" or "factory" in Angular

Service is tied with "service" key word; "this" key word to make function instance members public; and "=" to make assignments to "function instance members".

Factory is tied with "factory" key word; "return" keyword to make/return a public object; and ":" to all key/value pairs assignments.

Thomas.Benz
  • 7,303
  • 8
  • 33
  • 55