Phoenix404
Phoenix404

Reputation: 1058

Angular change child component style from parent component but not globally

I have created a shared component(<nextgen-table></nextgen-table>) based on Mat-table (Angular Material). While using this component inside a project, discover that I need to change the behavior(width) of the table columns.

I have exported nextgen-table in my other components (let's say X, Y ) where nextgen-table is, of course, a child component.

To change the width of specific columns of the mat-table I have to use something like this:

mat-cell:nth-child(1),
mat-header-cell:nth-child(1) {
    flex: 0 0 40%;
    text-align: left;
} 
mat-cell:nth-child(2),
mat-header-cell:nth-child(2) {
    flex: 0 0 20%;
}

The above CSS code I was implementing in the X.component.css and it was not working because of encapsulation I guess.

After a little bit of search, I have found the solution which worked correctly just by adding the encapsulation: ViewEncapsulation.None in the Component decorator of x.component.ts. After this solution, I was navigating from the component X to the Component Y in which I didn't implement the above CSS code. But the component Y had the first two columns as I wanted only for component X but somehow component Y had also which I didn't want for the component Y.

So my question is how can I update the style of nextgen-table from the parent component which only applies for the parent component and not in the other components.

I have also tried to use

:host(mat-cell:nth-child(1)){
  flex: 0 0 40%;
  text-align: left;
}

:host(mat-header-cell:nth-child(1)) {
    flex: 0 0 40%;
    text-align: left;
}

but nothing happened/changed.

Thanks in advance for the help

Upvotes: 1

Views: 16652

Answers (3)

Ste
Ste

Reputation: 685

The only reliable way i've ever found is to use ::ng-deep

Anything else seems to be "hit or miss" intermittently

Upvotes: 0

HirenParekh
HirenParekh

Reputation: 3735

All you need to do is use both :host and ::ng-deep pseudo-class selectors in your X or Y component.

Here is the working demo.

And here is the quick explanation.

styles which are written for the <nextgen-table> inside let say nextgen-table.component.css are get encapsulated by angular by adding a specific attributes for each style. i.e if you have written something like,

.mat-header-cell{
    background-color: #ff0000;
}

then it becomes something like,

.mat-header-cell[_ngcontent-c29]
    background-color: #ff0000;
}

So all we need to do is to override this style inside our component X or component Y.

We have ::ng-deep pseudo-selector which will prevent angular from encapsulating out component's css.

But using ::ng-deep will leak our css on to parent components as well. So to prevent that we need to encapsulate out ::ng-deep style. to do that we can use :host pseudo-selector.

so if we write following css inside component X,

:host ::ng-deep .x-table .mat-header-cell{
  background-color: lightblue;
}

then it will become something like,

[_nghost-c82] .x-table .mat-header-cell {
    background-color: lightblue;
}

now this above css selection has higher precedence than the style written in the table component .mat-header-cell[_ngcontent-c29].

That's how we can override child component's style inside any parent component. I hope this will help.

Update: As you can see in Angular's official docs that ::ng-deep is deprecated.

The shadow-piercing descendant combinator is deprecated and support is being removed from major browsers and tools. As such we plan to drop support in Angular (for all 3 of /deep/, >>> and ::ng-deep). Until then ::ng-deep should be preferred for a broader compatibility with the tools.

So if you don't want to be dependent on ::ng-deep than,

You can use ViewEncapsulation.None in your <nextgen-table> table component which you have already tried. Demo here

And to prevent the style from bleeding into other components you can scope the table's style by adding the selector in front of all the styles like this.

nextgen-table .mat-header-cell{
    background-color: #ff0000;
}

and then you do the same for your X component.

  • Disable view encapsulation using ViewEncapsulation.None
  • Then override styles on table component by writing styles that have higher specificity than the table's actual style.

disable encapsulation in side your X component,

@Component({
  selector: "app-x",
  styleUrls: ["x.component.css"],
  templateUrl: "x.component.html",
  encapsulation: ViewEncapsulation.None
})
export class XComponent {

}

then override the table's component style in x.compoent.css

app-x nextgen-table .mat-header-cell{
  background-color: lightblue;
}

If you don't want to disable the view encapsulation then you can write styles directly into global stylesheet styles.css.

Just remember that It's all about overriding and scoping your styles.

Upvotes: 5

David
David

Reputation: 34425

You can use the ::ng-deep pseudo class, to specifically target child elements without changing the view encapsultation for the whole component (which would mean that all its rules would leak).

Note: ::ng-deep has been marked as deprecated for since a few major versions now, but they will not remove suppoprt until they have a workaround.

parentX.html

<div class="compContainer">
    <nextgen-table></nextgen-table>
</div>

parentX.scss

::ng-deep .compContainer nextgen-table
{
    mat-cell:nth-child(1),
    mat-header-cell:nth-child(1) {
        flex: 0 0 40%;
        text-align: left;
    } 
    mat-cell:nth-child(2),
    mat-header-cell:nth-child(2) {
        flex: 0 0 20%;
    }
}

You could also add your css rules to the global style.scss file.

//Rules for parent X
app-parent-componentX .compContainer nextgen-table
{
    mat-cell...
}

//Rules for a parent Y
app-parent-componentY .compContainer nextgen-table
{
    mat-cell...
}

Upvotes: 5

Related Questions