LoginSignup
4
1

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のコード

画面はこんな感じになります。
スクリーンショット 2020-12-25 1.20.48.png
(黄色の部分が無くなった!!)

解決策を導き出すまでの道のり①~問題点の洗い出し~

まず、あのにっくき黄色部分が何者であるか、Chromeのデベロッパーツールで調べます。
border-top.png

(虱潰しに探した)結果は、どうやら.mat-form-field-infixborder-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

どうやら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なら

.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,ですが、
これは、カプセル化は一切行われず、すべてのスタイルをグローバルスコープに展開する設定です。
「え、元々カプセル化されてたの?」という感じかもしれませんが、詳しくは以下参照でお願いします。
公式
Angular 2: Component のスタイル実装と CSS のカプセル化

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

ここは私の予想・解釈ですが、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