4
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Angular MaterialのForm Field上部余白を消す

Posted at

はじめに

この記事の内容は正直邪道です(^^; (無理矢理やってます)
ここに手をくだそうとする時点で、デザイン方針の何かが間違っている気がしないでもない。

注意

この記事の解決策は以下の状況に当てはまる場合適用できません。

  • <mat-form-field>タグの中に複数<input>タグが存在し、それぞれに適用するスタイルを変えたい。
  • コンポーネントごとにスタイルを綺麗に切り分けて実装している

やりたいこと

Form Field実装したときの以下の赤枠余白を消したい。
スクリーンショット 2020-12-22 1.55.06.png

ソース (HTML一部のみ)

detailform.component.html
<label class="bg-lightblue">ラジオボタン + テキストボックス</label>
<div>
<mat-radio-group aria-label="Select an option" [(ngModel)]="radioandtext.value">
  <mat-radio-button class="radio-btn" *ngFor="let option of options" [value]="option">
    {{option}}
  </mat-radio-button>
  <mat-form-field appearance="standard">
    <input matInput [(ngModel)]="radioandtext.text" placeholder="">
  </mat-form-field>
</mat-radio-group>
</div>

bg-lightblueとかradio-btnは背景色とか余白とか調整しているだけで今回の話と無関係です。

元々ラジオボタンしかなかったところに「その他:テキストボックス」なんて追加すると、突然余白が現れて違和感あるなーってことがたまにある。多分。

解決策

SCSSに以下を追加して、その定義したクラスremove-topmat-form-fieldのクラスとして追加する!
そしてtsのComponentにも設定を追加!!

SCSS

detailform.component.scss
.remove-top {
  >.mat-form-field-wrapper {
    >.mat-form-field-flex {
      >.mat-form-field-infix {
        border-top: none;
      }
    }
  }
}

HTML (.remove-topのclass追加しただけ)

detailform.component.html
<label class="bg-lightblue">ラジオボタン + テキストボックス</label>
<div>
  <mat-radio-group aria-label="Select an option" [(ngModel)]="radioandtext.value">
    <mat-radio-button class="radio-btn" *ngFor="let option of options" [value]="option">
      {{option}}
    </mat-radio-button>
    <mat-form-field appearance="standard" class="remove-top">
      <input matInput [(ngModel)]="radioandtext.text" placeholder="">
    </mat-form-field>
  </mat-radio-group>
</div>

TypeScriptのコード (関係あるところ)

detailform.component.ts
import { Component, OnInit, ViewEncapsulation } from '@angular/core';

import { Radioandtext } from '../model/radioandtext';

@Component({
  selector: 'app-detailform',
  templateUrl: './detailform.component.html',
  styleUrls: ['./detailform.component.scss'],
  encapsulation: ViewEncapsulation.None,
})
export class DetailformComponent implements OnInit {

  radioandtext: Radioandtext = {
    value: '',
    text: ''
  }
  
  options: string[] = ["オプション1", "オプション2", "その他"];

  constructor(
  ) { }

  ngOnInit(): void {
  }
}
```

後述しますが、大事なのは`ViewEncapsulation`
`@Component`の中の`encapsulation: ViewEncapsulation.None,`の部分です。
**(意味合いを知らない状態で適用するのは、危険なのでご注意ください!!)**

いろいろ省略しているので、全体のコードを見たい場合は以下Githubのコードを参照ください。
[Githubのコード](https://github.com/nekotokurasu11/angular-app-with-aws/tree/removetop/server/angular-app/src/app/detailform)

画面はこんな感じになります。
![スクリーンショット 2020-12-25 1.20.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277737/3ca0897c-0406-b201-3042-cd4101929876.png)
(黄色の部分が無くなった!!)

## 解決策を導き出すまでの道のり①~問題点の洗い出し~
まず、あのにっくき黄色部分が何者であるか、Chromeのデベロッパーツールで調べます。
![border-top.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277737/0c3633e2-478f-727e-0493-f1704581fe20.png)

(虱潰しに探した)結果は、どうやら`.mat-form-field-infix`の`border-top`の部分がこの余白の正体らしいと分かります。

方針としては、この`border-top`を打ち消すクラスを定義していくことになります。

ソースの再掲 (HTML一部のみ)

```detailform.component.html
<label class="bg-lightblue">ラジオボタン + テキストボックス</label>
<div>
<mat-radio-group aria-label="Select an option" [(ngModel)]="radioandtext.value">
  <mat-radio-button class="radio-btn" *ngFor="let option of options" [value]="option">
    {{option}}
  </mat-radio-button>
  <mat-form-field appearance="standard">
    <input matInput [(ngModel)]="radioandtext.text" placeholder="">
  </mat-form-field>
</mat-radio-group>
</div>
```

まず、`.mat-form-field-infix`がどこに付与されているのか探すことになるのですが、上述した通り、書いたHTMLの中には存在しない……
てなわけで、デベロッパーツール上のHTMLソースを確認します。
![mat-form-field-infix.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277737/9f8e55ce-d8e5-e491-3cfc-73068cdc9267.png)


どうやら`mat-form-field`タグの中の、`<div class="mat-form-field-wrapper ~">`の中の、`<div class="mat-form-field-flex ~">`の中にあるらしいということが分かる。

そう。クラス`.mat-form-field-infix`が存在する要素はAngular Materialの内部で定義されているものであり、実装者からしたら**編集できない場所にある**のである!!!

ここで「できない」と言って、諦めても大丈夫だと思う。
(だってAngular Materialの問題だから!!)

## 解決策を導き出すまでの道のり②~実装方法の検討~
無理矢理でも余白を変えたい。とそう思ったなら、
まず考えられる手段は「`.mat-form-field-infix`のスタイルの定義自体を変えてしまう」でしょうか。

CSS等に慣れている人なら当然の話ですが、もしも上記手段でスタイルを変更した場合、
クラス`.mat-form-field-infix`が付与された要素のスタイルは**全て**変わってしまう。

できるだけ限定的な影響範囲でスタイルを変えるのが、得策でしょう。

そこで、今回考えたのが、
編集できない`.mat-form-field-infix`が付与された要素ではなく、
編集できる、`mat-form-field`の要素にクラスを追加して、`mat-form-field`から`.mat-form-field-infix`が付与された要素のスタイルを定義する方法になります。

これが以下(再掲)

SCSS

```detailform.component.scss
.remove-top {
  >.mat-form-field-wrapper {
    >.mat-form-field-flex {
      >.mat-form-field-infix {
        border-top: none;
      }
    }
  }
}
```

CSSなら

```css
.remove-top >.mat-form-field-wrapper >.mat-form-field-flex >.mat-form-field-infix {
        border-top: none;
}
```

HTML (`remove-top`のclass追加しただけ)

```detailform.component.html
<label class="bg-lightblue">ラジオボタン + テキストボックス</label>
<div>
  <mat-radio-group aria-label="Select an option" [(ngModel)]="radioandtext.value">
    <mat-radio-button class="radio-btn" *ngFor="let option of options" [value]="option">
      {{option}}
    </mat-radio-button>
    <mat-form-field appearance="standard" class="remove-top">
      <input matInput [(ngModel)]="radioandtext.text" placeholder="">
    </mat-form-field>
  </mat-radio-group>
</div>
```

これで`mat-form-field`要素のクラスに`remove-top`を付与した中のスタイル**だけ**変更することができます。
影響範囲をできうる限り小さくできる寸法です。

逆にいうと、`remove-top`を付与した`mat-form-field`中のスタイルは全て変更されてしまうので[注意](#注意)で書いたように、「複数`<input>`タグが存在し、それぞれに適用するスタイルを変えたい」場合は、上記のSCSSは適用無理です。

ここからはAngular独自の話。

(また再掲)
TypeScriptのコード (関係あるところ)

``````detailform.component.ts
import { Component, OnInit, ViewEncapsulation } from '@angular/core';

import { Radioandtext } from '../model/radioandtext';

@Component({
  selector: 'app-detailform',
  templateUrl: './detailform.component.html',
  styleUrls: ['./detailform.component.scss'],
  encapsulation: ViewEncapsulation.None,
})
export class DetailformComponent implements OnInit {

  radioandtext: Radioandtext = {
    value: '',
    text: ''
  }
  
  options: string[] = ["オプション1", "オプション2", "その他"];

  constructor(
  ) { }

  ngOnInit(): void {
  }
}
```



tsに加えた設定
`@Component`の中の`encapsulation: ViewEncapsulation.None,`ですが
これは**カプセル化は一切行われずすべてのスタイルをグローバルスコープに展開する**設定です
元々カプセル化されてたの?」という感じかもしれませんが詳しくは以下参照でお願いします
[公式](https://angular.jp/api/core/ViewEncapsulation)
[Angular 2: Component のスタイル実装と CSS のカプセル化](https://qiita.com/jimbo/items/b347c19d935e796c2482)

ざっくりいうと何も設定しない場合はカプセル化された状態になり
例えば別々のコンポーネントで同名のクラスを別々のスタイルとして定義してもコンポーネントごとにスタイルが適用されます競合することがない

ここは私の予想解釈ですが`mat-form-field`の要素はAngular Materialで定義されている別のComponentのようなもので
カプセル化された状態のままだと上記のSCSSは`mat-form-field`の中にスタイルをあてることはできないようです

今回の方針だと`encapsulation: ViewEncapsulation.None,`は必須になります

[注意](#注意)で書きましたがコンポーネントごとにスタイルを綺麗に切り分けて実装している=カプセル化を大いに利用している場合はこの設定はできません。「Form Field上部余白を消すのは無理、、ということになります

## おわり
どうにかスタイルを微調節できないかと苦心した結果です(^^;
参考程度でお願いします

4
1
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?