Styles in component for D3.js do not show in angular 2

I am using Angular 2 and D3.js. I want to show a red rectangle.

It only works if I put styles in the style.css file. Check this plunkr

When I put my styles in the component styles: [], it does not work. Check this plunkr

How to let it work when I use the component styles: []? Thanks

UPDATE: @micronyks provides a solution, but it makes the styles in the component global, basically no difference with writing in style.css file. In this plunkr, it shows style in one component will override another component's styles, so cannot show green and red rectangles.

UPDATE 2: @Günter's way perfectly solve this problem!! Just a remind, for Günter's way: it needs at least Angular beta 10. (My other plunkrs use Angular beta 8) The working demo for green and one red rectangle using Angular beta 12 is here.

import {Component} from 'angular2/core'
@Component({
  selector: 'my-app',
  providers: [],
   styles: [`
    /*this does not work*/
    .bar {
      fill: red;
    }
  `],
  template: `
    <div>
      <svg class="chart"></svg>
    </div>
  `,
  directives: []
})
export class App {
  constructor() {}

  ngOnInit() {
    this.draw();
  }

  draw() {
    let data = [{name: 'A', value: 1}];
    let width = 400, height = 200;

    let x = d3.scale.ordinal().rangeRoundBands([0, width]);
    let y = d3.scale.linear().range([height, 0]);

    let chart = d3.select(".chart")
      .attr("width", width)
      .attr("height", height)
      .append("g");

    x.domain(data.map(function(d) { return d.name; }));
    y.domain([0, d3.max(data, function(d) { return d.value; })]);

    chart.selectAll(".bar")
      .data(data)
      .enter().append("rect")
      .attr("class", "bar")
      .attr("x", function(d) { return x(d.name); })
      .attr("y", function(d) { return y(d.value); })
      .attr("height", function(d) { return height - y(d.value); })
      .attr("width", x.rangeBand());
  }
}

Update

Angular and SASS agreed on supporting ::ng-deep (instead of >>> or /deep/) a while ago until ::slotted or whatever makes it into browser standards becomes available in all browsers.

ViewEncapsulation.Emulated (default)

That's by design. Angular adds class names unique to components and rewrites the added styles to only apply to the components where they were added.

D3 generates HTML dynamically without Angulars knowledge and Angular can't apply the classes to make the styles apply on the generated HTML.

If you add the styles at the entry point HTML file, Angular also doesn't rewrite the styles and the added helper classes don't take effect.

ViewEncapsulation.None

With encapsulation: ViewEncapsulation.None Angular doesn't do this rewriting, therefore the result is similar to adding the HTML to the index.html.

"Shadow-piercing"

Alternatively you can use the recently introduced shadow piercing CSS combinators >>>, /deep/ and ::shadow (::shadow is just replaced by a and thus very limited). See also https://stackoverflow.com/a/36225709/217408 and the Plunker

:host /deep/ div {
  color: red;
}

SASS

/deep/ works fine with SASS but the alias >>> doesn't.

The shadow-piersing CSS combinators are rewritten by Angular and they don't need to be supported by the browsers. Chrome supported them for a while but they are deprecated - but as said, that doesn't matter, because Angular rewrites them to use its encapsulation emulation.

ViewEncapsulation.Native

Angular doesn't support any way to style such components from the outside. Only if the browser provides support like CSS variables then these can be used.


ViewEncapsulation will fix your problem.

import {Component,ViewEncapsulation} from 'angular2/core'

@Component({
  selector: 'my-app',
  encapsulation: ViewEncapsulation.None,
  providers: [],
   styles: [`
     .bar {
       fill: red;
    }
  `],
  template: `
    <div>
      <svg class="chart"></svg>
    </div>
  `,
  directives: []
})

View Encapsulation

This is because of the view encapsulation in Angular 2. By default, all the HTML and CSS is transformed so that it's only applied locally. In other words, if you add this style in your component's CSS:

h2 { color: red; }

It will only affect h2 elements inside the component, not every h2 element in your whole app. You can read more about this mechanisms in Angular documentation on View Encapsulation.

Why does it affect you?

Angular transforms your styles but since the C3 graph is not yet drawn, it can't transform HTML/SVG too. Because of that, component styles won't match elements inside of C3 graph.

What should I do?

External stylesheet

External stylesheets are not transformed by the View Encapsulation mechanisms, so effectively they will affect your C3 chart (and any other element for that matter).

If you're using Angular CLI, adding external stylesheet is really simple. Edit your angular-cli.json file and inside of apps property find styles array. Add another stylesheet here:

{
    …
    "apps": [
        {
            …
            "styles": [
                "styles.scss",
                "c3.scss" // <---- add this or any other file
            ],
        }
    ],
    …
}

In case you're not using Angular CLI, there must be some way to add external stylesheets. Probably the simplest one is adding another <link …> inside of <head> in your index.html file.

ViewEncapsulation.None

Your first option is: Create a component with the chart (and only chart) and turn off View Encapsulation inside of it. It's a good idea to do that also because of obeying the Single Responsibility Principle. Your chart, by design, should be encapsulated in a separate component. Turning of View Encapsulation is as simple as adding another property to your @Component decorator:

@Component({
    …
    encapsulation: ViewEncapsulation.None
})

/deep/ CSS selector

If, for some reason, you don't want to do that, there's another possibility. You can try using /deep/ selector inside of your CSS which forces styles down into all child components views. Effectively, this breaks the encapsulation and should affect your C3 chart. So, for example, you can do that in your component's CSS file:

/deep/ .c3-chart-arc path {
    stroke: white;
}

Either way, I recommend reading the aforementioned documentation on View Encapsulation in Angular 2 in order to understand why this happens and how it works. This feature is supposed to help you write code, not cause troubles :) This article might help you understand how it works: View Encapsulation on blog.thoughtram.io


you can use

::ng-deep
.bar {
    fill: red;
}

Here you can read perfect article explaining the approach.

And... information from the Angular documentation


...then I cannot show one red and one green rectangle... The problem comes back

I think it's some override, I do not know how much of this is true, but I think this solves your problem.

add in child1-cmp, child1-cmp .bar for example:

@Component({
  encapsulation: ViewEncapsulation.None,
  selector: 'child1-cmp',
   styles: [`
    child1-cmp .bar {
      fill: red;
    }
  `],
  template: `
    <div>
      <svg class="chart1"></svg>
    </div>
  `,
  directives: []
})

Note: in addition to encapsulation: ViewEncapsulation.None, as mentioned by micronyks.

Test

Plunker


or this:

@Component({
  selector: 'my-app',
  directives: [Child1Cmp, Child2Cmp],
   encapsulation: ViewEncapsulation.None,
   styles: [`
    child1-cmp .bar {
      fill: red;
    }
  
    child2-cmp .bar {
      fill: yellow;
    }
  `],
   ..//

@Component({
  //encapsulation: ViewEncapsulation.None,
  selector: 'child1-cmp',
  template: `
    <div>
      <svg class="chart1"></svg>
    </div>
  `,
  directives: []
})

@Component({
  //encapsulation: ViewEncapsulation.None,
  selector: 'child2-cmp',
  template: `
    <div>
      <svg class="chart2"></svg>
    </div>
  `,
  directives: []
})

Test

Plunker


or this using class .chart1, .chart2, for example if you want.

@Component({
  selector: 'my-app',
  directives: [Child1Cmp, Child2Cmp],
   encapsulation: ViewEncapsulation.None,
   styles: [`
    .chart1 .bar {
      fill: red;
    }
  
    .chart2 .bar {
      fill: yellow;
    }
  `],
   ..//

Test

Plunker