5

How can I override the CSS on the first div generated by the <mat-form-field>?

Background

I have a bunch of fields in an <mat-card> and the last field is causing too much padding at the bottom of the card.

<mat-form-field id="this-feels-hacky-and-wrong"...>
    <!-- I want to remove the padding-bottom from this element -->
    <div class="mat-input-wrapper mat-form-field-wrapper">
      ...
    </div>
</mat-form-field>

padding to remove

In my stlye.css I put

#this-feels-hacky-and-wrong div{
  padding-bottom: 0;
}

and that works but there must be a better/right way to do this, no?

I would prefer a solution that is in my component.scss file.

spottedmahn
  • 14,823
  • 13
  • 108
  • 178

5 Answers5

12

Try using this ::ng-deep

html

<mat-form-field class="this-feels-better"...>

component.scss

:host ::ng-deep .this-feels-better div.mat-input-wrapper.mat-form-field-wrapper{
  padding-bottom: 0;
}

Alternative

If you really don't want to use ng-deep because it'll be removed in the future, and you really want to have your css rule in your component.scss file, then you can change the component's view encapsulation to None

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

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

However, in that case you need to make sure that you prefix all your CSS rules in that component with some kind of selector unique to the component, to prevent the rules leaking to other components.

.uniqueClassOnComponent .this-feels-better div.mat-input-wrapper.mat-form-field-wrapper{
   padding-bottom: 0;
}

//Other generic rules
.uniqueClassOnComponent span { ... }
David
  • 33,444
  • 11
  • 80
  • 118
  • [How to style child components from parent component's css file?](https://stackoverflow.com/a/36528769/185123). And [Angular Docs](https://angular.io/guide/component-styles#deprecated-deep--and-ng-deep): "As such we plan to drop support in Angular (for all 3 of /deep/, >>> and ::ng-deep)." – spottedmahn May 14 '18 at 12:05
  • 2
    They won't drop it until there is a replacement, which they haven't found yet. Otherwise, set the component's viewEncapsulation to none and your code should work in component.scss. But you should prefix all your rules in your component with some selector unique to the component so that the style does not leak to other components – David May 14 '18 at 12:21
  • Good point, no they won't! From my research, this is the vNext solution: [Add support for Shadow DOM V1](https://github.com/angular/angular/issues/23636) – spottedmahn May 14 '18 at 13:06
  • Just noticed this (or it was just added) "... Until then, ::ng-deep should be preferred for a broader compatibility with the tools." – spottedmahn Jun 04 '18 at 16:27
  • There's an alternative solution. If you have a global css file, you can place your class there, and your components can use those css rules without changing the ViewEncapsulation. – andreisrob Oct 15 '18 at 16:21
  • Search for `::ng-deep` in github/. There are 41k uses right now. It's not going anywhere without a fight. Until every library control can solve all these dumb padding and margin issues by itself it's just delusional to think shadow DOM will fix it! – Simon_Weaver Jun 28 '19 at 02:49
9

Using ::ng-deep or /deep/ was not a solution for me, as it's being deprecated soon.

ViewEncapsulation.None wasn't a good solution for me either, because it tends to break several other component styles when used.

You can add a custom style (such as no-padding) to your global style sheet.

//example:
mat-form-field.no-padding .mat-form-field-wrapper {
    padding: 0;
}

Then apply it to your Material UI Form Fields as needed.

//example:
<mat-form-field class="no-padding">
    <mat-label>First Name</mat-label>
    <input matInput placeholder="First Name">
</mat-form-field>
pistol-pete
  • 1,213
  • 17
  • 13
0

With CSS you can use the n-th term. As long as the div you are trying to target has the same parent div as the others, you can reference the first and last child div like such:
.my-div-class:first-child {}
and
.my-div-class:last-child {}

So further this, the nth-term can then be used to target any child like this:
.my-div.class:nth-child(2) {}
This would target the second child.

For some further clarification to your question, this is what your CSS should look like:

mat-form-field:last-child div {
   padding-bottom: 0;
}
lewisnewson
  • 410
  • 6
  • 22
0

Update: See pistol-pete's answer above. It's the same, essentially.

For those coming in later, There's another way to do this. Do not use ng-deep, in its various forms, as it's deprecated. Breaking encapsulation is not recommended and goes against Angular's design practice. The solution is global CSS, but with a specific selector.

mat-form-field is part of Angular Material. It has its own encapsulation. Global styles, not component styles, can override this. However, you may not want to override all of these elements, maybe just one or two. I solved this by using a unique class to isolate the elements I wanted to change, then adding .mat-form-field-wrapper to target the element with the padding. Here's an example:

<mat-form-field class="pb-0">
  <mat-label>Not so hacky</mat-label>
  <imput matInput formControlName="notHacky" />
</mat-form-field>

Then with the pb-0 class, or whatever you want to call it, target in a global CSS space. In Angular, that's styles.css. With Sass, you'll likely have other global files you're importing into the styles file. Add this:

/* sass */
mat-form-field {
  &.pb-0 {
    .mat-form-field-wrapper {
      padding-bottom: 0;
    }
  }
}

/* plain css */
mat-form-field.pb-0 .mat-form-field-wrapper
 {
  padding-bottom: 0;
} 

You can repeat this for any component from any framework since global styles will be applied everywhere. I would recommend creating a unique file to hold all of these overrides.

0

Try this one.

HTML

<div style="width: 100%" class="time-period-area">
  <mat-form-field class="no-padding" appearance="outline">
    <mat-label>Time Period</mat-label>
    <input matInput style="height: 0; width: 100%" value=" " />
    <div class="mat-input-wrapper mat-form-field-wrapper">
      ...
    </div>
  </mat-form-field>
</div>

css

.time-period-area {
  mat-form-field.no-padding {
    width: 100%;
    padding: 0;
  }
}
spottedmahn
  • 14,823
  • 13
  • 108
  • 178