Как стилизовать дочерние компоненты из CSS файла родительского компонента?

У меня есть родительский компонент:

<parent></parent>

И я хочу заполнить эту группу дочерними компонентами:

<parent>
  <child></child>
  <child></child>
  <child></child>
</parent>

Родительский шаблон:

<div class="parent">
  <!-- Children goes here -->
  <ng-content></ng-content>
</div>

Детский шаблон:

<div class="child">Test</div>

Так как parent и child являются двумя отдельными компонентами, их стили блокируются в своей области.

В моем родительском компоненте я попытался сделать:

.parent .child {
  // Styles for child
}

Но стили .child не применяются к компонентам child.

Я попытался использовать styleUrls, чтобы включить таблицу стилей parent в компонент child, чтобы решить проблему с областью:

// child.component.ts
styleUrls: [
  './parent.component.css',
  './child.component.css',
]

Но это не помогло, попробовал другой путь, извлекая таблицу стилей child в parent, но это тоже не помогло.

Итак, как вы создаете дочерние компоненты, которые включены в родительский компонент?

Ответ 1

Обновление - Новейший путь

Не делай этого, если можешь этого избежать. Как отмечает Девон Санс в комментариях: эта функция, скорее всего, будет устаревшей.

Обновление - новый путь

Начиная с версии Angular 4.3.0, все комбинаторы для пирсинга CSS устарели. Команда Angular представила новый комбинатор ::ng-deep (все еще на экспериментальном уровне, а не полным и окончательным способом), как показано ниже,

ДЕМО: https://plnkr.co/edit/RBJIszu14o4svHLQt563?p=preview

styles: [
    '
     :host { color: red; }

     :host ::ng-deep parent {
       color:blue;
     }
     :host ::ng-deep child{
       color:orange;
     }
     :host ::ng-deep child.class1 {
       color:yellow;
     }
     :host ::ng-deep child.class2{
       color:pink;
     }
    '
],



template: '
      Angular2                                //red
      <parent>                                //blue
          <child></child>                     //orange
          <child class="class1"></child>      //yellow
          <child class="class2"></child>      //pink
      </parent>      
    '


Старый способ

Вы можете использовать encapsulation mode и/или piercing CSS combinators >>>,/deep/and ::shadow

рабочий пример: http://plnkr.co/edit/1RBDGQ?p=preview

styles: [
    '
     :host { color: red; }
     :host >>> parent {
       color:blue;
     }
     :host >>> child{
       color:orange;
     }
     :host >>> child.class1 {
       color:yellow;
     }
     :host >>> child.class2{
       color:pink;
     }
    '
    ],

template: '
  Angular2                                //red
  <parent>                                //blue
      <child></child>                     //orange
      <child class="class1"></child>      //yellow
      <child class="class2"></child>      //pink
  </parent>      
'

Ответ 2

ОБНОВЛЕНИЕ 3:

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

Прокомментируйте, если у вас есть понимание этого.

ОБНОВЛЕНИЕ 2:

Поскольку /deep/ и все остальные теневые пирсинг-селекторы теперь устарели. Angular drop ::ng-deep который следует использовать вместо этого для более широкой совместимости.

ОБНОВИТЬ:

Если вы используете Angular-CLI, вам нужно использовать /deep/ вместо >>> иначе он не будет работать.

ОРИГИНАЛ:

После перехода на страницу Angular2 Github и случайного поиска "style" я нашел этот вопрос: Angular 2 - styleHTML

Который сказал использовать что-то, что было добавлено в 2.0.0-beta.10, селекторы >>> и ::shadow.

(>>>) (и эквивалент /deep/) и :: shadow были добавлены в 2.0.0-beta.10. Они похожи на CSS-комбинаторы теневого DOM (которые устарели) и работают только с инкапсуляцией: ViewEncapsulation.Emulated, которая используется по умолчанию в Angular2. Вероятно, они также работают с ViewEncapsulation.None, но затем игнорируются только потому, что в них нет необходимости. Эти комбинаторы являются лишь промежуточным решением, пока не поддерживаются более продвинутые функции для многокомпонентного стиля.

Так просто делаешь

:host >>> .child {}

В parent файле стилей решена проблема. Обратите внимание, что, как указано в приведенной выше цитате, это решение является промежуточным, пока не поддерживается более продвинутый межкомпонентный стиль.

Ответ 3

Имела ту же проблему, поэтому, если вы используете angular2 -cli с использованием scss/sass '/deep/' вместо ' → > ', последний селектор еще не поддерживается (но отлично работает с css).

Ответ 4

Печально, что /deep/selector устарел (по крайней мере, в Chrome) https://www.chromestatus.com/features/6750456638341120

Короче говоря, похоже, что в настоящее время нет долгосрочного решения, кроме как каким-то образом заставить ваш дочерний компонент динамически стилизовать вещи.

Вы можете передать объект стиля своему ребенку и применить его через:
<div [attr.style]="styleobject">

Или, если у вас есть определенный стиль, вы можете использовать что-то вроде:
<div [style.background-color]="colorvar">

Больше обсуждений, связанных с этим: https://github.com/angular/angular/issues/6511

Ответ 5

Если вы не хотите использовать :: ng-deep, вы можете сделать это, что кажется правильным:

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

@Component({
    ....
    encapsulation: ViewEncapsulation.None
})

И тогда вы сможете изменить css форму вашего компонента без необходимости из :: ng-deep

.mat-sort-header-container {
  display:flex;
  justify-content:center;
}

ВНИМАНИЕ: будьте осторожны, так как если ваш компонент имеет много дочерних элементов, CSS, который вы пишете для этого компонента, может повлиять на все дочерние элементы!

Ответ 6

Если вы хотите больше ориентироваться на фактический дочерний компонент, чем вы должны следовать. Таким образом, если другие дочерние компоненты имеют одно и то же имя класса, они не будут затронуты.

Plunker: https://plnkr.co/edit/ooBRp3ROk6fbWPuToytO?p=preview

Например:

import {Component, NgModule } from '@angular/core'
import {BrowserModule} from '@angular/platform-browser'

@Component({
  selector: 'my-app',
  template: `
    <div>
      <h2>I'm the host parent</h2>
      <child-component class="target1"></child-component><br/>
      <child-component class="target2"></child-component><br/>
      <child-component class="target3"></child-component><br/>
      <child-component class="target4"></child-component><br/>
      <child-component></child-component><br/>
    </div>
  `,
  styles: [`

  /deep/ child-component.target1 .child-box {
      color: red !important; 
      border: 10px solid red !important;
  }  

  /deep/ child-component.target2 .child-box {
      color: purple !important; 
      border: 10px solid purple !important;
  }  

  /deep/ child-component.target3 .child-box {
      color: orange !important; 
      border: 10px solid orange !important;
  }  

  /* this won't work because the target component is spelled incorrectly */
  /deep/ xxxxchild-component.target4 .child-box {
      color: orange !important; 
      border: 10px solid orange !important;
  }  

  /* this will affect any component that has a class name called .child-box */
  /deep/ .child-box {
      color: blue !important; 
      border: 10px solid blue !important;
  }  


  `]
})
export class App {
}

@Component({
  selector: 'child-component',
  template: `
    <div class="child-box">
      Child: This is some text in a box
    </div>
  `,
  styles: [`
    .child-box {
      color: green;    
      border: 1px solid green;
    }
  `]
})
export class ChildComponent {
}


@NgModule({
  imports: [ BrowserModule ],
  declarations: [ App, ChildComponent ],
  bootstrap: [ App ]
})
export class AppModule {}

Надеюсь, это поможет!

codematrix

Ответ 7

В Angular есть несколько вариантов:

1) Вы можете использовать глубокие селектора css

:host >>> .childrens {
     color: red;
 }

2) Вы также можете изменить инкапсуляцию представления, которую он настроил на Emulated по умолчанию, но его можно легко изменить на Native, который использует встроенную версию браузера Shadow DOM, в вашем случае вам просто нужно отключить его.

Например: `

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

@Component({
  selector: 'parent',
  styles: [`
    .first {
      color:blue;
    }
    .second {
      color:red;
    }
 `],
 template: `
    <div>
      <child class="first">First</child>
      <child class="second">Second</child>
    </div>`,
  encapsulation: ViewEncapsulation.None,
 })
 export class ParentComponent  {
   constructor() {

   }
 }

Ответ 8

Вы не должны писать правила CSS для дочерних элементов компонента в родительском компоненте, так как Angular-компонент является автономной сущностью, которая должна явно декларировать то, что доступно для внешнего мира. Если в будущем дочерний макет изменится, ваши стили для элементов этого дочернего компонента, разбросанных по файлам SCSS других компонентов, могут легко сломаться, что сделает ваш стиль очень хрупким. Это то, что ViewEncapsulation для CSS. В противном случае было бы то же самое, если бы вы могли присваивать значения частным полям некоторого класса из любого другого класса в объектно-ориентированном программировании.

Поэтому вам нужно определить набор классов, которые вы можете применить к дочернему элементу хоста, и реализовать, как дочерний элемент реагирует на них.

Технически это можно сделать следующим образом:

// child.component.html:
<span class="label-1"></span>

// child.component.scss:
:host.child-color-black {
    .label-1 {
        color: black;
    }
}

:host.child-color-blue {
    .label-1 {
        color: blue ;
    }
}

// parent.component.html:
<child class="child-color-black"></child>
<child class="child-color-blue"></child>

Другими словами, вы используете :host псевдо-селектор, предоставляемый Angular + набор CSS-классов, чтобы определить возможные дочерние стили в самом дочернем компоненте. Затем у вас есть возможность запускать эти стили извне, применяя предопределенные классы к элементу хоста <child>.

Ответ 9

Мне очень нравится очиститель передавать переменную @INPUT, если у вас есть доступ к коду дочернего компонента:

Идея заключается в том, что родитель говорит ребенку, каково должно быть его состояние внешнего вида, и ребенок решает, как отображать состояние. Это хорошая архитектура

Способ SCSS:

.active {
  ::ng-deep md-list-item {
    background-color: #eee;
  }
}

Лучший способ: - использовать переменную selected:

<md-list>
    <a
            *ngFor="let convo of conversations"
            routerLink="/conversations/{{convo.id}}/messages"
            #rla="routerLinkActive"
            routerLinkActive="active">
        <app-conversation
                [selected]="rla.isActive"
                [convo]="convo"></app-conversation>
    </a>
</md-list>

Ответ 10

На самом деле есть еще один вариант. Что довольно безопасно. Вы можете использовать ViewEncapsulation.None, но поместите все стили вашего компонента в его тег (он же селектор). Но в любом случае всегда предпочитайте какой-нибудь глобальный стиль плюс инкапсулированные стили.

Вот модифицированный пример Дениса Рыбалки:

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

@Component({
  selector: 'parent',
  styles: ['
    parent {
      .first {
        color:blue;
      }
      .second {
        color:red;
      }
    }
 '],
 template: '
    <div>
      <child class="first">First</child>
      <child class="second">Second</child>
    </div>',
  encapsulation: ViewEncapsulation.None,
})
export class ParentComponent  {
  constructor() { }
}

Ответ 11

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

<parent>
  <child [foo]="bar"></child>
</parent>

Angular осуждает все способы воздействия на дочерние стили от родителей.

https://angular.io/guide/component-styles#deprecated-deep--and-ng-deep

Ответ 12

У меня также была эта проблема, и я не хотел использовать устаревшее решение так что я закончил с:

в отрыве

 <dynamic-table
  ContainerCustomStyle='width: 400px;'
  >
 </dynamic-Table>

дочерний компонент

@Input() ContainerCustomStyle: string;

в child в html div

 <div class="container mat-elevation-z8"
 [style]='GetStyle(ContainerCustomStyle)' >

и в коде

constructor(private sanitizer: DomSanitizer) {  }

  GetStyle(c) {
    if (isNullOrUndefined(c)) { return null; }
    return  this.sanitizer.bypassSecurityTrustStyle(c);
  }

работает как положено и не должно быть устаревшим;)

Ответ 13

Я предлагаю пример, чтобы сделать его более понятным, поскольку angular.io/guide/component-styles утверждает:

Комбинированный потомок-пронизывающий теневой прогиб устарел, а поддержка удаляется из основных браузеров и инструментов. Таким образом, мы планируем отказаться от поддержки в Angular (для всех 3 из /deep/, → > и:: ng-deep). До тех пор:: ng-deep следует предпочесть для более широкой совместимости с инструментами.

В app.component.scss, при необходимости импортируйте *.scss. _colors.scss имеет некоторые общие значения цвета:

$button_ripple_red: #A41E34;
$button_ripple_white_text: #FFF;

Применить правило ко всем компонентам

Все кнопки, имеющие класс btn-red, будут стилизованы.

@import `./theme/sass/_colors`;

// red background and white text
:host /deep/ button.red-btn {
    color: $button_ripple_white_text;
    background: $button_ripple_red;
}

Применить правило к одному компоненту

Все кнопки, имеющие btn-red класс на app-login компоненте, будут стилизованы.

@import `./theme/sass/_colors`;

/deep/ app-login button.red-btn {
    color: $button_ripple_white_text;
    background: $button_ripple_red;
}

Ответ 14

Я решил это за пределами Angular. Я определил общий scss, который я импортирую своим детям.

shared.scss

%cell {
  color: #333333;
  background: #eee;
  font-size: 13px;
  font-weight: 600;
}

child.scss

@import 'styles.scss';
.cell {
  @extend %cell;
}

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