Стиль дочерних компонентов из 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 но это тоже не помогло.

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

22 ответа

Решение

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

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

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

Начиная с версии 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>      
`

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

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

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

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

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

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

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

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

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

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

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

ОБНОВИТЬ:

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

ОРИГИНАЛ:

После перехода на страницу Github на Angular2 и случайного поиска "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В файле стилей решена проблема. Обратите внимание, что, как указано в приведенной выше цитате, это решение является промежуточным, пока не поддерживается более продвинутый межкомпонентный стиль.

Не следует писать правила 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> хост-элемент.

К сожалению, похоже, что / 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

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

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

Плункер: 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

На самом деле есть еще один вариант. Что довольно безопасно. Вы можете использовать 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() { }
}

Поскольку /deep/, >>> и:: ng-deep устарели. Наилучший подход - использовать следующее при оформлении дочерних компонентов:

:host-context(.theme-light) h2 {
  background-color: #eef;
}

Это будет искать световую тему в любом из предков вашего дочернего компонента. См. Документы здесь: https://angular.io/guide/component-styles#host-context

Есть несколько вариантов для достижения этой цели в Angular:

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

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

2) Вы также можете изменить инкапсуляцию вида, для нее установлено значение Эмуляция по умолчанию, но ее можно легко изменить на 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() {

   }
 }

Я считаю, что намного проще передать переменную @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>

На сегодняшний день (Angular 9) Angular использует Shadow DOM для отображения компонентов как пользовательских HTML-элементов. Один из элегантных способов стилизации этих настраиваемых элементов может заключаться в использовании настраиваемых переменных CSS. Вот общий пример:

class ChildElement extends HTMLElement {
  constructor() {
    super();
    
    var shadow = this.attachShadow({mode: 'open'});
    var wrapper = document.createElement('div');
    wrapper.setAttribute('class', 'wrapper');
    
    // Create some CSS to apply to the shadow dom
    var style = document.createElement('style');
    
    style.textContent = `
    
      /* Here we define the default value for the variable --background-clr */
      :host {
        --background-clr: green;
      }
      
      .wrapper {
        width: 100px;
        height: 100px;
        background-color: var(--background-clr);
        border: 1px solid red;
      }
    `;
    
    shadow.appendChild(style);
    shadow.appendChild(wrapper);
  }
}

// Define the new element
customElements.define('child-element', ChildElement);
/* CSS CODE */

/* This element is referred as :host from the point of view of the custom element. Commenting out this CSS will result in the background to be green, as defined in the custom element */

child-element {
  --background-clr: yellow; 
}
<div>
  <child-element></child-element>
</div>

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

В приложении Angular это может быть примерно так:

parent.component.scss

child-element {
  --background-clr: yellow;
}

child-element.component.scss

:host {
  --background-clr: green;
}

.wrapper {
  width: 100px;
  height: 100px;
  background-color: var(--background-clr);
  border: 1px solid red;
}

Я предпочитаю архивировать следующее:

используйте @Component, чтобы добавить класс css к элементу хоста и установить для инкапсуляции значение none. Чем ссылаться на этот класс, который был добавлен к хосту в компонентах style.css.scss. Это позволит нам объявлять стили, которые будут влиять только на нас и наших детей в рамках нашего класса. fe

      @Component({
  selector: 'my-component',
  templateUrl: './my-component.page.html',
  styleUrls: ['./my-component.page.scss'],
  host: {
    class: 'my-component-class'
  },
  encapsulation: ViewEncapsulation.None
})

в сочетании со следующим css (my-component.page.scss)

      // refer ourselves so we are allowed to overwrite children but not global styles
.my-component-class {
  // will effect direct h1 nodes within template and all h1 elements within child components of the 
  h1 {
    color: red;
  }
}
// without class "scope" will affect all h1 elements globally
h1 {
  color: blue;
}

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

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

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

https://angular.io/guide/component-styles

Для назначения класса элемента в дочернем компоненте вы можете просто использовать @Inputстрока в дочернем компоненте и используйте ее как выражение внутри шаблона. Вот пример того, что мы сделали для изменения типа значка и кнопки в общем компоненте кнопки загрузки Bootstrap, не влияя на то, как он уже использовался во всей кодовой базе:

app-loading-button.component.html (дочерний)

<button class="btn {{additionalClasses}}">...</button>

app-loading-button.component.ts

@Input() additionalClasses: string;

parent.html

<app-loading-button additionalClasses="fa fa-download btn-secondary">...</app-loading-button>

Это решение только с ванильным css, ничего особенного, вам даже не нужно . Я предполагаю, что вы не можете изменить дочерний элемент, в противном случае ответ будет еще более простым, я на всякий случай поместил этот ответ в конец.

Иногда необходимо переопределить дочерний CSS при использовании предварительно созданного компонента из библиотеки, и разработчики не предоставили никаких classвходные переменные. ::ng-deepустарел и encapsulation: ViewEncapsulation.Noneпревращает весь CSS вашего компонента в глобальный. Итак, вот простое решение, которое не использует ни то, ни другое.

Дело в том, что нам нужен глобальный стиль, чтобы CSS мог достичь дочернего элемента. Итак, мы можем просто добавить стиль в styles.cssили мы можем создать новый файл CSS и добавить его в stylesмассив в angular.json. Единственная проблема заключается в том, что нам нужен конкретный селектор, чтобы не нацеливаться на другие элементы. Это довольно простое решение — просто добавьте уникальное имя класса в html, я рекомендую использовать имя родительского компонента в имени класса, чтобы обеспечить его уникальность.

Родительский компонент

      <child class="child-in-parent-component"></child>

Давайте представим, что мы хотим изменить цвет фона всех кнопок в дочернем элементе, нам нужно добиться правильной специфичности, чтобы убедиться, что наши стили имеют приоритет. Мы можем сделать это рядом со всеми нашими свойствами, но лучший способ — просто повторять имя класса, пока наш селектор не станет достаточно конкретным, может потребоваться несколько попыток. Таким образом, кто-то другой может снова переопределить этот css, если это необходимо.

Файл глобальных стилей

      .child-in-parent-component.child-in-parent-component.child-in-parent-component
  button {
  background-color: red;
}

или быстро и грязно с !important(не рекомендуется)

      .child-in-parent-component button {
  background-color: red !important;
}

Если дочерний компонент может быть изменен

Просто добавьте входную переменную в компонент и используйте возможности Angular. ngStyleдиректива. Вы можете добавить несколько переменных для стилизации нескольких областей вашего компонента.

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

      type klass = { [prop: string]: any } | null;

@Component({...})
export class ChildComponent {
  @Input() containerClass: klass = null;
  @Input() pClass: klass = null;
...
}
      <div [ngStyle]="containerClass">
  <p [ngStyle]="pClass">What color will I be?</p>
</div>

Родительский компонент

      <child
  [containerClass]="{ padding: '20px', 'background-color': 'black' }"
  [pClass]="{ color: 'red' }"
>
</child>

Это предполагаемый способ создания компонента с динамическим стилем. Многие готовые компоненты будут иметь аналогичную входную переменную.

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

в отрыве

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

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

@Input() ContainerCustomStyle: string;

у ребенка в 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);
  }

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

это сработало для меня

в родительском компоненте:

      <child-component [styles]="{width: '160px', borderRadius: '16px'}" >
</child-component>

I have solved it outside Angular. I have defined a shared scss that I'm importing to my children.

shared.scss

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

child.scss

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

My proposed approach is a way how to solve the problem the OP has asked about. As mentioned at multiple occasions,::ng-deep,:ng-host will get depreciated and disabling encapsulation is just too much of a code leakage, in my view.

По мере обновления Интернета я нашел решение.

Сначала несколько предостережений.

  1. По-прежнему не делай этого. Чтобы уточнить, я бы не стал планировать дочерние компоненты, позволяющие вам стилизовать их. SOC. Если вы, как разработчик компонентов, хотите позволить это, тем больше возможностей для вас.
  2. Если ваш ребенок не живет в теневом доме, это не сработает для вас.
  3. Если вам необходимо поддерживать браузер, в котором не может быть теневого домена, это также не сработает для вас.

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

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

Пронзающий теневой комбайнатор устарел, и поддержка удаляется из основных браузеров и инструментов. Поэтому мы планируем отказаться от поддержки в Angular (для всех 3 of /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;
}

Пусть 'parent' будет именем класса родителя, а 'child' будет именем класса дочернего элемента

.parent .child{
//css definition for child inside parent components
} 

вы можете использовать этот формат для определения формата CSS для "дочернего" компонента внутри "родительского"

Другие вопросы по тегам