ほとんどの場合、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">

コード自体はかなり表現力豊かです。

  1. まず、サポートするsizesのリストと、各ブレークポイントを決定するために使用されるCSSクラスを定義します。
  2. HTMLでは、サイズリストをループし、div要素を作成し、cssを割り当てて表示します。 また、各divに追加の一意のcssクラスis-<SIZE_ENUM>を与えることに注意してください。
  3. 関数detectScreenSizeがあります。 ここで、画面サイズの変更を検出するロジックを記述します。 後で完了します。
  4. 画面サイズが変更されるたびにロジックを実行する必要があります。 HostListenerデコレータを使用して、window resizeイベントをリッスンします。
  5. また、アプリケーションを最初に初期化するときにロジックを実行する必要があります。 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);
  }

}

サイズ変更サービスコードは単純です。

  1. rxjsサブジェクトresizeSubjectを作成します。
  2. sizeをパラメーターとして受け取るパブリックメソッドonResizeがあります。 次に、値をサイズ変更ストリームにプッシュします。 (このメソッドは、後でsize-detectorコンポーネントで呼び出します)
  3. サイズ変更オブザーバブルでdistinctUntilChanged演算子を使用していることに注意してください。 これを使用して、不要な通知を減らします。 たとえば、画面サイズが200pxから300pxに変更された場合でも、ブートストラップではxsサイズと見なされます。 その場合、通知する必要はありません。 (必要に応じてオペレーターを削除できます)
  4. 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);
}

...

内訳を見て、一緒にロジックを調べてみましょう。

  1. まず、ElementRefと新しく作成したResizeServiceをコンポーネントに挿入する必要があります。
  2. CSSクラスに基づいて、どの時点でも、表示されるHTML要素は1つだけです。 sizes配列をループして、それを見つけます。
  3. sizes配列のサイズごとに、 HTML5要素のクエリセレクターを使用して、is-<SIZE_ENUM>で以前に定義した一意のcssクラスによって要素を検索します。
  4. 現在表示されている要素が見つかったら、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をサービスに移動して、ロジックを少し変更することができます。 それを実装することは難しくありません。 (おそらくやってみますか?)

それで全部です。 ハッピーコーディング!