it-swarm.com.ru

Как стилизовать дочерние компоненты из 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, но это тоже не помогло.

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

161
Chrillewoodz

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

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

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

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

DEMO: 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>      
`
162
micronyks

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

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

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

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

Поскольку /deep/ и все другие теневые пирсинг-селекторы устарели. Angular drop ::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 проблема решена. Обратите внимание, что, как указано в приведенной выше цитате, это решение является промежуточным, пока не поддерживается более продвинутый межкомпонентный стиль.

47
Chrillewoodz

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

15
SergiySev

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

14
Matthew B.

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

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

10
code5

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

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

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

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

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

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

8
Tonio

Есть несколько вариантов для достижения этой цели в 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() {

   }
 }
6
Denis Rybalka

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

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

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

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

// 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> Host.

2
Alexander Abakumov

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

в отрыве

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

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

1
d00lar

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

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

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

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

1
Jed Richards

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

Я предлагаю пример, чтобы сделать его более понятным, поскольку 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;
}
0
AndreaM16