onlineracoon
onlineracoon

Reputation: 2970

Angular directive modifying scope

Basically I have 2 directives:

The radar-chart directives is instantiated by:

<radar-chart data="" labels="" options="" colours=""></radar-chart>

The user-radar-chart directive is instantiated by:

<user-radar-chart preseverence="20" motivation="39"></user-radar-chart>

Now the user-radar-chart should use the radar-chart directive but with a fixed set of labels, colours and options, only the data should be different.

How can I do this?

This is what I have tried:

angular
    .module('myApp')
    .directive('userRadarChart', function () {
        return {
            restrict: 'E',
            template: '<canvas class="chart radar-chart" data="{{data}}" labels="{{labels}}" options="{{options}}" colours="{{colours}}"></canvas>',
            scope: {
                motivation: '@',
                preseverence: '@'
            },
            link: function ($scope, $element, $attrs) {
                $scope.data = [$attrs.motivation, $attrs.preseverence];
                $scope.labels = ['Motivation', 'Preseverence'],
                $scope.options = {};
                $scope.colours = ['Yellow'];
            }
        }
    })

Upvotes: 0

Views: 138

Answers (2)

Ed_
Ed_

Reputation: 19098

There are several different ways of doing this. The simplest (which I think is what you are getting at with your example) way is to have the template of user-radar-chart create a radar-chart directive:

This example shows that userRadarChart is just a radar chart with the color option preset.

.directive('radarChart', function() {

  return {
    restrict: 'E',
    template: "<span>I am a {{color}} radar chart, with data:</span>\
               <pre>{{ data | json }}</pre>",
    scope: {
      data: "=",
      color: '@'
    },

    link: function (scope, element){
      element.css('background-color', scope.color); 
    }
  }
})

.directive('userRadarChart', function() {
  return {
    restrict: 'E',
    scope: { data: "=" },
    template: '<radar-chart data="data" color="green"></radar-chart>'
  }
});

angular.module('myApp', [])

.controller('MyController', function () {
  this.data1 = {
    foo: 'bar'
  }
  this.data2 = {
    dog: 'bone' 
  }
})

.directive('radarChart', function() {

  return {
    restrict: 'E',
    template: "<span>I am a {{color}} radar chart, with data:</span>\
               <pre>{{ data | json }}</pre>",
    scope: {
      data: "=",
      color: '@'
    },
  
    link: function (scope, element){
      element.css('background-color', scope.color); 
    }
  }
})

.directive('userRadarChart', function() {
  return {
    restrict: 'E',
    scope: { data: "=" },
    template: '<radar-chart data="data" color="green"></radar-chart>'
  }
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.6/angular.min.js"></script>

<div ng-app="myApp">
  <div ng-controller="MyController as ctrl">
    <radar-chart color="red" data="ctrl.data1"></radar-chart>
    <user-radar-chart data="ctrl.data2"></user-radar-chart>
  </div>
</div>

Note that watchers aren't required for the above because we are using = so the data value is taking advantage of angular's built in two way data binding.

Another option is to have the user-radar-chart settings stored inside the radar-chart directive, and just have a type attribute on the radar-chart which would configure the chart for you:

.directive('radarChart', function() {

  return {
    restrict: 'E',
    scope: {
      data: "=",
      color: '@'
    },

    link: function (scope, element, attrs){
      if (attrs.type === "user")
        // define user-radar-chart settings here.
        scope.color = 'green';
      element.css('background-color', scope.color);

    }
  }
});

There are many more ways of doing this, but if the subcharts you're making really are as simple as just changing settings, I think either of the above ways are how it should be done - keep it simple.

Upvotes: 1

Reactgular
Reactgular

Reputation: 54741

I'm not sure why you're using <canvas> in your template when you say you want to use <radar-chart>, but I'm going to guess this is the problem.

There is no abstraction of directives in Angular. You could use inheritance for the directive's controller, but the link function and the directive can not be abstracted.

You can use other Angular directives inside the template for a directive, and I think this is what you're missing.

angular
    .module('myApp')
    .directive('userRadarChart', function () {
        return {
            restrict: 'E',
            template: '<radar-chart data="{{data}}" labels="[\'Motivation\', \'Perseverance\']" options="{}" colours="[\'Yellow\']"></radar-chart>',
            scope: {
                motivation: '@',
                perseverance: '@'
            },
            link: function ($scope, $element, $attrs) {
                $scope.$watchGroup(['motivation','perseverance'],function(values) {
                    $scope.data = values;
                });
            }
    }
});

Keep in mind that you should try to use watchers when possible. If the values for motivation and perseverance are bound to an Angular expression, then the chart won't update if they change.

Using a $scope.$watchGroup executes the callback function with an array of values whenever they change. When you assign attributes to the scope using @ they already exist in the scope. So you shouldn't use $attrs.motivation to access them.

I moved your static parameters to the template. Try to keep style and display in the template, and business logic in the code. Makes it easier to maintain.

Upvotes: 1

Related Questions