본문 바로가기

Web + APP/Angular

Angular Flex-Layout using MediaObserver

반응형
SMALL

Angualr Flex-Layout Library에서 mediaOvserver라는게 있단다.

 

그거는 아래처럼 설치가 가능하며, app.module.ts 파일에 집어넣어서 쓰면 된단다.

npm install @angular/flex-layout
import { NgModule } from "@angular/core";
import { BrowserModule } from "@angular/platform-browser";
import { FormsModule } from "@angular/forms";
import { AppComponent } from "./app.component";

import { FlexLayoutModule } from "@angular/flex-layout";
@NgModule({
	imports: [BrowserModule, FormsModule, FlexLayoutModule],
    declarations: [AppComponent],
    bootstrap: [AppComponent],
})
export class AppModule {}

 

MediaObserver Service는 mediaQuery changes를 감지하는 Observable인데, isActive() 란ㄴ 메소드가 현재 mediaQuery가 active하는지를 체크도 해주는 기능을 가지고 있단다.

 

MediaObserver는 2개의 API가 있단다.

 

1. asObservable(): Ovservable<MediaChange>

2. isActive(query: string): boolean

 

우리는 이 서비스를 사용하기 위해서, 앵귤러의 의존성 주입(DI)를 통해서 MediaOvserver를 constructor parameter에 집어넣을 것이란다.

 

코드를 보자꾸나.

 

import { Component, OnDestroy, OnInit } from '@angular/core';
import { MediaChange, MediaObserver } from '@angular/flex-layout';
import { Subscription } from 'rxjs';
import { distinctUntilChanged } from 'rxjs/operators';

@Component({
	selector: 'app-root',
    templateUrl: './app.component.html',
    styleUrls: ['./app.component.css'],
})
export class AppComponent implements Oninit, OnDestroy {
	title = 'Angular Flex-Layout';
    
    /**
     *
     *
     * @param mediaObserver
     */
	constructor(public mediaObserver: MediaObserver) {}
    
    private mediaSubscription!: Subscription;
    private activeMediaQuery: string = '';
    
    ngOnInit(): void {
    	const getAlias = (MediaChange: MediaChange[]) => {
        	return MediaChange[0].mqAlias;
		};
        
        this.mediaSubscription = this.mediaObserver
        	.asObservable()
            .pipe(
            	distinctUntilChanged(
                	(x: MediaChange[], y: MediaChange[]) => getAlias(x) === getAlias(y)
				)
			)
            .subscribe((chage) => {
            	chage.forEach((item) => {
                	this.activeMediaQuery = item
                    	? `'${item.mqAlias}' = (${item.mediaQuery})`
                        : '';
					if (item.mqAlias === 'md') {
                    	this.loadMobileContent();
					}
                    console.log('activeMediaQuery', this.activeMediaQuery);
                });
            });
	}
    
    ngOnDestroy(): void {
    	this.mediaSubscription.unsubscribe();
	}
    
    loadMobileContent {
    	console.log('load mobile content');
        
        // Do something special since the viewport is currently
        // using mobile display sizes
	}
}
	}
}

 

현재 Angular Flex-Layout의 wiki page는 좀 오래된 예제란다. media$ observable이 mediaObserver로 대체가 되었단다.

 

change detection이 일어날 때, 약간의 버그가 나오는데, 결과가 중복되는 경우가 있단다.

 

그래서 위의 코드에서, distinctUntilChanged() RxJS를 operator를 써서, 중복을 없앴단다.

 

 

sm, lt-md, lt-lg, lt-xl, gt-xs 등등, 대부분의 사이즈를 알 수 있단다.

 

그래서 이걸 어떻게 쓸 수 있을거 같으니?

 

애플리케이션을 디자인할 때, 우리의 UI를 효과적 사용하기 위해서 우리는 스크린 뷰 사이즈를 알아야 한단다.

 

뭐, 숨기거나 조작하거나 이럴 때 쓸 수 있겠지

 

<div fxLayout="row" fxLayoutGap="10px">
	<div fxFlex="1 0 10" *ngIf="mediaObserver.isActive('md')"
    	id="boxone" style="background: red;">Box One</div>
	<div fxFlex="1 0 10" id="boxtow" style="background: blue;">Box Two</div>
</div>

browser가 md 사이즈에 오면, 2번째 줄 div는 사라지게 만들 수 있겠지.

 

mediaObserver를 HTML에 사용하기 위해선, get() 메소드로 접근을 할 수 있게 해줘야해

 

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
})
export class AppComponent implements OnInit, OnDestroy {
  title = 'Angular Flex-Layout';  

get media() {
    return this.mediaObserver;
  }

...
}

 

또 다른 예제를 들고와보자꾸나

 

import { Component, OnInit } from '@angular/core';
import { MediaChange, MediaObserver } from '@angular/flex-layout';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss'],
})
export class AppComponent implements OnInit {
  constructor(private mediaObserver: MediaObserver) {}

  cols: Observable<any> | undefined;

  ngOnInit(): void {
    const grid = new Map([
      ['xs', 1],
      ['sm', 2],
      ['md', 3],
      ['lg', 4],
      ['xl', 5],
    ]);

    this.cols = this.mediaObserver.asObservable().pipe(
      map((change: MediaChange[]) => {
        console.log(change[0]);
        console.log(grid.get(change[0].mqAlias));
        return grid.get(change[0].mqAlias);
      })
    );
  }

 

위에 코드는 MediaObserver를 constructor에 주입을 했으며, 새로운 grid라는 친구를 만들어서, mediaObserver를 asObservable()을 이용해서 observable로 세팅을 했단다.

 

그래서 browser가 resize될 때, breakpoint를 mqAlias를 통해서 받을 수 있단다. 그리고, 이 전에 선언한 grid에서 get 메소드를 호출해 cols 변수에 집어넣었단다.

 

<mat-grid-list rowHeight="1:1" [cols]="cols | async" gutterSize="6px">
  <mat-grid-tile> {{1}} </mat-grid-tile>
  <mat-grid-tile> {{2}} </mat-grid-tile>
  <mat-grid-tile> {{3}} </mat-grid-tile>
  <mat-grid-tile> {{4}} </mat-grid-tile>
  <mat-grid-tile> {{5}} </mat-grid-tile>
</mat-grid-list>

 

HTML에서 Angular Material grid list component를 사용해보았으며, async pipe를 통해 observable을 구독했단다.

 

그래서 browser 사이즈가 줄어들었을 때, 이를 캐치하여 원하는 값을 넣을 수 있겠지.

 

이렇듯 mediaObserver를 쓰면, 우리의 애플리케이션이 viewport 변화를 알아내어서 우리가 원하는 동작을 하도록 해보았단다.

 

만약, 휴대폰 전용 웹뷰 코드에다 해당 mediaObserver를 잘 버무리면, PC에서도 똑같은 서비스를 제공할 수 있겠지 ?

 


이상 Angular Flex-Layout using MediaObserver였습니다. ^_^

반응형
LIST

'Web + APP > Angular' 카테고리의 다른 글

Renderer2 in Angular 12 (Why not ElementRef ?)  (0) 2021.09.25
iOS 한글 buffer 문제  (1) 2021.09.11
RxJS 구독해제 패턴 심화편  (3) 2021.06.26
Async Pipe 뜯어먹기  (4) 2021.05.30
RxJS가 해결하려고 했던 문제 3  (6) 2021.05.20