Що використовувати замість :: 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 може не знадобитися реалізовувати власні селектори, як тільки це з’явиться у браузерах).


Я погоджуюся з цим, але я не рекомендую писати новий код цілеспрямовано, використовуючи застарілі функції (та браузер).
MT_

7
Був би і я. Але немає альтернативи, яка, на мою думку, тут чітко окреслена. У вас є якісь пропозиції, які допоможуть у цьому?
dudewad

1
Єдиною альтернативою, про яку я можу швидко подумати, буде рефакторинг вкладеності компонентів, що може бути більшою роботою, ніж у вас є час, але може дати інші переваги ...
MT_

32
Зі сторонніми бібліотеками практично неможливо уникнути необхідності використовувати їх ::ng-deepраз у раз (якщо вам взагалі цікаво, як виглядає ваш сайт) - навіть з чимось на зразок кутового матеріалу. У них є помилки, які не виправляються місяцями, і обхідні шляхи часто пов’язані з нг-глибиною. І не плутайте різні застарілі "глибокі" селектори - ::ng-deepце, безумовно, найменш застарілий.
Simon_Weaver

1
Так, це одна з найпотворніших частин всієї системи. Але інкапсуляція - це те, що є інкапсуляція. Вам потрібно розірвати межу або явно використовуючи :: ng-deep у css, або вам потрібно це робити програмно. Іноді ми використовуємо атрибут тегу компонента, щоб вказати, в якому "режимі" знаходиться компонент (тобто контекст), і тоді стилі можуть жити в дочірньому компоненті w / o :: 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, а потім його стилі).

Я вважаю, що це не найкращий підхід із-за абзацу вище, але це мені добре допомогло.


12
Це просто обхідне рішення, і якщо у вас величезний проект, вимкнення ViewEncapsulationзавдасть великої шкоди, зробивши ці стилі можливим просочуванням у всі компоненти. Цією функцією слід користуватися з розумом та з повним розумінням
mpro 02

5
@mpro Я розумію, ось чому я дав застереження і стверджую, що це не найкращий підхід, і ви повинні пам’ятати про свої плюси і запитання і повинні бути особливо конкретними. Для мене такий підхід до цього часу добре працював. :: ng-deep позначений як застарілий, і це обхідне рішення.
AliF50,

1
Чесно кажучи, я думаю, що це жахливий висновок, якщо ви зробили це через загрозу зневаги. Так, я знаю, що ти це визнаєш, але я справді думаю, що цим ти стріляєш собі в ногу. Інкапсуляція подання настільки корисна з багатьох причин. Однак це не настільки погано, як той, хто в команді, що займається розмовою, знецінив це без будь-якого логічного обходу і привів багатьох до великої розгубленості. Врешті-решт ви все ще пишете код для веб-браузера - а не якийсь власний кутовий механізм.
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 . Ніхто не мудро. Що означає лише для тих компонентів, яким потрібно отримати доступ до більш глибоких компонентів.
@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


1
Я б не рекомендував використовувати цей селектор. Здається, ви відкриваєте буквальний кошмар зіткнень, особливо коли ваша програма зростає. Крім того, селектор * - це буквально найповільніший селектор у існуванні CSS.
dudewad

@dudewad, хоча селектор * є найповільнішим селектором, він застосовується лише буквально до наступного брата (+), а не до всього ланцюжка / дерева, тому він повинен мати лише номінальну різницю.
Ерік Філіпс,

Селектори CSS @ErikPhilips аналізуються справа наліво, тому насправді це найгірший сценарій.
dudewad

@dudewad Я думаю, нам чогось не вистачає. *це найгірший сценарій, за яким уважно слідують, element *але element + *немає десь поблизу перших двох.
Ерік Філіпс,

Я не знаю ... Я не тестував, це лише виходячи з того, що я знаю про те, як синтакси аналізатори CSS роблять свою справу.
dudewad

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

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

результат:

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.