0

I'm using ViewEncapsulation.None to override the material styles of my custom mat-form-field component that I use in my login page. However, I have reached a scenario where down the line in the app, whenever I use another mat-form-field in another component, some styles that are used in the login page mat-form-field are inherited, leaving the current component styles with no effect.

You can see what I mean below. This is the first instance where I use the first mat-form-field:

enter image description here

But when I use a mat-form-field again, in another component, some styles are ignored, like so:

enter image description here

When in reality, this would be the desired result:

enter image description here

What can I do to override these inherited styles?

Thanks in advance.

Manuel Brás
  • 413
  • 7
  • 21

1 Answers1

1

If you want your styles to only apply to the custom field, rather than any direct usages of mat-form-field.

You can set up your custom field with normal view encapsulation (as long as you aren't using the shadow-dom)

Then you use ::ng-deep to pierce the encapsulation.

For example one method I often use for selectors that don't select anything rendered by the component itself (ones that won't have the hash code attribute for the view encapsulation) is to select the :host element and then do a descendant combinator from there with ::ng-deep.

:host ::ng-deep .mat-form-field {
 background: pink;
}

Note, you might need to play around with things a little and double check which styles are/aren't getting applied via the dev tools.


Background Information

:host selects the host element. This element is what angular automatically creates as the parent of all components. It'll be kebob case component name and would be selectable via :host which gets the current component's host element (via attribute selector), or it can generally be selected via component-name {} as if it is a normal element.

The host element is what classes and inline styles get applied to, because Angular knows they are always there. There is also no way to avoid having a host element in Angular (there was a way back in angular.js)

::ng-deep as mentioned is "deprecated". The problem is... there's no replacement for it. Working with other libraries while trying to encapsulate some changes, it's easy enough to keep using ng-deep. Google has never given a removal version for it despite being "deprecated", and there have been several major versions since that deprecation. From an Angular issue, it looks like they don't plan to remove ng-deep until there's a replacement.

That being said, if you really don't want to use ng-deep, then you can remove ViewEncapsulation and replace the :host ::ng-deep by selecting the host element yourself via the host element's element selector. For example, component-name .mat-form-field{} will select .mat-form-field class only when a child of the host element component-name.

Zachary Haber
  • 10,376
  • 1
  • 17
  • 31
  • That seemed to solved my problem. I was aware of `ng::deep` but was a little hesitant on using it since it is deprecated but the frustrations of using `ViewEncapsulation.None` might just ecplise the worry of using a deprecated combinator. Btw, I don't really understand the `:host` part, could you explain? Should I always use it with `::ng-deep`? – Manuel Brás Jun 07 '21 at 15:12
  • 1
    @ManuelBrás, hopefully this answers all your questions! – Zachary Haber Jun 07 '21 at 15:37
  • Thanks for the awesome answer! `::ng-deep` seems to make my life easier right now. Also, is there a way I can group all the material styles I'm using under a single `::ng-deep`? Would be cleaner that way, if possible. – Manuel Brás Jun 07 '21 at 15:51
  • 1
    I believe that using scss/less, you can use nested classes and it'll work fine. `:host ::ng-deep{ .mat-form-field:{ } }` should work in theory. – Zachary Haber Jun 07 '21 at 15:53
  • I can't get this to work the other way around (status form to login form) though. The login form field won't have the correct styles. This is pissing me off. – Manuel Brás Jun 07 '21 at 18:42
  • 1
    Try passing in a relevant class to the mat form field, and using those to style off of instead of the more generic MUI classes. That way you are using completely different classes as a base between the login form and the status form. – Zachary Haber Jun 07 '21 at 19:04
  • Could you give an example? I tried this before but didn't manage to get it working, must have done something wrong for sure. – Manuel Brás Jun 07 '21 at 19:22
  • Here's an example of setting a class on the MUI components, and then styling off of those without bleeding via ViewEncapsulation.None: https://stackblitz.com/edit/angular-dwcqgd?file=src%2Fapp%2Fform-field-theming-example.css Obviously you'll have to be careful whenever you use ViewEncapsulation.None. – Zachary Haber Jun 07 '21 at 20:15