¿Cómo aplicar clases CSS a otro componente en AngularDart?

8

Digamos que hay un marco simple para mostrar ventanas emergentes:

@Component(
  selector: 'popup-host',
  template: '''
      <div class="popup-container">
        <ng-template #popupRef></ng-template>
      </div>
  ''',
  styles: ['.popup-container { position: absolute; top: 100; left: 100; z-index: 100; }'],
)
class PopupContainerComponent {
  final PopupController _controller;
  final ComponentLoader _loader;

  PopupContainerComponent(this._controller, this._loader);

  void ngOnInit() {
    _controller.container = this;
  }

  @ViewChild('popupRef', read: ComponentRef)
  ComponentRef popupRef;

  void render(PopupConfig config) {
    final componentRef = _loader.loadNextTo(config.factory, popupRef);
    if (componentRef.instance is HasValueSetter) {
      componentRef.instance.value = config.value;
    }
  }
}

@Injectable()
class PopupController {
  PopupContainerComponent _container;
  set container(PopupContainerComponent container) => _container = container;

  void showPopup(PopupConfig config) {
    container.render(config);
  }
  ...
}

class PopupConfig {
  final ComponentFactory factory;
  final dynamic value;
  PopupConfig(this.factory, [this.value]);
}

abstract class HasValueSetter {
  set value(dynamic value);
}

Esto se puede usar así:

// Somewhere in the root template
<popup-host></popup-host>

// In popup.dart
@Component(
  selector: 'happy-popup',
  template: '''
      <div class="header">This is the popup content.</div>
      <div class="main">The value is {{value}}.</div>
      <div class="footer">I am happy!</div>
  ''',
)
class HappyPopupComponent implements HasValueSetter {
  @override
  dynamic value;
}

// In some_other.dart
@Component(
  ...
  styles: [
    '.header { font-weight: bold }',
    '.main { color: red }',
    '.footer { color: green; font-style: italic }',
  ],
  ...
)
class SomeOtherComponent {
  final PopupController _popupController;
  ...
  SomeOtherComponent(this._popupController, ...) ...;

  void displayPopup() {
    _popupController.showPopup(HappyPopupComponentNgFactory, 42);
  }
}
...

¿Hay alguna forma de reenviar estilos desde <some-other-component>hasta <happy-popup>sin tener que definirlos en la raíz de la aplicación?

Sergiy Belozorov
fuente

Respuestas:

5

Puede lograr esto dividiendo el código de sus componentes en archivos separados, o en un archivo CSS separado en su caso.

En lugar de escribir el estilo directamente en los componentes styles, importaría el archivo CSS utilizando styleUrls. De esa manera puede pasar un archivo (s) con sus estilos, y el archivo se puede compartir entre múltiples componentes.

@Component(
      styleUrls: ['./hero1.css', './folder/hero2.css'],
)

Tenga en cuenta que las URL en styleUrls son relativas al componente.

Otro beneficio de importar el CSS con styleUrlses que también le otorga la posibilidad de utilizar las importaciones dentro de ese archivo.

hero1.css

@import './folder/hero2.css';

FYI: Es una práctica común dividir el código de sus componentes en archivos separados.

  • hero.dart
  • hero.css
  • hero.html

Y luego, en su archivo dart, haga referencia a ellos como:

@Component(
      templateUrl: './hero.html',
      styleUrls: ['./hero.css'],
)

Consulte AngularDart - Estilos de componentes para obtener información breve sobre esto.

Dino
fuente
Gracias. Era consciente del hecho de que podía separar las hojas de estilo en un archivo separado, pero no pensé en reutilizarlas en múltiples componentes. Sin embargo, no estoy seguro de si esta solución es lo suficientemente universal, por ejemplo, un componente emergente podría ser parte del marco e importar partes de la aplicación al marco puede ser contrario a las buenas prácticas de software. Idealmente, el marco proporcionaría una forma de importar un archivo CSS arbitrario proporcionado a través de PopupConfig. ¿Es posible personalizar la styleUrlspropiedad en un componente en tiempo de ejecución?
Sergiy Belozorov
Además, ¿qué pasa si hay varios componentes que desean abrir la misma ventana emergente, pero darle un estilo diferente?
Sergiy Belozorov
1
Sí, puede configurarlo styleUrlsdinámicamente en el tiempo de ejecución. Hay múltiples formas de hacerlo. Le sugiero que busque para Angular dynamic styleUrlsobtener algunas ideas y ver cuál le queda mejor. En cuanto a la segunda pregunta. Si ese es el caso, importaría el archivo de estilo emergente compartido + el archivo de estilo donde sobrescribo los estilos que quiero cambiar para un componente específico.
Dino
No estoy seguro de entender su sugerencia sobre cómo diseñar dos ventanas emergentes de manera diferente. Ambas ventanas emergentes usarían el mismo componente, por lo que la única forma en que puedo sobrescribir los estilos de un archivo compartido es agregando una nueva entrada styleUrlsen tiempo de ejecución, ¿no? ¿Qué sucede si aparecen dos ventanas emergentes al mismo tiempo, por ejemplo, una abre otra y necesitan un estilo diferente? Si modifico el styleUrls, ¿no sería el estilo de ambos? Finalmente, no pude encontrar un buen artículo sobre cómo modificar dinámicamente styleUrlssin hacks. Parece que Angular no lo admite de forma nativa.
Sergiy Belozorov
1

Como su ventana emergente no es un elemento secundario del componente que lo abrió, no puede usar :: ng-deep

lo único que creo que funcionará es eliminar la encapsulación de vista del host, la ventana emergente y el componente que abre la ventana emergente (pruebe solo la ventana emergente y el componente que abre la ventana emergente primero, si eso no funciona, elimine la encapsulación del host también.)

@Component(
  selector: 'happy-popup',
  template: '''
      <div class="header">This is the popup content.</div>
      <div class="main">The value is {{value}}.</div>
      <div class="footer">I am happy!</div>
  ''',
  encapsulation: ViewEncapsulation.None // <=== no encapsulation at all
)
class HappyPopupComponent implements HasValueSetter {
Ron
fuente
¿Puede explicar qué quiere decir con "eliminar encapsulación de vista"?
Sergiy Belozorov
@SergiyBelozorov Hecho :). avísame si te ayudó
Ron
Ciertamente me ayudó a aprender algo nuevo sobre Angular y cómo se pueden configurar los componentes :-), pero no estoy seguro de que este sea el mejor enfoque. Con nombres de clase genéricos como header, mainy footeruno puede diseñar fácilmente dichos componentes de manera incidental. Quizás uno pueda intentar usar nombres más específicos para evitar conflictos, pero preferiría una solución que mantenga la encapsulación.
Sergiy Belozorov
Entiendo su preocupación, pero esa es la única solución a sus requisitos. Como dijiste, uno puede tratar de usar nombres y clases más específicos, tal vez usar el enfoque BEM
Ron
1
La desactivación ViewEncapsulationes un gran no-no para aplicaciones de tamaño medio o grande. Tiene especial cuidado o todo su estilo se verá afectado a largo plazo. (Ej: nombres de clase genéricos). Y no estoy de acuerdo con que sea la única solución a sus requisitos.
Dino
0

Puedes combinar scss con tu código

Primero debe separar el archivo scss que desea compartir en la aplicación

Por ejemplo:

En style1.scss

.header { font-weight: bold }

Luego en style2.scss

@import "style1"

O puede combinar la lista de archivos scss en su código de componente definiendo en la lista de matriz

styleUrls: ['./style1.scss', './style2.scss'],
  • Nota: Cambie la ruta de acuerdo con su ruta de archivo y esto solo funciona cuando usa scss no css

Así es como puede configurar manualmente el soporte de scss para angular cli

En angular.json agregar

"projects": {
        "app": {
            "root": "",
            "sourceRoot": "src",
            "projectType": "application",
            "prefix": "app",
            "schematics": { // add this config
                "@schematics/angular:component": {
                    "style": "scss"
                }
            },
Tony Ngo
fuente
Sí, de hecho utilizamos scss y @importdeclaraciones. Pero al igual que la respuesta de @Dino, me pregunto si es posible no tener que codificar la URL del estilo en el componente emergente, sino leerlo desde el PopupConfigy, de alguna manera, asignarlo en tiempo de ejecución.
Sergiy Belozorov
0

Puede enviar el valor de prop al componente, que puede ser una clase personalizada y ponerlo en su popop html. Y luego, en el archivo scss, agregue anulaciones css adicionales para una clase específica. Entonces, para cada componente personalizado, puede tener un código CSS personalizado.

PD: Y sí, sugeriría importar un archivo scss como:

@Component(
      styleUrls: ['./hero1.css'],
)

Es mejor separar css de js + su código css, entonces puede ser mucho más largo y contener todos los casos de estilo.

Andris
fuente
0

Además de los métodos ya mencionados, sugeriría dos vías más para explorar:

  1. desde el ámbito angular de CSS a los componentes y nos gustaría mantenerlo de esta manera, el límite del componente de cruce se puede hacer descubriendo qué alcance Angular le asignó y agregando CSS de ámbito manual en global <style> etiqueta en la página:

    @ViewChild ('popupRef') popupRef; ngAfterViewInit () {this.popupRef.nativeElement.attributes [0] .name // esto tendrá un valor similar al _ngcontent-tro-c1que necesitarás para abarcar todo tu CSS personalizado. }

Un inconveniente aparente de este enfoque es que Angular oculta la administración de CSS y, por lo tanto, tendrá que recurrir a JS para administrarlo. ( un ejemplo aquí )

  1. puede intentar definir el CSS en @Component antes de crearlo utilizando una fábrica de decoradores personalizados como en esta respuesta

Personalmente, exploraría la segunda opción, ya que parece ser menos hacky

timur
fuente