Qué usar en lugar de :: ng-deep

94

Estoy tratando de diseñar un elemento colocado por la salida del enrutador en angular y quiero asegurarme de que el elemento generado tenga un ancho del 100%

De la mayoría de las respuestas, veo que debería usar el ::ng-deepselector, pero de los documentos de Angular está obsoleto. ¿Existe una alternativa a ::ng-deep?

Jacob Schwartz
fuente
2
::ng-deepno va a ninguna parte. Siempre será una configuración que pueda habilitar. No hay absolutamente ninguna forma de que puedan eliminarlo ahora sin una reacción masiva de la comunidad. Mire cuántos resultados regresan para esta búsqueda github.com/search?q=%3A%3Ang-deep&type=Code - es como decir que la !importantpropiedad css va a desaparecer
Simon_Weaver
No sé, hice una búsqueda en todo el proyecto por curiosidad en nuestro repositorio único (varias aplicaciones empresariales bastante grandes) y solo obtuve 69 referencias. Siento que definitivamente es una refactorización aceptable para salir de la desaprobación y con mucho gusto lo haría cada vez que sacaran la alternativa. Además, !importanttiene un lugar importante en la especificación CSS, mientras que ::deepsiempre fue solo una propuesta.
dudewad

Respuestas:

103

FWIW En mi investigación no he encontrado ningún reemplazo para ng-deep u otras alternativas aplicables. Esto se debe, creo, a que el equipo de Angular está cediendo a la especificación W3C en el dom de sombra, que inicialmente tenía selectores como deep. Sin embargo, el W3c eliminó la recomendación, pero no la reemplazó por una nueva. Hasta que eso suceda, imagino que el equipo de Angular mantendrá ::ng-deepsus alternativas disponibles, pero en estado obsoleto debido al estado pendiente de los borradores del W3C. No puedo tomarme el tiempo para encontrar la documentación para respaldar esto en este momento, pero la vi recientemente.

En pocas palabras: siga usando ::ng-deepy sus alternativas hasta que se cree un reemplazo; la desaprobación es solo un aviso temprano para que las personas no se sorprendan cada vez que se materialice el cambio real.

- ACTUALIZAR -

https://drafts.csswg.org/css-scoping-1/ Aquí está el borrador de la propuesta si está interesado. Parece que están trabajando en un conjunto robusto de selectores para elementos dentro de un árbol de dom de sombra; es esta especificación, una vez aprobada, la que creo que informará al clon angular, si es que hay uno (es decir, es posible que angular no necesite implementar sus propios selectores una vez que se active en los navegadores).

dudewad
fuente
Estoy de acuerdo con esto, pero no recomendaría escribir código nuevo a propósito utilizando la funcionalidad obsoleta del marco (y del navegador).
MT_
7
Yo tampoco. Pero no hay alternativa, que creo que se describe claramente aquí. ¿Tiene alguna sugerencia para ayudar con eso?
dudewad
1
La única alternativa en la que puedo pensar rápidamente sería refactorizar el anidamiento de componentes, que puede ser más trabajo del que tiene tiempo, pero podría generar otros beneficios ...
MT_
32
Con una biblioteca de terceros, es prácticamente imposible evitar tener que usarla de ::ng-deepvez en cuando (si le importa en absoluto cómo se ve su sitio), incluso con algo como material angular. Tienen errores que no se solucionan durante meses y las soluciones a menudo involucran ng-deep. Y no confunda los diferentes selectores "profundos" obsoletos: ::ng-deepdefinitivamente es el menos desaprobado.
Simon_Weaver
1
Sí, esta es una de las partes más feas de todo el sistema. Pero la encapsulación es lo que es la encapsulación. Tienes que romper el límite utilizando explícitamente :: ng-deep en css, o necesitas hacerlo programáticamente. A veces usamos un atributo en la etiqueta del componente para indicar en qué "modo" se encuentra un componente (es decir, contexto), y luego los estilos pueden vivir en el componente secundario sin :: ng-deep a través de un selector de atributos como: :host[some-context] {}- it depende del tipo de flexibilidad / portabilidad que desee. No me gusta mucho de ninguna manera, pero este es el mundo de la encapsulación.
dudewad
20

Para evitar lo obsoleto ::ng-deep, generalmente desactivo ViewEncapsulation. Aunque este no es el mejor enfoque, me ha servido bien.

Para deshabilitar ViewEncapsulation, haga lo siguiente en su 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 {

}

Esto hará que los estilos .scss de este componente sean globales para toda la aplicación. Para no permitir que los estilos suban por la cadena a los componentes principales y hermanos, envuelva todo el scss con el selector de la siguiente manera:

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

Ahora, los estilos especificados aquí se reducirán a los componentes secundarios, por lo que debe ser más específico con sus selectores css y tener en cuenta sus p y q cuando agregue CSS (tal vez agregue el selector secundario especificado en su aplicación Angular y luego sus estilos).

Digo que no es el mejor enfoque debido al párrafo anterior, pero esto me ha servido bien.

AliF50
fuente
12
Esto es solo una solución y si tiene un gran proyecto, apagarlo ViewEncapsulationcausará mucho daño al hacer que esos estilos se filtren en todos los componentes. Esta función debe usarse con prudencia y con total comprensión
mpro
5
@mpro Entiendo, es por eso que hice la advertencia y dije que este no es el mejor enfoque y que debes tener en cuenta tus p y q y tienes que ser más específico. Para mí, este enfoque ha funcionado bien hasta ahora. :: ng-deep está marcado para obsolescencia y esta es una solución.
AliF50
1
Honestamente, creo que esta es una conclusión horrible a la que llegar si lo hiciera debido a la amenaza de desaprobación. Sí, sé que lo reconoces, pero realmente creo que te estás disparando en el pie al hacer esto. La encapsulación de vistas es muy útil por muchas razones. Sin embargo, no es tan malo como cualquiera en el equipo angular lo desaprobó sin ninguna solución lógica y provocando mucha confusión. Al final del día, todavía está escribiendo código para un navegador web, no una especie de motor angular patentado.
Simon_Weaver
2
@Simon_Weaver Respeto tu opinión y gracias por compartir. Lo estoy sacando a la luz porque es lo que solía eludir la desaprobación. También destaqué las advertencias.
AliF50
3
@ AliF50 "Eludir la depreciación" no es realmente una cosa. El verdadero problema aquí es que, y nunca había visto esto antes en mi vida, lo desaprobaron sin nombrar una alternativa . Mi respuesta (la aceptada anteriormente) explica mi hipótesis de por qué hicieron el (W3C lo desaprobó) para alinearse con la especificación. Sin embargo, si lee las propuestas, parece que :: ng-deep será reemplazado por una alternativa adecuada, lo que significa que cuando esté disponible, simplemente actualice sus referencias :: ng-deep, en lugar de su enfoque que requiere literalmente rediseñar la aplicación completa.
dudewad
15

La alternativa simple y fácil a un estilo profundo es un estilo común que utiliza el selector de elementos del componente principal. Entonces, si tuviera esto en hero-details.component.css:

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

Se convertiría en esto en styles.css:

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

Básicamente, un estilo profundo es un estilo no encapsulado, por lo que conceptualmente me parece más un estilo común que un estilo componente. Personalmente, ya no usaría estilos profundos. Los cambios importantes son normales en las actualizaciones de versiones principales y la eliminación de funciones obsoletas es un juego justo.

J. Lenthe
fuente
1
Vaya, me siento tonto ahora. ¡Gracias! Viniendo de otros frameworks de front-end, pensé que esto era imposible
Rafael Vidaurre
1
Esto es realmente útil. Es una pena que :: ng-deep haya estado en desuso durante tanto tiempo sin tener un reemplazo (: host :: ng-deep funciona como se esperaba, pero no quiero usar cosas en desuso).
Alexei
6

Como alguien dijo antes, si está utilizando una biblioteca de terceros, es prácticamente imposible evitar tener que usarla de ::ng-deepvez en cuando. Pero, ¿qué vas a hacer con tus proyectos anteriores cuando ::ng-deepya no sean compatibles con los navegadores?

Para estar preparado para ese momento, sugeriré lo siguiente:

  1. Utilice ViewEncapsulation.None sabiamente. Lo que se traduce en solo para aquellos componentes que necesitan acceder a componentes más profundos.
@Component({
      selector: 'app-example',
      templateUrl: './example.component.html',
      styleUrls: ['./example.component.scss'],
      encapsulation: ViewEncapsulation.None
    })
  1. Ahora, para evitar colisiones y rarezas de CSS, debe (como regla) siempre envolver la plantilla de su componente con una clase. Entonces, example.component.html debería 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. Nuevamente, por regla, la primera línea de cada archivo SCSS se dirigirá al contenedor de componentes. Como no hay encapsulación , puede modificar el componente de terceros dirigiéndose a sus clases. Dicho esto, example.component.scss debería ser como:
.app-example-container {
/* All the CSS code goes here */
.mat-tab-group .mat-tab-label {color: red;}
}
guzmanoj
fuente
2

Este no es un reemplazo general para :: ng-deep, sino para el caso de uso descrito por el autor de la pregunta:

En el caso especial en el que desea diseñar el elemento insertado por un router-outlet, existe una solución elegante usando el selector de vecino adyacente en CSS:

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

Esto se aplicará a todos los elementos que sean vecinos directos de una salida de enrutador.

Para leer más:
https://developer.mozilla.org/en-US/docs/Web/CSS/Adjected_sibling_combinator
https://angular.io/guide/router#router-outlet

mrm1st3r
fuente
1
No recomendaría usar este selector. Parece que estás abriendo una verdadera pesadilla de colisiones, especialmente cuando tu aplicación crece. Además de eso, el * selector es literalmente el selector más lento en existencia de CSS.
dudewad
@dudewad mientras que el selector * es el selector más lento, solo se aplica literalmente al siguiente hermano (+), no a toda la cadena / árbol, por lo que solo debería hacer una diferencia nominal.
Erik Philips
Los selectores de @ErikPhilips CSS se analizan de derecha a izquierda, por lo que este es en realidad el peor de los casos.
dudewad
@dudewad Creo que nos falta algo. *Es el peor de los casos seguido de cerca por los dos primeros, element *pero element + *no está cerca.
Erik Philips
No lo sé ... No lo he probado, esto se basa solo en lo que sé sobre cómo los analizadores CSS hacen lo suyo.
dudewad
1

Para evitar cambiar la encapsulación predeterminada, escribí un ayudante que agrega estilos globales para el componente:

deepStyle.ts

import { ViewContainerRef } from '@angular/core';

export function deepStyle(vcr: ViewContainerRef, csss: string[]){
    let id = 'deep-' + vcr.element.nativeElement.tagName;
    let styleElement = document.getElementById('pierce-' + vcr.element.nativeElement.name);
    if(!styleElement){
        styleElement = document.createElement('style');
        styleElement.id = id;
        styleElement.innerHTML = csss.map(css => vcr.element.nativeElement.tagName + ' ' + css).join('\n');
        document.head.append(styleElement);
    }
}

my-component.ts

import { Component, ViewContainerRef } from '@angular/core';
import { deepStyle } from '../deepStyle';

@Component({
  selector: 'my-component',
  templateUrl: './my-component.html',
  styleUrls: ['./my-component.css']
})
export class MyComponent {
   constructor(vcr: ViewContainerRef) {
    deepStyle(vcr, [`
       img {
         height: 180px;
       }
    `]);
  }
}

resultado:

<head>
...
<style id="deep-MY-COMPONENT">
    MY-COMPONENT img {
      height: 180px;
    }
</style>
...
</head>
Guilherme Meinlschmiedt Abdo
fuente