Estilos en componente para D3.js no se muestran en angular 2


Estoy usando Angular 2 y D3.js. Quiero mostrar un rectángulo rojo.

Solo funciona si pongo estilos en el estilo .archivo css . Compruebe este plunkr

Cuando me pongo mis estilos en el componente styles: [], no funciona. Compruebe este plunkr

¿Cómo dejar que funcione cuando uso el componente styles: []? Gracias

ACTUALIZACIÓN: @micronyks proporciona una solución, pero hace que los estilos en el componente sean globales, básicamente no hay diferencia con escribiendo en estilo .archivo css . En este plunkr, muestra el estilo en un componente anulará los estilos de otro componente, por lo que no puede mostrar rectángulos verdes y rojos.

ACTUALIZACIÓN 2: El camino de @Günter resuelve perfectamente este problema!! Solo un recordatorio, a la manera de Günter: necesita al menos Angular beta 10. (Mis otros plunkrs utilizan Angular beta 8) La demostración de trabajo para verde y un rectángulo rojo usando Angular beta 12 es aquí.

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());
  }
}
Author: Hongbo Miao, 2016-03-25

6 answers

Visualización de la encapsulación.Emulado (por defecto)

Eso es por diseño. Angular agrega nombres de clase únicos a los componentes y reescribe los estilos agregados para que solo se apliquen a los componentes donde se agregaron.

D3 genera HTML dinámicamente sin el conocimiento de Angulars y Angular no puede aplicar las clases para hacer que los estilos se apliquen en el HTML generado.

Si agrega los estilos en el archivo HTML del punto de entrada, Angular tampoco reescribe los estilos y el ayudante agregado las clases no surten efecto.

Visualización de la encapsulación.Ninguno

Con encapsulation: ViewEncapsulation.None Angular no hace esta reescritura, por lo tanto el resultado es similar a agregar el HTML a index.html.

"Piercing de sombras"

Alternativamente, puede usar los combinadores CSS shadow piercing recientemente introducidos>>>, /deep/ y ::shadow (::shadow se acaba de sustituir por un y por lo tanto muy limitado). Ver también https://stackoverflow.com/a/36225709/217408 y el Émbolo

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

SASS

/deep/ funciona bien con SASS, pero el alias >>> no.

Los combinadores CSS de perforación de sombras son reescritos por Angular y no necesitan ser soportados por los navegadores. Chrome soportado por un tiempo, pero están en desuso-pero como se dijo, eso no importa, porque Angular reescribe para utilizar su encapsulación emulación.

Visualización de la encapsulación.Nativo

Angular no admite ninguna forma de estilizar dichos componentes desde el exterior. Solo si el navegador proporciona soporte como variables CSS, se pueden usar.

 36
Author: Günter Zöchbauer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:34:08

ViewEncapsulation solucionará tu problema.

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: []
})
 16
Author: micronyks,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-03-25 05:46:56

Ver Encapsulación

Esto se debe a la encapsulación de la vista en Angular 2. De forma predeterminada, todo el HTML y CSS se transforma para que solo se aplique localmente. En otras palabras, si agregas este estilo en el CSS de tu componente:

h2 { color: red; }

Solo afectará a los elementos h2 dentro del componente, no a todos los elementos h2 de toda la aplicación. Puede leer más sobre estos mecanismos en Documentación Angular en View Encapsulation.

¿por Qué ¿afectarte?

Angular transforma sus estilos pero como el gráfico C3 aún no está dibujado, tampoco puede transformar HTML/SVG. Debido a eso, los estilos de componente no coincidirán con los elementos dentro del gráfico C3.

¿Qué debo hacer?

Hoja de estilos externa

Las hojas de estilo externas no son transformadas por los mecanismos de Encapsulación de vista, por lo que afectarán de manera efectiva a su gráfico C3 (y a cualquier otro elemento).

Si está utilizando Angular CLI, agregar external stylesheet es realmente simple. Edite su archivo angular-cli.json y dentro de la propiedad apps encuentre la matriz styles. Agregue otra hoja de estilo aquí:

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

En caso de que no esté utilizando Angular CLI, debe haber alguna forma de agregar hojas de estilo externas. Probablemente la más simple es agregar otro <link …> dentro de <head> en su archivo index.html.

ViewEncapsulation.None

Su primera opción es: Crear un componente con el gráfico (y solo el gráfico) y desactivar la encapsulación de vistas dentro de él. Es una buena idea de hacer eso también por obedecer el Principio de Responsabilidad Única. Su gráfico, por diseño, debe estar encapsulado en un componente separado. Girar la encapsulación de la vista es tan simple como agregar otra propiedad a su decorador @Component:

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

/deep/ Selector de CSS

Si, por alguna razón, no quieres hacer eso, hay otra posibilidad. Puede intentar usar el selector /deep/ dentro de su CSS que fuerza los estilos hacia abajo en todas las vistas de componentes secundarios. Efectivamente, esto rompe la encapsulación y debería afectar su gráfico C3. Así, por ejemplo, puedes hacerlo en el archivo CSS de tu componente:

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

De cualquier manera, recomiendo leer la documentación antes mencionada sobre Ver Encapsulación en Angular 2 para entender por qué sucede esto y cómo funciona. Esta función se supone que le ayudará a escribir código, no causar problemas :) Este artículo puede ayudarte a entender cómo funciona: Ver Encapsulación en blog.thoughtram.io

 4
Author: Michał Miszczyszyn,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-02-12 14:52:34

...entonces no puedo mostrar un rectángulo rojo y uno verde... Problema vuelve

Creo que es alguna anulación, no sé cuánto de esto es cierto, pero creo que esto resuelve su problema.

Añadir child1-cmp, child1-cmp .bar por ejemplo:

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

Nota: además de encapsulation: ViewEncapsulation.None, como se menciona en micronyks.

Prueba

Plunker


O esto:

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

Prueba

Plunker


O esto usando la clase .chart1, .chart2, por ejemplo, si quieres.

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

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

Prueba

Plunker

 1
Author: Angel Angel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:25:57
 1
Author: Mihail,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-04-25 08:45:05

Encontré que * /deep/ .my-element-class funciona, pero por alguna razón, SOLO si el elemento padre svg está presente en la plantilla html (no cuando el elemento padre svg es creado al vuelo por d3).

Por ejemplo, la siguiente situación funcionaría:

Mycomponent.componente.html

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

Mycomponent.componente.css

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

Mycomponent.componente.ts

d3.select("svg#mygraph").append("circle").classed("my-element-class", true)
 ...
 0
Author: maia,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-10-13 19:19:23