0

I am writing my first non-trival Angular App and I have hit a snag with a directive. The directive takes data from a controller's scope and applies it to Google Chart. The chart is not the issue - which is to say it works fine with dummy data - it is access to the properties of the scope object which were obtained via http:

I am accessing data returned via an API in a service which utilizes $http:

dashboardServices.factory('SearchList', ['$http','$q',
  function($http, $q){
    return {
      getSearchDetails:function(searchType, resultType){

        return $http.get("api/searches/"+searchType+"/"+resultType)
        .then(function(response){
          if (typeof(response.data === 'object')) {

            return response.data;
          } else {
            return $q.reject(response.data);
          }
        },function(response){
          $q.reject(response.data);
        });


      }
    }
  }]);

In my controller, I am taking the response from this service and attaching to my scope via the promises' "then" method:

dashboardControllers.controller('DashboardCtrl', ['$scope',  'SearchList',
        function($scope,  SearchList){

            $scope.searchData = {};
            $scope.searchData.chartTitle="Search Result Performance"

            SearchList.getSearchDetails("all", "count").then(function(response){

                $scope.searchData.total = response.value; //value is the key from my API
            });
            SearchList.getSearchDetails("no_results", "count").then(function(response){ 
                $scope.searchData.noResults = response.value;
            });

        }]);

To an extent this works fine, i can then use the 2-way binding to print out the values in the view AS TEXT. Note: I want to be able to write the values as text as I am trying to use a single scope object for both the chart and the textual data.

{{searchData.total | number}}

As mentioned, I have written a directive that will print a specific chart for this data, in this directive ONLY the $scope.searchData.chartTitle property is accessible. The values that were set in the then functions are not accessible in the directive's link method:

Here is the directive:

statsApp.directive('searchResultsPieChart', function(){
    return{
        restrict : "A",
        scope:{
         vals:'@vals'
        },

        link: function($scope, $elem, $attr){

            var dt_data = $scope.vals;

            var dt = new google.visualization.DataTable();
            dt.addColumn("string","Result Type")
            dt.addColumn("number","Total")
            dt.addRow(["Successful Searches",dt_data.total]);
            dt.addRow(["No Results",dt_data.noResults]);


            var options = {};
            options.title = $scope.vals.title;

            var googleChart = new google.visualization.PieChart($elem[0]);
            googleChart.draw(dt,options)
        }
    }
});

Here is how I am using the directive in the view:

<div search-results-pie-chart vals="{{searchData}}"></div>

I can see that the issue is that the numeric values are not available to the directive despite being available when bound to the view.

Clearly the directive needs to be called later when these items are available or via some callback (or perhaps an entirely different approach), unfortunately i am not sure why this is the case or how to go about solving.

Any help would be greatly appreciated. I hope this makes sense.

picus
  • 1,507
  • 2
  • 13
  • 23
  • 2
    Not the root of your issue, but [avoid the deferred antipattern](http://stackoverflow.com/q/23803743/1048572) (in `getSearchDetails`)! – Bergi Oct 14 '14 at 23:05
  • 1
    To help you avoid it, $http already returns promises. See here under `general usage`: https://docs.angularjs.org/api/ng/service/$http – aarosil Oct 14 '14 at 23:31
  • yes, I had been using a different pattern tried the old deferred way - well, my old deferred way - to get see if there was any difference, there was not I have altered my code, thanks, guys – picus Oct 14 '14 at 23:50
  • @picus have you tried to setting a `$watch` in the directive to redraw the visualization when the scope variable gets updated? – aarosil Oct 16 '14 at 15:45

1 Answers1

0

I think the following will help you.

  1. First change the directive scope binding for vals to use = instead of @ (see this question for good explanation of the differences - basically @ interpolates the value whereas = binds to the variable in the parent scope)

  2. Then, move the part of the directive that creates the graph into a render function within your link function.

  3. Then, $watch vals for any changes, then call the render function with the new values

  4. You would also have to slightly change the approach of using ele[0], as you'll need to clear out the contents of it and add a new element with the new chart when the data changes (otherwise many charts will be added as the data changes!)

Here is an example of what to do in your link function with regard to the $watch and new render function (changing the $scope binding like I mentioned is not shown):

$scope.$watch('vals', function(newVals, oldVals) {
    return $scope.render(newVals);
}, true);

$scope.render = function (dt_data) {

    var dt = new google.visualization.DataTable();
    dt.addColumn("string","Result Type")
    dt.addColumn("number","Total")
    dt.addRow(["Successful Searches",dt_data.total]);
    dt.addRow(["No Results",dt_data.noResults]);

    var options = {};
    options.title = $scope.vals.title;

    var googleChart = new google.visualization.PieChart($elem[0]);
    googleChart.draw(dt,options)

}

Hope this helps you out!!!

Community
  • 1
  • 1
aarosil
  • 4,848
  • 3
  • 27
  • 41