Search code examples
javascriptangularjsangular-materialangular-directive

In Angular, dynamic color for a chip


It need to programmatically set the color of a chip in Angular Material.

Let's say that each chip shows an item defined as:

{
  name: 'the name',
  active: true
}

And the chip should be gray if it is not active and red if it is. The obvious way to do this would be:

<md-chips ng-model="data.items" readonly="true">
  <md-chip-template ng-class="$chip.active ? 'active' : ''">
    {{$chip.name}}
  </md-chip-template>
</md-chips>

But this won't work. It will only color the inside of the chip. It does not actually modify the md-chip tag, just the md-chip-template inside of it.

So why not use static chips? I could do:

<md-chips ng-model="data.items" readonly="true">
  <md-chip ng-repeat="chip in data.items" ng-class="$chip.active ? 'active' : ''">
    {{chip.name}}
  </md-chip>
</md-chips>

But that won't work either. You can't use ng-repeat inside of ng-chips. It just deletes everything you try to repeat.

So, I got to this idea. Use a directive that will update the parent of its elements on change!

<md-chips ng-model="data.items" readonly="true">
  <md-chip-template ng-class="$chip.active ? 'active' : ''" chip-style>
    {{$chip.name}}
  </md-chip-template>
</md-chips>

The directive chipStyle should look up its parent with the tag md-chip when its model is changed and copy to it whether it has the active style.

Is this a good idea? Even if it is, I have not been able to call a function bound to the directive when the data.items array changes.

Is what I'm trying to do possible? If so, how can I do it?

The idea is that when data.item[someIndex].active changes on $apply, the class of the md-chip (not md-chip-template!) changes too. Surely, it must be possible!

The function to run would look like:

if (element.hasClass('active')) {
  element.parents('md-chip').addClass('active');
} else {
  element.parents('md-chip').removeClass('active');
}

But I can't call it automatically when the data changes!


Solution

  • If I understood your question correctly, then you can definitely do what you want with a directive, but I agree it's a bit complicated.

    The way I could achieve it:

    HTML

    <md-chips ng-model="chips" readonly="true">
      <md-chip-template is-active active="{{$chip.active}}">
        <b>{{$chip.name}}</b>
      </md-chip-template>
    </md-chips>
    

    isActive directive

    app.directive('isActive', function() {
        var directive = {
            restrict: 'A',
            link: link
        };
    
        function link(scope, el, attrs) {
          attrs.$observe('active', function(val) {
            if (val === 'true')
              el.parent().parent().addClass('active');
            else
              el.parent().parent().removeClass('active');
          });
        }
    
        return directive;
    });
    

    Codepen demo: I'm using $timeout to change the active status of the first 2 chips, just to emulate that you can indeed change it and have their background changed as well.