Angularでレスポンシブ画面サイズを検出する
ほとんどの場合、CSSメディアクエリを使用してレスポンシブな画面サイズの変更を処理し、コンテンツのレイアウトを変えます。 ただし、CSSメディアクエリだけでは不十分な場合があります。 コードで応答性を処理する必要があります。
この記事では、Angularでレスポンシブブレークポイントを検出する方法について説明します- Typescriptコードでレスポンシブブレークポイントサイズを維持していません(レスポンシブブレークポイントはCSSですでに定義されているため) 。
この例ではBootstrapでAngularを使用しますが、CSSフレームワークとクラスで機能します。 はじめましょう。
どんな計画ですか
CSSクラスを使用して、現在のレスポンシブブレークポイントを決定します。 BootstrapCSSには5つのブレークポイントがあります。 各ブレークポイントのvisibilityを決定するCSSクラスは次のとおりです。
- xsでのみ表示:
.d-block .d-sm-none
- smでのみ表示:
.d-none .d-sm-block .d-md-none
- mdでのみ表示:
.d-none .d-md-block .d-lg-none
- lgでのみ表示:
.d-none .d-lg-block .d-xl-none
- xlでのみ表示:
.d-none .d-xl-block
CSS display
プロパティは、none
またはblock
の間で切り替えられます。 これらのクラスをHTML要素に適用します。
画面サイズが変更されるたびに、スタイルdisplay: block
のHTML要素をループして検索します。これにより、現在のブレークポイントが検出されます。
興奮して解決策が見当たらない場合のコードは次のとおりです:https://stackblitz.com/edit/angular-size。
実装:コンポーネント
Angularコンポーネントsize-detector
を作成しましょう。
コンポーネントHTMLテンプレート:
<!-- size-detector.component.html -->
<div *ngFor="let s of sizes" class="{{s.css + ' ' + (prefix + s.id) }}">{{s.name}}</div>
コンポーネントTypescriptコード:
// size-detector.component.ts
...
export class SizeDetectorComponent implements AfterViewInit {
prefix = 'is-';
sizes = [
{
id: SCREEN_SIZE.XS, name: 'xs', css: `d-block d-sm-none`
},
{
id: SCREEN_SIZE.SM, name: 'sm', css: `d-none d-sm-block d-md-none`
},
{
id: SCREEN_SIZE.MD, name: 'md', css: `d-none d-md-block d-lg-none`
},
{
id: SCREEN_SIZE.LG, name: 'lg', css: `d-none d-lg-block d-xl-none`
},
{
id: SCREEN_SIZE.XL, name: 'xl', css: `d-none d-xl-block`
},
];
@HostListener("window:resize", [])
private onResize() {
this.detectScreenSize();
}
ngAfterViewInit() {
this.detectScreenSize();
}
private detectScreenSize() {
// we will write this logic later
}
}
コンポーネントコードを見た後、それらのSCREEN_SIZE.*
値はどこから来ているのか疑問に思うかもしれません。 列挙型です。 screen size enum
を作成しましょう(新しいファイルを作成することも、同じコンポーネントファイルに列挙型を配置することもできます)
// screen-size.enum.ts
/_ An enum that define all screen sizes the application support _/
export enum SCREEN_SIZE {
XS,
SM,
MD,
LG,
XL
}
また、プロジェクトにBootstrapを追加することを忘れないでください! npmまたはyarnを介して追加できますが、この例では、より簡単な方法を使用します。 index.html
にcdnリンクを追加します。
<!-- index.html -->
<link rel="stylesheet"
href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css">
コード自体はかなり表現力豊かです。
- まず、サポートする
sizes
のリストと、各ブレークポイントを決定するために使用されるCSSクラスを定義します。 - HTMLでは、サイズリストをループし、
div
要素を作成し、cssを割り当てて表示します。 また、各div
に追加の一意のcssクラスis-<SIZE_ENUM>
を与えることに注意してください。 - 関数
detectScreenSize
があります。 ここで、画面サイズの変更を検出するロジックを記述します。 後で完了します。 - 画面サイズが変更されるたびにロジックを実行する必要があります。
HostListener
デコレータを使用して、window resize
イベントをリッスンします。 - また、アプリケーションを最初に初期化するときにロジックを実行する必要があります。
AfterViewInit
コンポーネントのライフサイクルフック中に実行する必要があります。
実装:サービスとコンポーネント
これで、コンポーネントコードの「ほぼ」準備が整いました。次に、resize service
の実装を開始しましょう。
// resize.service.ts
@Injectable()
export class ResizeService {
get onResize$(): Observable<SCREEN_SIZE> {
return this.resizeSubject.asObservable().pipe(distinctUntilChanged());
}
private resizeSubject: Subject<SCREEN_SIZE>;
constructor() {
this.resizeSubject = new Subject();
}
onResize(size: SCREEN_SIZE) {
this.resizeSubject.next(size);
}
}
サイズ変更サービスコードは単純です。
- rxjsサブジェクト
resizeSubject
を作成します。 size
をパラメーターとして受け取るパブリックメソッドonResize
があります。 次に、値をサイズ変更ストリームにプッシュします。 (このメソッドは、後でsize-detector
コンポーネントで呼び出します)- サイズ変更オブザーバブルで
distinctUntilChanged
演算子を使用していることに注意してください。 これを使用して、不要な通知を減らします。 たとえば、画面サイズが200pxから300pxに変更された場合でも、ブートストラップではxs
サイズと見なされます。 その場合、通知する必要はありません。 (必要に応じてオペレーターを削除できます) onResize$
を介してサイズ変更ストリームを監視可能なものとしてエクスポートします。 コンポーネント、サービス、ディレクティブなどは、このストリームにサブスクライブして、サイズが変更されるたびに通知を受け取ることができます。
次に、size-detector
コンポーネントに戻り、detectScreenSize
ロジックを更新しましょう。
// size-detector.component.ts
...
private detectScreenSize() {
constructor(private elementRef: ElementRef, private resizeSvc: ResizeService) { }
const currentSize = this.sizes.find(x => {
// get the HTML element
const el = this.elementRef.nativeElement.querySelector(`.${this.prefix}${x.id}`);
// check its display property value
const isVisible = window.getComputedStyle(el).display != 'none';
return isVisible;
});
this.resizeSvc.onResize(currentSize.id);
}
...
内訳を見て、一緒にロジックを調べてみましょう。
- まず、
ElementRef
と新しく作成したResizeService
をコンポーネントに挿入する必要があります。 - CSSクラスに基づいて、どの時点でも、表示されるHTML要素は1つだけです。
sizes
配列をループして、それを見つけます。 sizes
配列のサイズごとに、 HTML5要素のクエリセレクターを使用して、is-<SIZE_ENUM>
で以前に定義した一意のcssクラスによって要素を検索します。- 現在表示されている要素が見つかったら、
onResize
メソッドを呼び出してサイズ変更サービスに通知します。
サービスとコンポーネントの使用
size-detector
コンポーネントをルートコンポーネントapp-component
の下に配置できます。 例えば:
<!-- app.component.html -->
<hello name="{{ name }}"></hello>
<!-- Your size-detector component place here -->
<app-size-detector></app-size-detector>
この例では、app-component
に別のhello-component
がありますが、それは問題ではありません。
コンポーネントをapp-component
に配置しているので、ResizeService
をどこでも(ディレクティブ、コンポーネント、サービスなど)使用できることを意味します。
たとえば、hello-component
で画面サイズの変化を検出したい場合、コンストラクターにResizeService
を挿入し、onSizeChange$
をサブスクライブして、監視可能にします。私が必要なもの。
// hello.component.ts
@Component({
selector: 'hello',
template: `<h1>Hello {{size}}!</h1>`,
})
export class HelloComponent {
size: SCREEN_SIZE;
constructor(private resizeSvc: ResizeService) {
// subscribe to the size change stream
this.resizeSvc.onResize$.subscribe(x => {
this.size = x;
});
}
}
上記のコードでは、画面サイズの変更を検出し、現在の画面サイズの値を表示するだけです。
実際に見てください!
実際のユースケースシナリオの1つは、画面にアコーディオンがある場合です。 モバイルでは、すべてのアコーディオンパネルを折りたたんで、一度にアクティブなパネルのみを表示します。 ただし、デスクトップでは、すべてのパネルを展開することをお勧めします。
概要
これは、JavaScriptコードで実際のブレークポイントサイズを維持せずに、画面サイズの変更を検出する方法です。 コードは次のとおりです:https://stackblitz.com/edit/angular-size。
考えてみれば、ユーザーがアプリを閲覧するときに画面サイズを変更することはめったにありません。 画面サイズの変更は、アプリケーション全体で処理することも(上記の例のように)、必要なときにいつでも処理することもできます(ユースケース/コンポーネントごと)。
さらに、JavaScriptコードでブレークポイントのサイズを複製して維持することを気にしない場合は、コンポーネントを削除し、detectScreenSize
をサービスに移動して、ロジックを少し変更することができます。 それを実装することは難しくありません。 (おそらくやってみますか?)
それで全部です。 ハッピーコーディング!