Что использовать вместо :: ng-deep

94

Я пытаюсь стилизовать элемент, размещенный у выхода маршрутизатора, в угловой, и хочу убедиться, что сгенерированный элемент имеет ширину 100%.

Из большинства ответов я вижу, что мне следует использовать ::ng-deepселектор, но из документов Angular он устарел. Есть альтернатива ::ng-deep?

Якоб Шварц
источник
2
::ng-deepникуда не денется. Это всегда будет параметр, который вы можете включить. Нет абсолютно никакого способа удалить его сейчас без массивной реакции сообщества. Посмотрите, сколько результатов возвращается по этому запросу github.com/search?q=%3A%3Ang-deep&type=Code - это все равно что сказать, что !importantсвойство css исчезнет,
Simon_Weaver
Я не знаю - я из любопытства провел поиск по всему проекту в нашем монорепозитории (несколько довольно крупных корпоративных приложений) и нашел только 69 ссылок. Я чувствую, что это определенно приемлемый рефакторинг для отказа от устаревания, и я с радостью сделаю это всякий раз, когда они предложат альтернативу. Кроме того, он !importantзанимает важное место в спецификации CSS, тогда как ::deepвсегда был лишь предложением.
dudewad

Ответы:

103

FWIW В своем исследовании я не нашел замены ng-deep или другим подходящим альтернативам. Это связано с тем, что, как я полагаю, команда Angular придерживается спецификации W3C в отношении теневого объекта, который изначально имел такие селекторы, как deep. Однако с тех пор W3c удалил эту рекомендацию, но не заменил ее новой. До тех пор, пока этого не произойдет, я полагаю, что команда Angular сохранит, ::ng-deepи его альтернативы будут доступны, но в устаревшем состоянии из-за состояния ожидания черновиков W3C. Я не могу найти время, чтобы найти документацию, подтверждающую это прямо сейчас, но я видел это недавно.

Короче говоря: продолжайте использовать ::ng-deepи его альтернативы до тех пор, пока не будет создана замена - отказ от поддержки - это всего лишь раннее уведомление, чтобы люди не остались в стороне, когда реальное изменение материализуется.

- ОБНОВЛЕНИЕ -

https://drafts.csswg.org/css-scoping-1/ Вот черновик предложения, если вам интересно. Похоже, что они работают над надежным набором селекторов для элементов в теневом дереве; именно эта спецификация, после утверждения, я думаю, проинформирует клон angular, если он вообще есть (то есть angular может не потребоваться реализовывать свои собственные селекторы, как только они появятся в браузерах).

чувак
источник
Я согласен с этим, но я бы не рекомендовал писать новый код целенаправленно с использованием устаревших функций фреймворка (и браузера).
MT_
7
Либо я. Но альтернативы нет, и я думаю, что она здесь довольно четко обозначена. Есть ли у вас какие-нибудь предложения, чтобы помочь с этим?
dudewad
1
Единственная альтернатива, о которой я могу быстро придумать, - это рефакторинг вложенности компонентов, что может потребовать больше работы, чем у вас есть времени, но может принести другие преимущества ...
MT_
32
Со сторонней библиотекой практически невозможно избежать использования ::ng-deepвремя от времени (если вы вообще заботитесь о том, как выглядит ваш сайт) - даже с чем-то вроде материала angular. У них есть ошибки, которые не исправляются месяцами, и обходные пути часто включают ng-deep. И не путайте разные устаревшие «глубокие» селекторы - ::ng-deepэто определенно наименее устаревший.
Simon_Weaver
1
Да, это одна из самых уродливых частей всей системы. Но инкапсуляция - это то, что такое инкапсуляция. Вы должны нарушить границу либо явно используя :: ng-deep в css, либо сделать это программно. Иногда мы используем атрибут в теге компонента, чтобы указать, в каком «режиме» находится компонент (т. Е. Контекст), а затем стили могут жить в дочернем компоненте без :: ng-deep через селектор атрибутов, например: :host[some-context] {}- it зависит от того, какую гибкость / переносимость вы хотите. Мне не очень нравится ни то, ни другое, но это мир инкапсуляции.
dudewad
20

Чтобы обойти устаревшее ::ng-deep, я обычно отключаю ViewEncapsulation. Хотя это не лучший подход, он сослужил мне хорошую службу.

Чтобы отключить ViewEncapsulation, сделайте в своем компоненте следующее:

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

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

export class HeaderComponent {

}

Это сделает стили .scss в этом компоненте глобальными для всего приложения. Чтобы стили не переходили вверх по цепочке к родительским и родственным компонентам, оберните весь scss селектором следующим образом:

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

Теперь стили, указанные здесь, перейдут к дочерним компонентам, поэтому вы должны быть более конкретными с вашими селекторами css и учитывать свои p и q при добавлении CSS (возможно, добавьте дочерний селектор, указанный в вашем приложении Angular, а затем его стили).

Я говорю, что это не лучший подход из-за параграфа выше, но он мне хорошо послужил.

AliF50
источник
12
Это всего лишь обходной путь, и если у вас большой проект, отключение ViewEncapsulationнанесет большой ущерб, поскольку эти стили могут проникнуть во все компоненты. Эту функцию следует использовать с умом и с полным пониманием
mpro
5
@mpro Я понимаю, поэтому я сделал предостережение и сказал, что это не лучший подход, и вы должны помнить о своих p и q и должны быть более конкретными. Для меня этот подход пока работает хорошо. :: ng-deep отмечен как устаревший, и это обходной путь.
AliF50 02
1
Честно говоря, я считаю, что это ужасный вывод, если вы сделали это из-за угрозы отказа от поддержки. Да, я знаю, что ты это признаешь, но я действительно думаю, что таким образом ты прострелил себе ногу. Инкапсуляция представлений так полезна по многим причинам. Однако это не так плохо, как кто-то из команды angular отказался от него без какого-либо логического обходного пути, что привело многих к большой путанице. В конце концов, вы все еще пишете код для веб-браузера, а не какой-то проприетарный движок angular.
Simon_Weaver
2
@Simon_Weaver Я уважаю ваше мнение и благодарю за то, что поделились. Я просто вспоминаю об этом, потому что это то, что я использовал, чтобы обойти устаревание. Я также обнаружил предостережения.
AliF50
3
@ AliF50 "Обойти депрекцию" на самом деле не так. Настоящая проблема здесь в том, что, а я никогда раньше не видел этого, они отказались от него, не назвав альтернативы . Мой ответ (принятый выше) объясняет мою гипотезу о том, почему они сделали (W3C устарел) для согласования со спецификацией. Однако, если вы прочитаете предложения, похоже, что :: ng-deep будет заменен подходящей альтернативой, что означает, что, когда он станет доступным, вы просто обновите свои ссылки :: ng-deep, а не ваш подход, который буквально требует перепроектирование всего приложения.
dudewad
15

Простая и легкая альтернатива глубокому стилю - это общий стиль, использующий селектор элементов родительского компонента. Итак, если у вас есть это в hero-details.component.css:

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

Это будет в styles.css:

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

По сути, глубокий стиль - это неинкапсулированный стиль, поэтому он концептуально кажется мне больше общим стилем, чем стилем компонента. Лично я бы больше не использовал глубокие стили. Критические изменения являются нормальным явлением в обновлениях основных версий, а удаление устаревших функций - это честная игра.

Дж. Ленте
источник
1
Вау, теперь я чувствую себя тупым. Благодарность! Исходя из других интерфейсных фреймворков, я думал, что это невозможно
Рафаэль Видорре
1
Это действительно полезно. Жалко, что :: ng-deep так долго считался устаревшим без замены (: host :: ng-deep работает должным образом, но я не хочу использовать устаревшие вещи).
Алексей
6

Как кто-то сказал ранее, если вы используете стороннюю библиотеку, практически невозможно избежать ее использования ::ng-deepвремя от времени. Но что вы собираетесь делать со своими предыдущими проектами, когда они ::ng-deepбольше не поддерживаются браузерами?

Чтобы быть готовым к этому моменту, я предлагаю следующее:

  1. Используйте ViewEncapsulation.None с умом. Что означает только те компоненты, которым требуется доступ к более глубоким компонентам.
@Component({
      selector: 'app-example',
      templateUrl: './example.component.html',
      styleUrls: ['./example.component.scss'],
      encapsulation: ViewEncapsulation.None
    })
  1. Теперь, чтобы избежать коллизий и странностей CSS, вы должны (как правило) всегда заключать в шаблон вашего компонента класс. Итак, example.component.html должен выглядеть так:
<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. Опять же, по правилу, первая строка каждого файла SCSS будет нацелена на контейнер компонента. Поскольку инкапсуляции нет, вы можете изменять сторонний компонент, ориентируясь на их классы. При этом example.component.scss должен выглядеть так:
.app-example-container {
/* All the CSS code goes here */
.mat-tab-group .mat-tab-label {color: red;}
}
гузманой
источник
2

Это не общая замена :: ng-deep, а вариант использования, описанный автором вопроса:

В особом случае, когда вы хотите стилизовать элемент, вставленный маршрутизатором-выходом, есть элегантное решение, использующее соседний селектор соседей в CSS:

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

Это будет применяться ко всем элементам, которые являются непосредственными соседями маршрутизатора-розетки.

Дополнительная литература:
https://developer.mozilla.org/en-US/docs/Web/CSS/Adjacent_sibling_combinator
https://angular.io/guide/router#router-outlet

mrm1st3r
источник
1
Я бы не рекомендовал использовать этот селектор. Похоже, что вы открываете настоящий кошмар столкновений, особенно когда ваше приложение растет. Вдобавок ко всему, селектор * - буквально самый медленный селектор в существовании CSS.
dudewad
@dudewad, в то время как селектор * является самым медленным селектором, он применяется только буквально к следующему родственнику (+), а не ко всей цепочке / дереву, поэтому он должен иметь только номинальное значение.
Эрик Филипс,
CSS-селекторы @ErikPhilips анализируются справа налево, так что на самом деле это наихудший сценарий.
dudewad
@dudewad Я думаю, мы что-то упускаем. *Это наихудший сценарий, за которым следуют первые два, element *но element + *его нет рядом.
Эрик Филипс,
Я не знаю ... Я не тестировал это, это просто основано на том, что я знаю о том, как парсеры CSS делают свое дело.
чувак
1

Чтобы избежать изменения инкапсуляции по умолчанию, я написал помощник, добавляющий глобальные стили для компонента:

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

мой-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;
       }
    `]);
  }
}

результат:

<head>
...
<style id="deep-MY-COMPONENT">
    MY-COMPONENT img {
      height: 180px;
    }
</style>
...
</head>
Гильерме Майнльшмидт Абдо
источник