web-dev-qa-db-fra.com

Comment styliser les composants enfants à partir du fichier CSS du composant parent?

J'ai un composant parent:

<parent></parent>

Et je veux remplir ce groupe avec des composants enfants:

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

Modèle parent:

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

Modèle enfant:

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

Étant donné que parent et child sont deux composants distincts, leurs styles sont verrouillés dans leur propre domaine. 

Dans mon composant parent, j'ai essayé de faire:

.parent .child {
  // Styles for child
}

Mais les styles .child ne sont pas appliqués aux composants child.

J'ai essayé d'utiliser styleUrls pour inclure la feuille de style de parent dans le composant child afin de résoudre le problème de portée:

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

Mais cela n'a pas aidé, a également essayé dans l'autre sens en récupérant la feuille de style child dans parent mais cela n'a pas aidé non plus.

Alors, comment qualifiez-vous les composants enfants inclus dans un composant parent?

161
Chrillewoodz

Mise à jour - nouvelle façon

Ne le fais pas, si tu peux l'éviter. Comme Devon Sans le fait remarquer dans les commentaires: Cette fonctionnalité sera probablement obsolète.

Mise à jour - Newer Way

À partir de Angular 4.3.0, tous les combinartors css de piercing étaient obsolètes. L’équipe angulaire a présenté un nouveau combinateur ::ng-deep (il s’agit toujours d’un niveau expérimental et non de la manière complète et finale) comme indiqué ci-dessous,

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>      
    `


L'ancienne manière

Vous pouvez utiliser encapsulation mode et/ou piercing CSS combinators >>>, /deep/ and ::shadow

exemple de travail: 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

UPDATE 3:

::ng-deep est également obsolète, ce qui signifie que vous ne devriez plus faire cela du tout. Il n'est pas clair en quoi cela affecte les éléments pour lesquels vous devez remplacer les styles des composants enfants d'un composant parent. Pour moi, il semble étrange que cela soit supprimé complètement, car cela affecterait les choses en tant que bibliothèques où vous devez remplacer les styles dans un composant de bibliothèque.

Commentez si vous avez un aperçu de cela.

UPDATE 2:

Depuis /deep/ et tous les autres sélecteurs de perforation d'ombre sont maintenant obsolètes. Angular drop ::ng-deep qui devrait être utilisé à la place pour une compatibilité plus large. 

METTRE À JOUR:

Si vous utilisez Angular-CLI, vous devez utiliser /deep/ au lieu de >>>, sinon cela ne fonctionnera pas. 

ORIGINAL:

Après être allé sur la page Github de Angular2 et avoir fait une recherche aléatoire pour "style", j'ai trouvé cette question: Angular 2 - innerHTML styling

Ce qui dit d'utiliser quelque chose qui a été ajouté dans 2.0.0-beta.10, les sélecteurs >>> et ::shadow

(>>>) (et les équivalents/deep /) et :: shadow ont été ajoutés dans 2.0.0-beta.10. Ils sont similaires aux combinateurs CSS fantômes DOM (qui sont obsolètes) et ne fonctionnent qu'avec l’encapsulation: ViewEncapsulation.Emulated, qui est la valeur par défaut dans Angular2. Ils fonctionnent probablement aussi avec ViewEncapsulation.None mais ne sont alors ignorés que parce qu'ils ne sont pas nécessaires. Ces combinateurs ne sont qu'une solution intermédiaire jusqu'à ce que des fonctionnalités plus avancées pour le style multi-composants soient prises en charge.

Alors simplement faire:

:Host >>> .child {}

Dans la feuille de style de parent, le problème a été résolu. Veuillez noter que, comme indiqué dans la citation ci-dessus, cette solution n'est intermédiaire que jusqu'à ce que le style multi-composants avancé soit pris en charge.

47
Chrillewoodz

Même problème, donc si vous utilisez angular2-cli avec scss/sass, utilisez '/ deep /' au lieu de '>>>', le dernier sélecteur n'est pas encore pris en charge (mais fonctionne parfaitement avec css).

15
SergiySev

Malheureusement, il semble que le sélecteur/deep/soit obsolète (du moins sous Chrome) https://www.chromestatus.com/features/6750456638341120

En résumé, il semble qu’il n’existe (actuellement) pas de solution à long terme autre que d’obliger votre composant enfant à styliser les éléments de manière dynamique .

Vous pouvez passer un objet de style à votre enfant et le faire appliquer via:
<div [attr.style]="styleobject">

Ou si vous avez un style spécifique, vous pouvez utiliser quelque chose comme:
<div [style.background-color]="colorvar">

Plus de discussion à ce sujet: https://github.com/angular/angular/issues/6511

14
Matthew B.

Si vous souhaitez cibler davantage le composant enfant, procédez comme suit. Ainsi, si d'autres composants enfants partagent le même nom de classe, ils ne seront pas affectés. 

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

Par exemple:

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 {}

J'espère que cela t'aides!

codematrix

10
code5

Si vous ne voulez pas utiliser :: ng-deep, vous pouvez le faire, ce qui semble être la bonne manière:

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

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

Et ensuite, vous pourrez modifier la forme css de votre composant sans avoir besoin de :: ng-deep

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

ATTENTION: faites attention car si votre composant a beaucoup d'enfants, le css que vous écrivez pour ce composant peut affecter tous les enfants!

8
Tonio

Angular offre quelques options pour y parvenir:

1) Vous pouvez utiliser des sélecteurs deep css

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

2) Vous pouvez également modifier l’encapsulation de la vue, elle est définie par défaut sur Emulé, mais peut également être remplacée par Native qui utilise la mise en œuvre du navigateur Shadow DOM, vous devez simplement le désactiver 

Par exemple: `

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

Je trouve beaucoup plus propre de passer une variable @INPUT si vous avez accès au code du composant enfant:

L'idée est que le parent indique à l'enfant quel doit être son état d'apparence et que l'enfant décide comment afficher l'état. C'est une belle architecture 

SCSS Way:

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

Meilleure façon: - utilisez la variable 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

Vous ne devez pas écrire de règles CSS pour un élément de composant enfant dans un composant parent, car un composant angulaire est une entité autonome qui devrait déclarer explicitement ce qui est disponible pour le monde extérieur. Si la disposition des enfants change à l'avenir, les styles de ces éléments de composants enfants disséminés dans les fichiers SCSS des autres composants pourraient facilement se briser, ce qui rendrait votre style très fragile. C'est ce que ViewEncapsulation est dans le cas de CSS. Sinon, il en irait de même si vous pouviez affecter des valeurs à des champs privés d'une classe à partir d'une autre classe dans la programmation orientée objet.

Par conséquent, vous devez définir un ensemble de classes que vous pouvez appliquer à l'élément hôte enfant et mettre en œuvre la manière dont l'enfant y répond.

Techniquement, cela pourrait être fait comme suit:

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

En d'autres termes, vous utilisez le pseudo-sélecteur :Host fourni par Angular + et l'ensemble de classes CSS pour définir les styles enfant possibles dans le composant enfant même. Vous avez ensuite la possibilité de déclencher ces styles de l'extérieur en appliquant des classes prédéfinies à l'élément hôte <child>.

2
Alexander Abakumov

j'ai également eu ce problème et je ne voulais pas utiliser une solution obsolète donc je me suis retrouvé avec:

en parrent

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

composant enfant

@Input() ContainerCustomStyle: string;

chez l'enfant en html div

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

et en code

constructor(private sanitizer: DomSanitizer) {  }

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

fonctionne comme prévu et ne devrait pas être déconseillé;)

1
d00lar

La réponse rapide est que vous ne devriez pas faire cela du tout. Il casse l’encapsulation de composants et compromet l’avantage que vous retirez des composants autonomes. Envisagez de passer un indicateur prop au composant enfant. Celui-ci peut alors décider lui-même comment rendre le rendu différemment ou appliquer un code CSS différent, si nécessaire.

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

Angular décourage tous les moyens d’affecter les styles d’enfant aux parents.

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

1
Jed Richards

En fait, il y a encore une option. Ce qui est plutôt sûr. Vous pouvez utiliser ViewEncapsulation.None BUT mais placez tous vos styles de composant dans sa balise (sélecteur) Quoi qu'il en soit, préférez toujours un style global plus des styles encapsulés.

Voici l'exemple modifié de Denis Rybalka:

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

Je propose un exemple pour le rendre plus clair, car angular.io/guide/component-styles déclare:

Le combinateur de descendants perçant les ombres est déconseillé et la prise en charge est supprimée des principaux navigateurs et outils. En tant que tel, nous prévoyons de supprimer le support angulaire (pour les 3 utilisateurs de/deep /, >>> et :: ng-deep). Jusque-là: ng-deep devrait être préféré pour une compatibilité plus large avec les outils.

Sur app.component.scss, importez votre *.scss si nécessaire. _colors.scss a des valeurs de couleur communes:

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

Appliquer une règle à tous les composants

Tous les boutons ayant la classe btn-red seront stylés.

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

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

Appliquer une règle à un seul composant

Tous les boutons ayant la classe btn-red sur le composant app-login seront stylés.

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

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