0

In an angular js application I have a table column defined in main.html as a clickable item. On clicking, it should go to a new customer page, carrying the value of the column cell as customer name. I have a corresponding service defined for the app module in main.js.

Customer page view receives the time series activity data for the selected / clicked customer and supposed to display various charts and tables for the customer. The view and its controller codes are also attached. Also attached are the REST API service.

I expect when the customer is clicked on the table column cell, the control should go to to the setClientFromSelection() under the corresponding service in main.js - where I had the console.log to print the client name - it is not going there!

Not sure what mistake I am making! Any pointer will be much appreciated.

main.html (relevant code):

<tr ng-repeat="data in clientdata track by data.attributes.client" ng-if="data.attributes.Status == statusColor">
<td><a ng-href="#customer" ng-click="setClientFromSelection(data.attributes.client)">{{data.attributes.client.split(".")[0]}}</a></td></tr>

main.js (relevant code):

'use strict';

angular.module('c3App')
    .controller('MainCtrl', ['$scope', 'ClientPerf', 'colorTransportService',
        function ($scope, ClientPerf, colorTransportService) {
            ClientPerf.get()
                .success(function(data) {
                    if (angular.isUndefined($scope.statusColor))  { $scope.statusColor = 'RED'; };
                    $scope.clientdata = data.payload.array;
                });

            $scope.$on('healthStatusClicked', function(e, color) {
                $scope.statusColor = angular.uppercase(color);
            });
    }])
    .service('clientTransportService', ['$rootScope', function($rootScope) {
        var client = '';

        var setClientFromSelection = function(clientName) {
            console.log("clientName: ", clientName);
            client = clientName;
            console.log("client: ", client);
            $rootScope.$broadcast('clientSelected', client);
        }

        var getSelectedClient = function() { return client; }

        return { 
            setClientFromSelection: setClientFromSelection, 
            getSelectedClient: getSelectedClient 
        };
    }])

clientDetails.html view:

<div class="col-lg-6 text-center">
    <div class="panel panel-default" ng-style="{'width': '100%'}">
        <div class="panel-heading">
            <h3 class="panel-title">Usage<linechart data="dailyUsageData" options="dailyUsageOptions" mode=""></linechart></h3>
        </div>
        <div id="customer_activities_graph" ng-style="{'width': '97%'}"></div>
    </div>      
    <div class=" panel panel-default" ng-style="{'width': '100%'}">
        <div class="panel-heading">
            <h3 class="panel-title">{{client}} Timeline</h3>
        </div>
        <div id="container5" ng-style="{'width': '95%', 'margin-left': '2%'}"></div>
    </div>
</div>

customer.js controller relevant code:

'use strict';

angular.module('c3App')
    .controller('CustomerCtrl', ['$scope', 'DailyUsage4Client', 'clientTransportService',
        function ($scope, DailyUsage4Client, clientTransportService) {
            $scope.dailyUsageData = [];
            $scope.dailyUsageOptions = {
                axes: {x: {type: "date", key: "x"}, y: {type: "linear"}},
                series: [
                    {
                      y: "value",
                      label: "Activity Count",
                      color: "#ff7f0e",
                      type: "column",
                      axis: "y"
                    }],
                tooltip: {
                    mode: "scrubber",
                    formatter: function (x, y, series) {
                          return moment(x).fromNow() + ' : ' + y;
                        }
                    },
                stacks: [],
                lineMode: "linear",
                tension: 0.7,
                drawLegend: true,
                drawDots: true,
                columnsHGap: 5
            };
            DailyUsage4Client.get()
                .success(function (data) {
                    if (angular.isUndefined($scope.client)) { $scope.client = 'servicemax.com'; };
                    var dailyUsage = data.payload.array;
                    for(var k = 0; k < dailyUsage.length; k++) {
                        $scope.dailyUsageData.push({
                            date: new Date(dailyUsage[k].attributes.dt.toString().replace(/(\d{4})(\d{2})(\d{2})/, "$2/$3/$1")),
                            value: dailyUsage[k].attributes.activities
                        });
                    };
                });

            $scope.$on('clientSelected', function(e, client) {
                $scope.client = client.split(".")[0];
            });
    }]);

For sake of completeness, I have the Rest call defined as below:

angular.module('ClientServices', ['ngResource'])
    .config(function ($httpProvider) {
        $httpProvider.defaults.headers.post['Content-Type'] = 'application/json';
        $httpProvider.defaults.cache = false;
    })
    .factory("DailyUsage4Client", function($http, $rootScope) {
        return {
            get: function() { return $http.get('http://c3.captora.com/c3rests/c3/usageByDay/{{client}}'); }
        }
    });
user1931485
  • 23
  • 1
  • 8

3 Answers3

0

clientTransportService is a factory (returns an object) but is used as a service. Try changing it to a factory...

.factory('clientTransportService', ['$rootScope', function($rootScope) {

    // ...

    return { 
        setClientFromSelection: setClientFromSelection, 
        getSelectedClient: getSelectedClient 
    };
}])

Or modify it to become a service...

.service('clientTransportService', ['$rootScope', function($rootScope) {
    var client = '';

    this.setClientFromSelection = function(clientName) {
        console.log("clientName: ", clientName);
        client = clientName;
        console.log("client: ", client);
        $rootScope.$broadcast('clientSelected', client);
    }

    this.getSelectedClient = function() { return client; }
}])

The controller also needs to add setClientFromSelection() to the scope...

$scope.setClientFromSelection = clientTransportService.setClientFromSelection;
Anthony Chu
  • 37,170
  • 10
  • 81
  • 71
  • Thank you for your advice. I changed the service to factory to return value and also added the setClientFromSelection in the customer.js controller. Unfortunately no changes in the result. – user1931485 Jul 25 '14 at 05:38
  • Hard to tell the structure of your view but i looks like `setClientFromSelection` should be added to the scope on the main controller (need to inject `clientTransportService`) – Anthony Chu Jul 25 '14 at 05:42
  • nah! still didn't help. – user1931485 Jul 26 '14 at 04:10
  • You're going to have to do a bit more debugging and post more details of the problem. – Anthony Chu Jul 26 '14 at 04:22
  • It seems there are couple of issues as far as I can tell after inserting debug statements in different areas of the code. The MainCtrl is properly setting the client name from the click and broadcasting (hopefully - don't know how to confirm) it. but the $scope.on in the CustomerCtrl is not firing to pick it up. Also, the Rest call that needs the client name as a parameter is throwing an error. Can't figure out either one – user1931485 Jul 27 '14 at 03:24
0

Change your clientTransportService to a factory because it is returning an object. Or else change your service definition to make it a service as answered by Anthony Chu.

That's because when you inject a service, you will be provided with an instance of the function passed in the service definition.

When you inject a factory, you will be provided with value that is returned by invoking the function you passed in the factory definition.

See this full answer on service vs. factory vs. provider

Community
  • 1
  • 1
Raghavendra
  • 5,281
  • 4
  • 36
  • 51
0

You are making life unncessarily hard on yourself by not taking advantage of a decent routes scheme. Consider creating a route such as the following in your app.js file (or wherever you're configuring your main app module):

$routeProvider.when('/customers/:customer_id', {
        templateUrl : '/views/customer.html',
        controller : 'CustomerCtrl'
});

You can therefore simply link to the correct customer id.

In your controller, you can retrieve that id from $routeParams, like so:

angular.module('yourApp').controller('OrganisationCtrl', function ($scope, $log, $window, $routeParams, $filter, kometContent, kometSystem, $location, $sce, $alert) {
    ($scope.getCustomer = function(id) {
        kometContent.customer(id).then(function (response) {
            $scope.customer = response;
        }, function (response) {
            $scope.$emit(kometSystem.error, 'Server error ' + response.status);
        });
    })($routeParams.customer_id);
});

Note that we're getting the id to pass into that self-running anonymous method via $routeParams. This is what we mean when we talk about convention over code. Isn't that a lot easier? Now, we can simply link to '/customers/:customer_id' (for example, '/customers/17') without any special handling.

Avram Score
  • 3,927
  • 2
  • 17
  • 12
  • pardon my naivety. What is kometContent and kometSystem? – user1931485 Jul 26 '14 at 03:59
  • I re-adpated actual code I have in use. komeContent is a service that performs REST transactions, and kometSystem is to hold API routes. I didn't bother changing them, since they aren't relevant to the discussion of using a route param. – Avram Score Jul 28 '14 at 02:03