Como aplicar classes CSS a outro componente no AngularDart?

8

Digamos que exista uma estrutura simples para exibir pop-ups:

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

Isso pode ser usado assim:

// 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);
  }
}
...

Existe uma maneira de encaminhar estilos de <some-other-component>para <happy-popup>sem precisar defini-los na raiz do aplicativo?

Sergiy Belozorov
fonte

Respostas:

5

Você pode conseguir isso dividindo o código dos componentes em arquivos separados - ou em um arquivo CSS separado no seu caso.

Em vez de escrever o estilo diretamente no componente - styles, você importaria o arquivo CSS usando o styleUrls. Dessa forma, você pode passar um arquivo com seus estilos e o arquivo pode ser compartilhado entre vários componentes.

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

Lembre-se de que os URLs em styleUrls são relativos ao componente.

Outro benefício de importar o css styleUrlsé que ele também concede a capacidade de usar importações nesse arquivo.

hero1.css

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

FYI: É uma prática comum dividir o código dos componentes em arquivos separados.

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

E então no seu arquivo dardo referencie-os como:

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

Consulte AngularDart - Estilos de componentes para obter informações breves sobre isso.

Dino
fonte
Obrigado. Eu sabia que podia separar folhas de estilo em um arquivo separado, mas não pensava em reutilizá-las em vários componentes. Não tenho certeza se essa solução é universal o suficiente, por exemplo, um componente pop-up pode fazer parte da estrutura e importar partes do aplicativo para a estrutura pode ser contrário às boas práticas de software. Idealmente, o framework forneceria uma maneira de importar um arquivo css arbitrário fornecido via PopupConfig. É possível personalizar a styleUrlspropriedade em um componente em tempo de execução?
Sergiy Belozorov 03/12/19
Além disso, e se houver vários componentes que desejam abrir o mesmo pop-up, mas estilizá-lo de maneira diferente?
Sergiy Belozorov 03/12/19
1
Sim, você pode definir o styleUrlsdinamicamente no tempo de execução. Existem várias maneiras de fazer isso. Sugiro que você procure Angular dynamic styleUrlsalgumas idéias e veja qual delas se encaixa melhor em você. Em relação à segunda questão. Se esse for o caso, eu importaria o arquivo de estilo pop-up compartilhado + o arquivo de estilo em que sobrescrevi os estilos que desejo alterar para um componente específico.
Dino
Não sei se entendi sua sugestão sobre como criar dois pop-ups de maneira diferente. Os dois pop-ups usariam o mesmo componente; portanto, a única maneira de substituir os estilos de um arquivo compartilhado é anexando uma nova entrada styleUrlsno tempo de execução, não é? E se dois pop-ups aparecerem ao mesmo tempo, por exemplo, um abre outro e precisam ser estilizados de maneira diferente? Se eu modificar o styleUrls, não seria o estilo de ambos? Por fim, não consegui encontrar um bom artigo sobre como modificar dinamicamente styleUrlssem hacks. Parece não ser suportado pela Angular nativamente.
Sergiy Belozorov 5/12/19
1

Como seu pop-up não é filho do componente que o abriu, você não pode usar :: ng-deep

a única coisa que acho que funcionará é remover o encapsulamento de exibição do host, o pop-up e o componente que abre o pop-up (tente apenas o pop-up e o componente que abre o pop-up primeiro, se isso não funcionar, remova o encapsulamento do host também.)

@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
fonte
Você pode explicar o que você quer dizer com "remover o encapsulamento de visualizações"?
Sergiy Belozorov 03/12/19
@SergiyBelozorov Done :). deixe-me saber se ele te ajudou
Ron
Certamente me ajudou a aprender algo novo sobre o Angular e como se pode configurar componentes :-), mas não tenho certeza se essa é a melhor abordagem. Com nomes de classes genéricas, como header, main, e footerpode-se facilmente de estilo tais componentes aliás. Talvez alguém possa tentar usar nomes mais específicos para evitar conflitos, mas eu preferiria uma solução que mantenha o encapsulamento.
Sergiy Belozorov 03/12/19
Entendo sua preocupação, mas essa é a única solução para seus requisitos. Como você disse pode-se tentar usar o nome e as classes mais específico, talvez usar a abordagem BEM
Ron
1
Desabilitar ViewEncapsulationé um grande não-não para aplicativos de tamanho médio ou grande. É preciso um cuidado especial ou todo o seu estilo ficará confuso a longo prazo. (ex: nomes genéricos de classe). E eu discordo que é a única solução para os requisitos dele.
Dino
0

Você pode combinar scss com seu código

Primeiro, você precisa separar o arquivo scss que deseja compartilhar no aplicativo

Por exemplo:

Em style1.scss

.header { font-weight: bold }

Em seguida, em style2.scss

@import "style1"

Ou você pode combinar a lista de arquivos scss no código do componente, definindo na lista de matriz

styleUrls: ['./style1.scss', './style2.scss'],
  • Nota: Altere o caminho de acordo com o caminho do arquivo e isso só funcionará quando você usar scss e não css

Isto é como você pode configurar manualmente o suporte do scss para o angular cli

Em angular.json, adicione

"projects": {
        "app": {
            "root": "",
            "sourceRoot": "src",
            "projectType": "application",
            "prefix": "app",
            "schematics": { // add this config
                "@schematics/angular:component": {
                    "style": "scss"
                }
            },
Tony Ngo
fonte
Sim, de fato usamos scss e @importdeclarações. Mas, assim como a resposta do @Dino, eu me pergunto se é possível não precisar codificar a URL do estilo no componente pop-up, mas lê-la PopupConfige atribuí-la de alguma forma em tempo de execução?
Sergiy Belozorov 03/12/19
0

Você pode enviar um valor prop para o componente, que pode ser uma classe personalizada e colocá-lo no seu pop-up html. E então, no arquivo scss, adicione substituições extras de css para uma classe específica. Portanto, para cada componente personalizado, você pode ter um código CSS personalizado.

PS: E sim, eu sugeriria importar arquivos scss como:

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

É melhor separar css de js + seu código css, pois pode demorar muito mais, contendo todos os casos de estilo.

Andris
fonte
0

Além dos métodos já mencionados, sugiro mais dois caminhos a serem explorados:

  1. desde que o Angular seleciona o CSS para os componentes e gostaríamos de mantê-lo dessa maneira, o limite do componente de cruzamento pode ser feito, descobrindo qual escopo o Angular atribuiu a ele e adicionando CSS com escopo manual à <style>tag global na página:

    @ViewChild ('popupRef') popupRef; ngAfterViewInit () {this.popupRef.nativeElement.attributes [0] .name // isso terá um valor semelhante ao _ngcontent-tro-c1qual você precisará definir todo o seu CSS personalizado. }

Uma desvantagem aparente dessa abordagem é que a Angular oculta o gerenciamento de CSS e, portanto, você precisará recorrer ao JS simples para gerenciá-lo. ( um exemplo aqui )

  1. você pode tentar definir o CSS no @Component antes de criá-lo, utilizando a fábrica de decoradores personalizados, como nesta resposta

Pessoalmente, eu exploraria a segunda opção, pois parece ser menos invasiva

timur
fonte