Стили в компоненте для D3.js не отображаются в angular 2

Я использую Angular 2 и D3.js. Я хочу показать красный прямоугольник.

Он работает только в том случае, если я помещаю стили в файл style.css. Проверьте этот plunkr

Когда я помещаю свои стили в компонент styles: [], он не работает. Проверьте этот plunkr

Как это сделать, если я использую компонент styles: []? Благодаря

ОБНОВЛЕНИЕ: @micronyks предоставляет решение, но делает стили в компоненте глобальными, в основном не имеет никакого различия с записью в файле style.css. В этом plunkr он показывает, что стиль в одном компоненте будет переопределять другие стили компонентов, поэтому не может отображаться зеленые и красные прямоугольники.

ОБНОВЛЕНИЕ 2: @Günter отлично справится с этой проблемой!! Напомним, что для Günter: для него требуется не менее Angular beta 10. (Мои другие утилиты используют Angular beta 8). Рабочая демо для зеленого и одного красного прямоугольника с использованием Angular beta 12 - .

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());
  }
}

Ответ 1

Обновить

Angular и SASS договорились о поддержке ::ng-deep (вместо >>> или /deep/) некоторое время назад, пока ::slotted или что-либо еще, что делает его стандартом браузера, станет доступным во всех браузерах.

ViewEncapsulation.Emulated (по умолчанию)

Это по замыслу. Angular добавляет имена классов, уникальные для компонентов, и переписывает добавленные стили, чтобы применять их только к тем компонентам, где они были добавлены.

D3 генерирует HTML динамически без знания Angulars, и Angular не может применять классы, чтобы стили применяли к сгенерированному HTML.

Если вы добавляете стили в HTML файл точки входа, Angular также не переписывает стили, и добавленные вспомогательные классы не вступают в силу.

ViewEncapsulation.None

С encapsulation: ViewEncapsulation.None Angular не выполняет эту перезапись, поэтому результат аналогичен добавлению HTML в index.html.

"Тень-пирсинг"

В качестве альтернативы вы можете использовать недавно введенные CSS проколотые комбинаторы >>>, /deep/ и ::shadow (::shadow просто заменяется на и поэтому очень ограничен). Смотрите также fooobar.com/questions/252129/... и Плункер

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

SASS

/deep/ отлично работает с SASS, но псевдоним >>> - нет.

CSS-комбинаторы с добавлением теней переписаны Angular и не нуждаются в поддержке браузерами. Chrome некоторое время поддерживал их, но они устарели - но, как уже было сказано, это не имеет значения, потому что Angular переписывает их, чтобы использовать эмуляцию инкапсуляции.

ViewEncapsulation.Native

Angular не поддерживает какой-либо способ стилизации таких компонентов извне. Только если браузер поддерживает такие переменные, как CSS-переменные, их можно использовать.

Ответ 2

ViewEncapsulation устранит вашу проблему.

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: []
})

Ответ 3

Посмотреть инкапсуляцию

Это связано с инкапсуляцией представлений в Angular 2. По умолчанию все HTML и CSS преобразуются так, что применяются только локально. Другими словами, если вы добавите этот стиль в свой компонент CSS:

h2 { color: red; }

Это повлияет только на элементы h2 внутри компонента, а не на каждый элемент h2 во всем приложении. Вы можете прочитать больше об этих механизмах в документации Angular по View Encapsulation.

Почему это влияет на вас?

Angular преобразует ваши стили, но поскольку граф C3 еще не отрисован, он также не может трансформировать HTML/SVG. Из-за этого стили компонентов не будут соответствовать элементам в графе C3.

Что я должен делать?

Внешняя таблица стилей

Внешние таблицы стилей не трансформируются механизмами View Encapsulation, поэтому они эффективно влияют на вашу диаграмму C3 (и любой другой элемент в этом отношении).

Если вы используете Angular CLI, добавить внешнюю таблицу стилей очень просто. Отредактируйте ваш файл angular-cli.json и внутри свойства apps найдите массив styles. Добавьте еще одну таблицу стилей здесь:

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

Если вы не используете Angular CLI, должен быть какой-то способ добавить внешние таблицы стилей. Вероятно, самым простым является добавление еще одного <link …> внутри <head> в ваш файл index.html.

ViewEncapsulation.None

Ваш первый вариант: создать компонент с диаграммой (и только с диаграммой) и отключить внутри нее просмотр инкапсуляции. Это хорошая идея сделать это также из-за соблюдения принципа единой ответственности. Ваша диаграмма, по замыслу, должна быть заключена в отдельный компонент. Поворот View Encapsulation так же прост, как добавление другого свойства в ваш декоратор @Component:

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

/deep/ CSS селектор

Если по какой-то причине вы не хотите этого делать, есть другая возможность. Вы можете попробовать использовать /deep/ selector внутри вашего CSS, который заставляет стили переходить во все представления дочерних компонентов. По сути, это нарушает инкапсуляцию и должно повлиять на вашу диаграмму C3. Так, например, вы можете сделать это в файле CSS вашего компонента:

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

В любом случае, я рекомендую прочитать вышеупомянутую документацию по View Encapsulation в Angular 2, чтобы понять, почему это происходит и как это работает. Предполагается, что эта функция поможет вам писать код, а не доставлять неприятности :) Эта статья может помочь вам понять, как она работает: Посмотреть инкапсуляцию на blog.thoughtram.io

Ответ 5

... тогда я не могу показать один красный и один зеленый прямоугольник... Проблема возвращается

Я думаю, что это переопределение, я не знаю, насколько это верно, но я думаю, что это решает вашу проблему.

добавить child1-cmp, child1-cmp .bar, например:

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

Примечание: в дополнение к encapsulation: ViewEncapsulation.None, как указано micronyks.

Тест

Plunker


или это:

@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: []
})

Тест

Plunker


или это, используя класс .chart1, .chart2, например, если вы хотите.

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

    .chart2 .bar {
      fill: yellow;
    }
  `],
   ..//

Тест

Plunker

Ответ 6

Я обнаружил, что * /deep/ .my-element-class работает, но по какой-то причине ТОЛЬКО, если родительский элемент svg присутствует в шаблоне html (не тогда, когда родительский элемент svg создается на лету с помощью d3).

Например, будет работать следующая ситуация:

mycomponent.component.html

<svg id="mygraph"></svg> <!-- IMPORTANT!! -->

mycomponent.component.css

* /deep/ .my-element-class {
  /* ... desired styles */
}

mycomponent.component.ts

d3.select("svg#mygraph").append("circle").classed("my-element-class", true)
 ...