Стили в компоненте для 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
Ответ 4
ты можешь использовать
::ng-deep
.bar {
fill: red;
}
Здесь вы можете прочитать прекрасную статью, объясняющую подход.
И... информация из угловой документации
Ответ 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.
Тест
или это:
@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: []
})
Тест
или это, используя класс .chart1
, .chart2
, например, если вы хотите.
@Component({
selector: 'my-app',
directives: [Child1Cmp, Child2Cmp],
encapsulation: ViewEncapsulation.None,
styles: [`
.chart1 .bar {
fill: red;
}
.chart2 .bar {
fill: yellow;
}
`],
..//
Тест
Ответ 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)
...