O que usar no lugar de :: ng-deep

100

Estou tentando estilizar um elemento colocado pela saída do roteador em angular e quero ter certeza de que o elemento gerado terá uma largura de 100%

Pela maioria das respostas, vejo que devo usar o ::ng-deepseletor, mas pelos documentos do Angular ele está sendo descontinuado. Existe uma alternativa para ::ng-deep?

Jacob Schwartz
fonte
2
::ng-deepnão vai a lugar nenhum. Sempre será uma configuração que você pode ativar. Não há absolutamente nenhuma maneira de removê-lo agora sem uma reação massiva da comunidade. Veja quantos resultados retornaram para esta pesquisa github.com/search?q=%3A%3Ang-deep&type=Code - é como dizer que a !importantpropriedade css vai desaparecer
Simon_Weaver
Não sei - fiz uma pesquisa em todo o projeto por curiosidade em nosso mono-repo (vários aplicativos corporativos razoavelmente grandes) e só encontrei 69 referências. Eu sinto que esse é definitivamente um refatorador aceitável para sair da depreciação e faria isso com prazer sempre que trouxerem a alternativa. Além disso, !importanttem um lugar importante na especificação CSS, mas ::deepsempre foi apenas uma proposta.
dudewad

Respostas:

111

FWIW Em minha pesquisa, não encontrei nenhum substituto para o ng-deep ou outras alternativas aplicáveis. Isso ocorre porque, acredito, a equipe Angular está adiando as especificações do W3C no shadow dom, que inicialmente tinha seletores como deep. No entanto, o W3c removeu a recomendação, mas não a substituiu por uma nova. Até que isso aconteça, imagino que a equipe Angular manterá ::ng-deepe suas alternativas disponíveis, mas em estado obsoleto devido ao estado pendente dos rascunhos do W3C. Não consigo encontrar a documentação para fazer o backup agora, mas eu a vi recentemente.

Resumindo: Continue usando ::ng-deepe suas alternativas até que uma substituição seja criada - a descontinuação é apenas um aviso antecipado para que as pessoas não sejam surpreendidas sempre que a mudança real se materializar.

- ATUALIZAÇÃO -

https://drafts.csswg.org/css-scoping-1/ Aqui está o rascunho da proposta, se você estiver interessado. Parece que eles estão trabalhando em um conjunto robusto de seletores para elementos dentro de uma árvore shadow dom; é essa especificação, uma vez aprovada, que eu acho que informará o clone angular, se houver um (ou seja, o angular pode não precisar implementar seus próprios seletores uma vez que isso vá ao ar nos navegadores).

cara
fonte
Eu concordo com isso, mas não recomendaria escrever um novo código propositalmente usando a funcionalidade de estrutura obsoleta (e navegador).
MT_
7
Eu também não. Mas não há alternativa, o que eu acho que está erroneamente claramente delineado aqui. Você tem alguma sugestão para ajudar com isso?
dudewad
1
A única alternativa em que posso pensar rapidamente seria refatorar o aninhamento de componentes, o que pode dar mais trabalho do que você tem tempo, mas pode render outros benefícios ...
MT_
36
Com uma biblioteca de terceiros, é virtualmente impossível evitar ter que usar de ::ng-deepvez em quando (se você se preocupa com a aparência do seu site) - mesmo com algo parecido com material angular. Eles têm bugs que não são corrigidos por meses e as soluções alternativas geralmente envolvem ng-deep. E não confunda os diferentes seletores 'profundos' obsoletos - ::ng-deepé definitivamente o menos obsoleto.
Simon_Weaver
1
Sim, esta é uma das partes mais feias de todo o sistema. Mas encapsulamento é o que é encapsulamento. Você tem que quebrar o limite usando explicitamente :: ng-deep em css ou precisa fazê-lo programaticamente. Às vezes, usamos um atributo na tag do componente para indicar em que "modo" um componente está (ou seja, contexto), e então os estilos podem viver no componente filho w / o :: ng-deep por meio de um seletor de atributo como: :host[some-context] {}- it depende do tipo de flexibilidade / portabilidade que você deseja. Eu não gosto muito de nenhuma maneira, mas este é o mundo do encapsulamento.
dudewad
22

Para ignorar o obsoleto ::ng-deep, geralmente desativo ViewEncapsulation. Embora essa não seja a melhor abordagem, ela me serviu bem.

Para desativar ViewEncapsulation, faça o seguinte em seu componente:

import { Component, ViewEncapsulation } from '@angular/core';

@Component({
  selector: 'app-header',
  templateUrl: './header.component.html',
  styleUrls: ['./header.component.scss'],
  encapsulation: ViewEncapsulation.None
})

export class HeaderComponent {

}

Isso tornará os estilos .scss neste componente globais para todo o aplicativo. Para não permitir que os estilos subam na cadeia de componentes pai e irmão, envolva todo o scss com o seletor assim:

app-header {
  // your styles here and any child component styles can go here
}

Agora, os estilos especificados aqui irão para os componentes filhos, então você precisa ser mais específico com seus seletores de css e cuidar de seus p's e q's ao adicionar CSS (talvez adicionar o seletor filho especificado em seu aplicativo Angular e então seus estilos).

Eu digo que não é a melhor abordagem por causa do parágrafo acima, mas isso me serviu bem.

AliF50
fonte
12
Esta é apenas uma solução alternativa e se você tiver um projeto enorme, desligá-lo ViewEncapsulationcausará muitos danos ao fazer com que esses estilos vazem para todos os componentes. Este recurso deve ser usado com sabedoria e total compreensão
mpro
5
@mpro eu entendo, é por isso que dei a advertência e digo que esta não é a melhor abordagem e você tem que se preocupar com seus p e q e tem que ser mais específico. Para mim, essa abordagem funcionou bem até agora. :: ng-deep está marcado para reprovação e esta é uma solução alternativa.
AliF50
1
Honestamente, acho que essa é uma conclusão horrível de se chegar se você fez isso por causa da ameaça de depreciação. Sim, eu sei que você reconhece isso, mas realmente acho que você está dando um tiro no próprio pé ao fazer isso. O encapsulamento de visualizações é muito útil por vários motivos. No entanto, não é tão ruim quanto qualquer um na equipe angular o desaprovou sem qualquer solução alternativa lógica e levando muitos a muita confusão. No final do dia, você ainda está escrevendo código para um navegador da web - não algum tipo de mecanismo angular proprietário.
Simon_Weaver
2
@Simon_Weaver Respeito sua opinião e obrigado por compartilhar. Estou apenas revelando isso porque é o que usei para contornar a depreciação. Eu também trouxe à tona as advertências.
AliF50 de
4
@ AliF50 "Contornar a depreciação" não é realmente uma coisa. O verdadeiro problema aqui é que, e nunca vi isso antes na minha vida, eles o rejeitaram sem nomear uma alternativa . Minha resposta (a aceita acima) explica minha hipótese de por que eles fizeram (o W3C desaprovou) para se alinhar com a especificação. No entanto, se você ler as propostas, parece que :: ng-deep será substituído por uma alternativa adequada, o que significa que quando estiver disponível, você simplesmente atualiza suas referências :: ng-deep, ao invés de sua abordagem que pede literalmente re-arquitetar todo o aplicativo.
dudewad de
19

A alternativa simples e fácil para um estilo profundo é um estilo comum que usa o seletor de elemento do componente pai. Então, se você tivesse isso em hero-details.component.css:

:host ::ng-deep h3 {
  font-style: italic;
}

Seria assim em styles.css:

app-hero-details h3 {
  font-style: italic;
}

Basicamente, um estilo profundo é um estilo não encapsulado, portanto, conceitualmente, parece mais um estilo comum para mim do que um estilo componente. Pessoalmente, eu não usaria mais estilos profundos. Alterações significativas são normais em atualizações de versão principais e a remoção de recursos obsoletos é um jogo justo.

J. Lenthe
fonte
1
Uau, me sinto idiota agora. Obrigado! Vindo de outros frameworks front-end achei que isso era impossível
Rafael Vidaurre
1
Isso é muito útil. É uma pena que :: ng-deep tenha ficado obsoleto por tanto tempo sem ter um substituto (: host :: ng-deep funciona como esperado, mas eu não quero usar coisas obsoletas).
Alexei
8

Como alguém disse antes, se você estiver usando uma biblioteca de terceiros, é virtualmente impossível evitar ter que usar de ::ng-deepvez em quando. Mas o que você vai fazer com seus projetos anteriores quando o::ng-deep não mais suportados pelos navegadores?

Para estar pronto para esse momento, vou sugerir o seguinte:

  1. Use ViewEncapsulation.None com sabedoria. O que se traduz em apenas para os componentes que precisam acessar componentes mais profundos.
@Component({
      selector: 'app-example',
      templateUrl: './example.component.html',
      styleUrls: ['./example.component.scss'],
      encapsulation: ViewEncapsulation.None
    })
  1. Agora, para evitar colisões e estranhezas do CSS, você deve (como regra) sempre envolver o template do seu componente com uma classe. Portanto, example.component.html deve ser como:
<section class="app-example-container">
<!-- a third party component -->
<mat-tab-group>
<mat-tab label="First"></mat-tab>
<mat-tab label="Second"></mat-tab>
</mat-tab-group>
</section>
  1. Novamente, por regra, a primeira linha de cada arquivo SCSS terá como destino o contêiner do componente. Como não há encapsulamento, você pode modificar o componente de terceiros direcionando suas classes. Dito isso, example.component.scss deve ser como:
.app-example-container {
/* All the CSS code goes here */
.mat-tab-group .mat-tab-label {color: red;}
}

Uma nota pessoal para o futuro: https://angular.io/guide/component-styles
Esse deve ser o primeiro lugar a olhar para alternativas oficiais / caminhos a seguir

Guzmanoj
fonte
But what are you going to do about your previous projects when the ::ng-deep became no longer supported by browsers?Isso não é compilado / polyfilled pelo angular cli, então não há envolvimento do navegador? Ótima resposta btw.
beppe9000
Isso é realmente renderizado para o arquivo CSS e, em seguida, o navegador aplica os estilos de acordo. Aliasing é possível (eu acho) usando :host /deep/ .mat-tab-labelisso deve ser transformado em ::ng-deep. Para ser honesto, parece que usar o alias é mais conveniente porque a CLI pode alterá-lo durante o tempo de compilação, mas ainda assim, você precisará fazer um ng builde implementar novamente. Minha resolução foi evitar ::ng-deepem todos os meus projetos :)
guzmanoj
sim, faz sentido evitar reimplantar
beppe9000
1

Este não é um substituto geral para :: ng-deep, mas para o caso de uso descrito pelo autor da pergunta:

No caso especial em que você deseja estilizar o elemento inserido por uma saída de roteador, há uma solução elegante usando o seletor vizinho adjacente em CSS:

router-outlet+* {
  /* styling here... */
}

Isso se aplica a todos os elementos que são vizinhos diretos de uma tomada do roteador.

Leitura adicional:
https://developer.mozilla.org/en-US/docs/Web/CSS/Adjacent_sibling_combinator
https://angular.io/guide/router#router-outlet

mrm1st3r
fonte
1
Eu não recomendaria usar este seletor. Parece que você está abrindo um pesadelo literal de colisões, especialmente quando seu aplicativo cresce. Além disso, o seletor * é literalmente o seletor mais lento na existência do CSS.
dudewad de
@dudewad embora o seletor * seja o seletor mais lento, ele só está sendo aplicado literalmente ao próximo irmão (+), não a toda a cadeia / árvore, então deve fazer apenas uma diferença nominal.
Erik Philips
Os seletores CSS @ErikPhilips são analisados ​​da direita para a esquerda, portanto, este é o pior cenário possível.
dudewad
@dudewad, acho que está faltando alguma coisa. *é o pior cenário seguido de perto, element *mas element + *não está nem perto dos dois primeiros.
Erik Philips
Eu não sei ... Eu não testei, isso é baseado no que eu sei sobre como os analisadores CSS fazem o seu trabalho.
dudewad