Angular State Management (NGXS) Code

2018. 9. 22. 23:04

오늘은 지난 글에서 소개했던 ngxs의 코드를 살펴보겠습니다.


아래와 같은 순서로 만들어 가게 되는데요.


1. Action 추가 (소비 내역 추가)

2. State 추가 (하루 소비 내역)

3. Select 추가 (component에서 하루 소비 내역 조회)


1. Action 추가


action은 데이터를 변경하는 동작을 말합니다.

소비 내역을 추가하는 action을 정의하였습니다.

(저는 가계부를 만드는 개인 프로젝트 진행중입니다.)

import { Consumption } from "../domain/consumption";

export class AddConsumption {
    static readonly type = '[Budget Page] Add Consumption';
    constructor(public consumption: Consumption) {}
}

지난 번 생성한 Consumption 도메인을 활용합니다.


type에는 어떠한 action인지 설명을 적어줍니다.

ngxs 공식 페이지에서는 아래와 같은 규칙을 권장합니다.

- 명령이 수행되는 맥락 (ex: [User API],[Product Page])

- 행위를 설명하는 동사 (ex: Get, Add, Delete)

- 행위의 대상 Entity (ex: User, Product)


constructor는 추가할 consumption을 인자로 받습니다.


즉, 설명과 Action이 수행될 metadata를 정의해주는 것입니다.


2. State 추가


State의 이름과 기본값, action의 실제 동작을 정의합니다.

ngxs 코드의 가장 핵심 부분이라고 볼 수 있습니다.


State는 @State라는 annotation을 사용하여 정의합니다.

import { Action, State, StateContext } from '@ngxs/store';
import { DailyExpense } from '../domain/dailyExpense';
import { AddConsumption } from './budget.actions';

@State({
    name: 'dailyExpense',
    defaults: {
        datetime: new Date(),
        consumptions: [],
    }
})
export class DailyExpenseState {
    @Action(AddConsumption)
    addConsumption(context: StateContext, action:AddConsumption){
        const state = context.getState();
        context.setState({
            ...state,
            consumptions: [
                ...state.consumptions,
                // this is the new consumption instance that we add
                action.consumption,
            ],
        });
    }
}

name은 Store에서 State를 구분하는 역할을 합니다.

따라서 유일(Unique)한 이름을 부여해야만 합니다.


기본값은 날짜와 소비내역 배열로 하였습니다.


State class에는 action과 dependency를 정의합니다.

저는 아직 dependency는 정의하지 않았구요.

action만 정의하였습니다.


Action 추가에서 action의 class를 만들었죠.

실제 수행되는 코드는 State안에 있습니다.

@Action annotation을 사용합니다.

context와 action을 인자로 받는 method를 만듭니다.

context에는 ngxs Store에 저장된 State가 담겨있습니다.

그것을 가져와서 action을 수행합니다.


3. Select 활용


이제 실제로 Component에서 State를 활용해보겠습니다.

먼저 app.module.ts에 import를 해주어야합니다.

...
import { NgxsModule } from '@ngxs/store';
import { DailyExpenseState } from './budget/budget.state';

@NgModule({
  declarations: [
    ...
  ],
  imports: [
    ...
    NgxsModule.forRoot([
      DailyExpenseState
    ])
  ],
  bootstrap: [AppComponent]
})
export class AppModule { }

ngxs 모듈에 정의한 State를 넣어주었습니다.


다음은 State를 사용할 Component의 코드를 보겠습니다.

...
import { Store, Select } from "@ngxs/store";
import { Observable } from 'rxjs';

@Component({
    ...
})
export class BudgetComponent implements OnInit {
    ...
    // ngxs select
    @Select(state => state.dailyExpense) dailyExpense$: Observable<DailyExpense>;

    // ngxs store dependency injection
    constructor(private store: Store) { }

    ngOnInit() {
        this.balance = this.budget;
        this.date = new FormControl(new Date());

        this.dailyExpense$.subscribe((dailyExpense) => {
            // using state data
            ...
        })
    }

    // using ngxs action 
    addConsumption(consumption: Consumption) {
        this.store
            .dispatch(new AddConsumption(consumption));;
    }
}

State를 조회하기 위해 @Select annotation을 사용합니다.

State중 가져올 값을 lambda 형태로 정의해주면 됩니다.

그리고 저장할 변수값을 정하죠. 

보통 $를 붙여서 state임을 표시합니다. 


타입을 보면 아시겠지만 state는 rxjs의 Observable 입니다.

따라서 subscribe하면 값이 화면에 동적으로 반영되죠.


state를 변경할 때는 설명드린바와 같이 action을 사용합니다.

action을 사용할 때에는 생성자에 주입한 Store를 이용합니다.

Store에 action 객체를 생성하여 dispatch하는 형태입니다.


복잡해보이지만 천천히 살펴보시면 쉽게 이해가 되실 겁니다.

지난 포스팅의 ngxs의 개념이 도움이 되실거에요.

(http://dschci.tistory.com/111)


아래는 ngxs를 기반으로 동작하는 화면입니다.


ngxs를 활용한 화면 예제


다음에는 동작화면의 Material Form에 대해 알아볼게요.

진행하는 작업은 github에서 보실 수 있습니다.


소스주소: https://github.com/jsrho1023/account-book

'IT Tech > Angular' 카테고리의 다른 글

Angular Forms (Validation)  (0) 2018.10.14
Angular Forms (Simple)  (0) 2018.10.07
Angular State Management (NGXS)  (0) 2018.09.15
Npm 거슬리는 pacakge-lock.json?  (2) 2018.09.02
Angular 업데이트(Update from 5 to 6)  (0) 2018.05.19

TechTrip IT Tech/Angular

Angular State Management (NGXS)

2018. 9. 15. 22:57

지난 번에는 Service를 만들어 비즈니스 로직을 분리해봤습니다.


원래 Service에 RxJS를 더해 직접 구현하려다가

(Angular 기본접근법은 그렇습니다) 

조금 변경하여 State Management 라이브러리를 도입하기로 하였습니다.

Application 복잡도는 낮지만 배움과 재미를 위한 개발이라서요.


라이브러리는 많은 선배 개발자들의 고민을 바탕으로 하고 있습니다.

그렇게 만들어진 좋은 구조를 익혀가는 것도 의미가 있다고 생각되었어요.


Angular의 State Management를 생각하면 먼저 떠오르는 것은 ngrx입니다.

하지만 저는 ngxs를 써보려고 합니다.


그 이유는 ngxs가 더 Angular 답기 때문입니다.


ngrx의 출발은 Angular가 아닌 React입니다.

React는 Redux라는 State Management 라이브러리를 가지고 있습니다. 

 ※ React는 Facebook에서 만든 Front-End Library

Redux를 Angular로 가져온 것이 바로 ngrx 입니다.

잘 사용하려면 먼저 Redux를 이해할 필요가 있습니다.

Redux를 이해하려면 React의 라이프사이클에 익숙해야하죠.

즉, Learning Curve가 높을 수 있습니다.


그에 반해 ngxs는 ngrx보다는 새로운 angular 전용 라이브러리입니다.


오늘은 ngxs의 간단한 소개를 할까 합니다.

ngxs는 4가지 중요한 개념이 있습니다.

(다른 State Management 라이브러리도 비슷한 개념을 가집니다.)


먼저 doc site의 설명을 그대로 가져오면 아래와 같습니다.

State: state를 정의하는 class

Action: 수행하는 action과 metadata에 대한 정보가 담긴 class 

Selects: Store에서 state의 일부를 가져오는 function

Store: state의 저장소이자, action, select를 관리하는 전역 객체


네 이해 안되실거에요. 저도 그렇습니다.


제가 작업하면서 개념적으로 이해한 것은 아래와 같습니다.

State: Angular Component가 화면에 보여줘야하는 데이터

Action: 서버와 통신(비동기)하고 데이터를 변경하는 역할

Selects: 데이터를 Angular Component에게 가져오는 역할

Store: State를 저장, action과 select를 관리/참조하는 중심


저는 머리 속에 아래 그림처럼 정리하였습니다.

사실 많은 부분을 생략하여 그린 그림입니다.

이해를 돕는 차원에서 봐주시면 될 것 같아요.


ngxs 중요 개념 (많은 부분 생략)


그래서 ngxs 라이브러리를 사용하면 좋은 점은 무엇일까요?

스스로 개발해야했던 부분이 이미 상당부분 구현되어있습니다.

아마도 제가 스스로 만드는 것보다 훨씬 잘 만들어져있겠죠.


서버와의 통신 부분이 구조화/모듈화될 수 있습니다.

코드 관리가 더 수월해지는 장점이 있습니다.


다음 포스팅에서는 실제 코드로 작성된 부분을 공유해볼게요.


정보출처:

ngxs document site - https://ngxs.gitbook.io/ngxs


'IT Tech > Angular' 카테고리의 다른 글

Angular Forms (Simple)  (0) 2018.10.07
Angular State Management (NGXS) Code  (0) 2018.09.22
Npm 거슬리는 pacakge-lock.json?  (2) 2018.09.02
Angular 업데이트(Update from 5 to 6)  (0) 2018.05.19
Angular Service 만들기  (2) 2018.05.13

TechTrip IT Tech/Angular

Angular 업데이트(Update from 5 to 6)

2018. 5. 19. 18:16

진행이 느리다보니 Angular가 버젼업이 되었네요.

Angular는 버젼 업이 꽤 빠른 편입니다.

2017년 11월 5 버젼이 나왔는데, 다음해 5월 다시 6 버젼이 나오네요.


불행인지 다행인지 진행한 내용이 많지 않아서 쉽게 업데이트가 됩니다.

오늘은 Angular 버젼업에 대해 공유해보겠습니다.


Angular에서 Version 업데이트를 위한 별도의 사이트도 있네요.

버젼과 복잡도, 방법을 입력하면 상세한 가이드를 볼 수 있습니다.


https://update.angular.io/


아래 나오는 스텝을 (조금 추가해서) 따라하면 됩니다. 

명령어 실행 시 관리자 권한이 필요합니다.


0-1. 구버젼 의존성 확인


@angular/upgrade 패키지

downgradeComponent 

downgradeInjectable

UpgradeComponent

UpgradeModule

위 모듈들을 @angular/upgrade/static 으로 변경

(저는 위 모듈들을 사용하지 않았었죠.)


HttpModule, Http 서비스

HttpClientModule, HttpClient 서비스로 변경

(이 역시도 사용하기 직전이었습니다.)


0-2. Node version 확인 (8 이상)


node --version


1. Angular CLI 업데이트 및 설정 파일 업데이트: 약 3분


npm install -g @angular/cli

npm install @angular/cli

npm install

npm update

ng update @angular/cli


2. Angular Framework package 업데이트: 약 20초


ng update @angular/core


3. Angular Material 업데이트: 약 20초


ng update @angular/material


4. Angular 이외 다른 의존 패키지 업데이트: 약 10초


이 부분은 App에 사용한 패키지에 따라 다르겠죠.


npm prune

npm update


5. RxJS 업데이트: 약 15초


npm install -g rxjs-tslint

rxjs-5-to-6-migrate -p src/tsconfig.app.json

npm uninstall -g rxjs-tslint


저는 rxjs-tslint를 다시 사용하지 않을 것 같아 지웠습니다.


위의 과정을 거치고 6.0 버젼으로 업그레이드가 완료되었습니다.


그런데..

ng serve 가 동작하지 않습니다.

아래와 같은 에러메시지와 함께요.

Cannot find module 'webpack/lib/RequestShortener'

아무래도 webpack이 설치 되어야할 것 같죠?


npm install --save-dev webpack


이제 앞으로는 Angular 6로 작업해보도록 하겠습니다.

(기본 기능에는 큰 차이가 없을 것 같습니다.)

'IT Tech > Angular' 카테고리의 다른 글

Angular State Management (NGXS)  (0) 2018.09.15
Npm 거슬리는 pacakge-lock.json?  (2) 2018.09.02
Angular Service 만들기  (2) 2018.05.13
Angular Domain Model  (0) 2018.04.24
Angular 테스트(Test) 하기  (0) 2018.04.11

TechTrip IT Tech/Angular

Angular Service 만들기

2018. 5. 13. 19:12

지난 번에는 데이터를 분리했었죠.

이번에는 비즈니스 로직을 좀 나누어 보겠습니다.

(사실 비즈니스 로직이 없...)


Angular의 Service를 이용할 예정입니다.

먼저 왜 Service를 이용하는지 알아보죠.


Service는 Angular에서의 DI(Dependency Injection)을 구현하는 필수 요소입니다.

DI 패턴은 객체의 관리(변경, 테스트 등)를 용이하도록 만들어주는 방식이죠.

코드의 재사용성을 높이고, 모듈 간의 결합도를 낮추기 때문인데요.

Angular에서 DI를 용이하도록 준비해둔 것이 바로 이 Service라는 것!


그럼 역시 코드를 살펴보도록 하겠습니다.


간단하게 cli로 Service를 생성할 수 있습니다.


1. Service 생성


ng generate service {서비스이름}

(축약: ng g s {서비스이름})


아래와 같이 Injectable을 import하여 서비스가 생성됩니다.

이름을 짓고 메소드를 추가해주었구요.

import { Injectable } from '@angular/core';
import { DailyExpense } from "../domain/dailyExpense";

@Injectable()
export class DailyExpenseService {
    getDailyExpense(date:Date) {
        ....
        return dailyExpense
    }
}


2. Service의 등록


아래와 같이 생성한 Service를 AppModule에 추가합니다.

providers라는 속성에 Array 값의 하나로 넣었습니다.

(Service를 많은 Component에서 재사용하기 위함)


특정 Component에만 사용되는 경우가 있겠죠?

그러면 해당 Component의 providers에 추가하면 됩니다.

import { DailyExpenseService } from './service/daily-expense.service';

@NgModule({
  declarations: [
    ...
  ],
  imports: [
    ...
  ],
  providers: [DailyExpenseService],
  bootstrap: [AppComponent]
})
export class AppModule { }


그러면 이제 Service를 Component에서 사용할 준비를 마쳤습니다.


3. Service의 사용


아래와 같이 import를 해주고, 생성자에 Service를 argument로 넣어줍니다.

import { DailyExpenseService } from '../service/daily-expense.service';

@Component({
    ...
})
export class BudgetComponent implements OnInit{
    ...
    dailyExpenseService: DailyExpenseSerivce;
    
    constructor(dailyExpenseService : DailyExpenseService){
        this.dailyExpenseService = dailyExpenseService;
    }

    ngOnInit(){
        ...
        this.dailyExpense = this.dailyExpenseService.getDailyExpense(new Date());
        this.dataSource = new MatTableDataSource(this.dailyExpense.consumptions);
        ...
    }
}


예상하신 대로입니다.

Service를 arg로 가진 Component에 Angular가 알아서 Service를 주입합니다.

Component에서는 Service가 가진 method들을 자유롭게 활용할 수 있구요.

굉장히 편리하죠?


Service를 분리하면서 테스트도 더욱 쉬워졌습니다.

Component를 만들 필요 없이 Service를 단위 테스트하면 되니까요.

그리고 그에 더해 여러 Component에서 함께 사용할 수 있습니다.


제가 포스팅을 시작한 Angular 5 버젼에서는 위처럼 사용하였습니다.


p.s: 6 버젼으로 업그레이드되면서 추가된 기능이있죠.

아래와 같이 service의 decorator에 provideIn 속성을 넣습니다.

import { Injectable } from '@angular/core';
import { DailyExpense } from "../domain/dailyExpense";

@Injectable({providedIn: 'root'})
export class DailyExpenseService {
    getDailyExpense(date:Date) {
        ....
        return dailyExpense
    }
}

위와 같이 하면 2번의 과정을 생략할 수 있습니다.

Scope을 Service안에서 잡아주는거죠.

좀 더 직관적인 면이 있습니다.

그리고 기능적으로는 Lazy loading이 추가되었다고 하네요.

'IT Tech > Angular' 카테고리의 다른 글

Npm 거슬리는 pacakge-lock.json?  (2) 2018.09.02
Angular 업데이트(Update from 5 to 6)  (0) 2018.05.19
Angular Domain Model  (0) 2018.04.24
Angular 테스트(Test) 하기  (0) 2018.04.11
Angular Material Table  (0) 2018.04.02

TechTrip IT Tech/Angular

Angular Domain Model

2018. 4. 24. 21:21

도메인 모델이라고 쓰고, 데이터의 분리라고 읽어야할 것 같습니다.

지금까지 Budget Component에 데이터를 다 넣어뒀었는데요.


테이블 데이터를 아래와 같이 가지고 있습니다.

export class BudgetComponent implements OnInit{
    ...
    consumptions = [{
        amount: 12000,
        desc: "food"
    },{
        amount: 10000,
        desc: "beverage"
    },{
        amount: 12000,
        desc: "dissert"
    }];
    dataSource = new MatTableDataSource(this.consumptions);
    ...
}


이 부분을 조금씩 덜어내보려고 합니다.

TypeScript는 ECMAScript 2015 문법을 사용할 수 있죠.


계속 사용해오던 export, import가 그에 해당합니다.

src/domain 이라는 폴더를 만듭니다.

Consumption이라는 Module을 아래와 같이 만들어줍니다.

export class Consumption {
    amount: number;
    desc: string;

    constructor (amount:number, desc:string){
        this.amount = amount;
        this.desc = desc;
    }
}


그리고 BudgetComponent에서 아래와 같이 사용합니다.

import { Consumption } from '../domain/consumption';
    ...
    consumptions = [
        new Consumption(12000, "food"),
        new Consumption(10000, "beverage"),
        new Consumption(5000, "dissert")];

    dataSource = new MatTableDataSource(this.consumptions);
    ...


이번엔 consumptions를 분리해볼까요.

날짜별로 소비를 정리한다고 생각하고 만들어봅니다.

역시 Consumption을 사용해줘야하기 때문에 import하구요.

consumption을 추가할 수 있는 method도 만들어줍니다.

import { Consumption } from "./consumption";

export class DailyExpense{
    datetime: Date;
    consumptions: Array;

    constructor(datetime: Date){
        this.datetime = datetime;
        this.consumptions = new Array();
    }

    addConsumption(consumption: Consumption){
        this.consumptions.push(consumption);
    }
}


그러면 DailyExpense를 사용해봅시다.

Consumption을 사용할 때와 동일합니다. 

import { DailyExpense } from '../domain/dailyExpense';
import { Consumption } from '../domain/consumption';

...    
    dailyExpense : DailyExpense;

    dataSource : MatTableDataSource; 
    remain: number;

    ngOnInit(){
        ...
        this.dailyExpense = new DailyExpense(new Date());
        this.dailyExpense
              .addConsumption(new Consumption(12000, "food"))
        this.dailyExpense
              .addConsumption(new Consumption(10000, "beverage"))
        this.dailyExpense
              .addConsumption(new Consumption(5000, "dissert"))
        this.dataSource 
              = new MatTableDataSource(this.dailyExpense.consumptions);
        ...
    }


이번에는 ngOnInit()에서 데이터를 만들어 넣었습니다.

좀 더 복잡해보이지만 유연해졌죠.

결과물은 이전과 동일합니다만...

데이터 변경 및 관리를 개발하기 더 수월해졌습니다.

'IT Tech > Angular' 카테고리의 다른 글

Angular 업데이트(Update from 5 to 6)  (0) 2018.05.19
Angular Service 만들기  (2) 2018.05.13
Angular 테스트(Test) 하기  (0) 2018.04.11
Angular Material Table  (0) 2018.04.02
Angular Material Icon  (0) 2018.03.18

TechTrip IT Tech/Angular

Angular 테스트(Test) 하기

2018. 4. 11. 22:00

Angular의 테스트에 대해 보고자 합니다.

여지껏 테스트는 안해왔는데요.

(테스트할 코드가 많지 않았기도했지만...)


Angular는 Jasmine이라는 테스트 프레임웍을 사용합니다.

Jasmine은 테스트를 이해하기 쉽게 정리하는 것을 돕습니다.


아래 예제를 참고해보도록 하죠.

describe('BudgetComponent', () => {
  ...
  it('should create', () => {
    expect(component).toBeTruthy();
  });
  ...
}


테스트 코드의 일부를 가져와봤습니다.

it은 무엇이 테스트 되는지를 보여줍니다.

describe는 여러개의 it을 묶어서 설명합니다.


또한 Jasmine은 테스트를 편리하게 하기 위한 API를 제공하죠.

예를들어 아래와 같은 메서드들이 있습니다.

expect().nothing();
expect(thing).toBe(expected);
expect(result).toBeDefined();
expect(result).toBeGreaterThan(3);
expect(result).toBeLessThan(0);
expect(thing).toBeNaN();
expect(result).toBeNull();
expect(thing).toBeTruthy();
expect(array).toContain(anElement);
expect(string).toContain(substring);
expect(bigObject).toEqual({"foo": ['bar', 'baz']});
...


메서드의 이름만 봐도 어떤 걸 테스트하는지 이해가 됩니다.

더 많은 메서드들은 아래 출처에서 참고하시면 되구요.


Angular 테스트를 수행하려면 아래와 같은 명령어 하나면 됩니다.

ng test


위와 같이 실행하면 브라우저에 아래와 같은 화면이 뜹니다.


Angular Test 실행화면



Karma라는 툴이 실행됨을 알 수 있는데요.

Jasmine 테스트를 브라우저로 쉽게 수행하는 걸 돕습니다.

Jasmine 테스트를 위해 html을 작성해야하는데요.

Karma가 대신하도록 Angular에 이미 설정이 되어있습니다.


그럼 실제 테스트 코드를 보도록 하죠.

Budget Component를 테스트하는 코드입니다.

budget.component.spec.ts 파일이에요.

테스트 코드는 spec 파일에 작성하게 됩니다.

import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { MatInputModule } from '@angular/material/input';
import { MatTableModule } from '@angular/material/table';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { BudgetComponent } from './budget.component';

describe('BudgetComponent', () => {
  let component: BudgetComponent;
  let fixture: ComponentFixture;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [ BudgetComponent ],
      imports: [ MatInputModule, MatTableModule, BrowserAnimationsModule ]
    })
    .compileComponents();
  }));

  beforeEach(() => {
    fixture = TestBed.createComponent(BudgetComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  it('should create', () => {
    expect(component).toBeTruthy();
  });

  it('should have budget value', () => {
    const budgetComponent: HTMLElement = fixture.nativeElement;
    let titleElement = budgetComponent.querySelector(".budget-total");
    expect(titleElement.textContent).toEqual("Today's Budget: ₩ 20000");
  })
});


먼저 BudgetComponent가 사용하는 모듈들이 import되어야 합니다.

그리고 it이 수행되기 전 필수 작업들이 beforeEach에서 수행되죠.

컴포넌트를 컴파일하고 화면을 그리는 작업입니다.


'should create'라는 테스트는 기본으로 생성됩니다.

component가 생성 되는지를 테스트하는 것이지요.

빠진 모듈이 있으면 should create부터 실패합니다.


저는 budget value가 셋팅이 되는지를 테스트했습니다.

fixture.nativeElement로부터 화면을 가져올 수 있어요.

budget-total 클래스를 가진 h3 요소의 값을 검사했습니다.

20000으로 설정된 값이 표출되고 있는지를요.

간단한 테스트죠.


더 복잡하고 다양한 테스트 스킬이 존재합니다.

경험해보면서 공유할 가치가 있는 것들은 공유할게요.


코드가 변하면서 테스트도 변해야합니다.

TDD(Test Driven Development)가 한창 화두일 때가 있었죠.

테스트를 먼저 작성하고 코딩을 하는 방식인데요.

저는 적절히 섞어서 작업해나갈 예정입니다.


소스주소: https://github.com/jsrho1023/account-book


출처:

https://angular.io/guide/testing

https://jasmine.github.io/tutorials/your_first_suite

'IT Tech > Angular' 카테고리의 다른 글

Angular Service 만들기  (2) 2018.05.13
Angular Domain Model  (0) 2018.04.24
Angular Material Table  (0) 2018.04.02
Angular Material Icon  (0) 2018.03.18
Angular Material Header Toolbar  (0) 2018.03.11

TechTrip IT Tech/Angular

Angular Material Table

2018. 4. 2. 22:00

지난 포스트에 예고했던 Material Table에 대해 알아보겠습니다.


0. Import MatTableModule


이제는 익숙한 과정이죠?

Material Table을 사용하기 위해서 Module을 추가합니다.


App.module.ts에 MatTableModule을 Import 합니다.

import { MatTableModule } from '@angular/material/table';
@NgModule({
  ...
  imports: [
    ...
    MatTableModule
  ],
  ...
})
export class AppModule { }


1. mat-table 추가


이제 화면(Template)에 테이블을 추가합니다.

저는 budget.component.html에 넣었습니다.

<mat-table [dataSource]="dataSource">
</mat-table>

dataSource에 Property Binding 보이시죠?

mat-table에서 사용할 데이터에 TypeScript 변수를 넣은거죠.


2. dataSource 추가


mat-table에 바인딩된 데이터를 추가해보겠습니다.

budget.component.ts파일에 data를 추가합니다.

import { MatTableDataSource } from '@angular/material';

@Component({
    ...
})
export class BudgetComponent implements OnInit{
    ...
    consumptions = [{
        amount: 12000,
        desc: "food"
    },{
        amount: 10000,
        desc: "beverage"
    },{
        amount: 12000,
        desc: "dissert"
    }];
    dataSource = new MatTableDataSource(this.consumptions);
    ...
}

MatTableDataSource 모듈이 필요합니다.

테이블에 보여줄 데이터를 Array 형태로 만들구요.

MatTableDataSource 객체를 생성하여 dataSource에 넣어줍니다.

그러면 mat-table과 바인딩될 준비가 끝납니다.


3. Column 정의


테이블의 Column Template을 정의합니다.

어느 제목에 어느 데이터를 넣을지를 의미합니다.

mat-header-cell 에는 제목이 들어가구요.

mat-cell 에는 어떤 데이터를 보여줄지 넣습니다.

저는 아래와 같이 정의하였습니다.

consumption의 amount와 desc를 두 개 column으로 보여줍니다.

<mat-table [dataSource]="dataSource">
  <!-- Amount Column -->
  <ng-container matColumnDef="amount">
    <mat-header-cell *matHeaderCellDef> Amount </mat-header-cell>
    <mat-cell *matCellDef="let consumption"> {{consumption.amount}} </mat-cell>
  </ng-container>

  <!-- Description Column -->
  <ng-container matColumnDef="desc">
    <mat-header-cell *matHeaderCellDef> Description </mat-header-cell>
    <mat-cell *matCellDef="let consumption"> {{consumption.desc}} </mat-cell>
  </ng-container>
</mat-table>


4. Row 정의


테이블의 Row Template을 정의합니다.

Row, Column을 어떻게 보여줄지 정의하면 테이블이 되죠.

amount와 desc를 각 열에 보여주기로 합니다.

<mat-table [dataSource]="dataSource">
  <!-- Amount Column -->
  <ng-container matColumnDef="amount">
    <mat-header-cell *matHeaderCellDef> Amount </mat-header-cell>
    <mat-cell *matCellDef="let consumption"> {{consumption.amount}} </mat-cell>
  </ng-container>

  <!-- Description Column -->
  <ng-container matColumnDef="desc">
    <mat-header-cell *matHeaderCellDef> Description </mat-header-cell>
    <mat-cell *matCellDef="let consumption"> {{consumption.desc}} </mat-cell>
  </ng-container>

  <!-- Row Template -->
  <mat-header-row *matHeaderRowDef="['amount','desc']"></mat-header-row>
  <mat-row *matRowDef="let consumption; columns: ['amount','desc'];">
  </mat-row>
</mat-table>

이렇게 하면 아래와 같이 Material Table이 보여집니다.

여기서 Table에 살짝 shadow를 넣어줬는데요.

div로 감싸주고 mat-elevation-z1이라는 class를 추가하면 됩니다.


Mat Table 화면


Material Table을 이용하면 몇 가지 장점이 있습니다.

정렬이나 페이징 필터링 등의 기능을 추가하기 쉽구요.

어느정도 반응형을 지원합니다.

필요할 때 추가해서 사용해보도록 할게요.


소스주소: https://github.com/jsrho1023/account-book


출처: 

https://material.io/

https://material.angular.io/components/table/overview

'IT Tech > Angular' 카테고리의 다른 글

Angular Domain Model  (0) 2018.04.24
Angular 테스트(Test) 하기  (0) 2018.04.11
Angular Material Icon  (0) 2018.03.18
Angular Material Header Toolbar  (0) 2018.03.11
Angular Directive (Structural Directive)  (0) 2018.02.12

TechTrip IT Tech/Angular

Angular Material Header Toolbar

2018. 3. 11. 15:53

명절 이후 한동안 게을러졌습니다.

다시 마음을 잡고, Application에 모양을 좀 내볼까 합니다.


사이트 상단에 늘 떠있는 제목을 한 번 만들어볼게요.

이번에도 Material 디자인을 이용합니다.

header라는 컴포넌트를 하나 만듭니다.

복습차원에서 되짚어보자면 명령어는 아래와 같습니다.

ng generate component header (ng g c header)


app.component.html에 header 컴포넌트를 넣어줍니다.

<app-header></app-header>
<app-budget></app-budget>


MatToolbar를 사용하기 위해 Module을 import 하구요.

import { MatToolbarModule } from '@angular/material/toolbar';

@NgModule({
    ...,
    imports: [
        MatToolbarModule
    ],
    ...
})
export class AppModule { }


이제 header 컴포넌트를 작성해봅니다.

mat-toolbra라는 태그를 사용합니다.

<mat-toolbar color="primary">
  <span class="application-title">Daily Account Log</span>
  <span class="fill-remaining-space"></span>
  <span>To be added</span>
</mat-toolbar>


색상은 지정하지 않으면 기본적인 배경색인데요.

저는 primary라는 값을 넣었습니다.

mat-toolbar는 기본적으로 flex row 형태의 display를 사용합니다.

좌측에 타이틀 우측에 메뉴 영역을 배치해보겠습니다.

header.component.css에 스타일을 넣어볼게요.

.application-title{
    font-size: 20px;
}
.fill-remaining-space {
    flex: 1;
}


위처럼 넣으면 가운데 영역이 채워집니다.

결과물은 아래와 같습니다.


angular material toolbar 적용


실제 적용해보면 상단, 좌, 우에 하얀 테두리가 보일 수 있습니다.

body에 기본적인 margin과 padding이 들어있어서 그렇습니다.

style.css 에 body의 margin, padding을 없애주면 됩니다.

body{
    margin: 0;
    padding: 0;
}


소스주소: https://github.com/jsrho1023/account-book

'IT Tech > Angular' 카테고리의 다른 글

Angular Material Table  (0) 2018.04.02
Angular Material Icon  (0) 2018.03.18
Angular Directive (Structural Directive)  (0) 2018.02.12
Angular Directive (Attribute Directive)  (2) 2018.02.04
Angular Material 디자인  (0) 2018.01.27

TechTrip IT Tech/Angular

Angular Directive (Structural Directive)

2018. 2. 12. 23:27

이번에는 Angular의 두 번째 Directive를 보겠습니다.


Attribute Directive는 DOM에 스타일과 동작을 부여했죠?

Structural Directive는 DOM 자체를 추가 제거합니다.


짧은 설명으로는 이해가 어려우니 예제를 살펴보겠습니다.

ngIf 라는 Directive를 사용해볼게요.

ngIf 를 이용하면 조건에 따라 DOM을 추가/제거할 수 있습니다.


<h3 
  [ngStyle]="{
    'color':budget<=1000?'red':'black',
    'background-color':budget<=1000?'yellow':'transparent'}">
      My Budget: 
      <span *ngif="buget!==""">&#8361;</span>{{budget}}</h3>


'원'표시를 span 태그로 감싸주었습니다.

그리고 *ngIf 라는 directive를 안에 넣어주었죠.

그 안에는 조건문이 들어갑니다.

조건을 만족시키면 span을 보여주고, 아니면 제거하는 겁니다.

budget 값이 빈문자열이라면 가격표시가 사라집니다.


재미난 것은 span 태그가 숨겨지는게 아니라 실제로 사라집니다.

크롬의 개발자 도구에서 보면 정확히 보이는데요.


ngIf를 활용한 DOM의 생성과 삭제


위와 같이 필요에 따라 DOM의 생성, 제거를 제어할 수 있습니다.

Structural Directive의 활용은 다양할 수 있겠죠?

Validation Message를 띄워준다든지 하는 것들이요.


참고로 ngIf 앞의 *표시는 Structural Directive임을 나타내는 syntax 입니다.

대표적으로 *ngFor라는 녀석도 있습니다.

다음에 필요시 사용해보도록 할게요.


소스주소: https://github.com/jsrho1023/account-book


'IT Tech > Angular' 카테고리의 다른 글

Angular Material Icon  (0) 2018.03.18
Angular Material Header Toolbar  (0) 2018.03.11
Angular Directive (Attribute Directive)  (2) 2018.02.04
Angular Material 디자인  (0) 2018.01.27
Angular 데이터 바인딩 III (ngModel)  (0) 2018.01.21

TechTrip IT Tech/Angular

Angular Directive (Attribute Directive)

2018. 2. 4. 20:31

알게 모르게 계속 사용했던 Directive에 대해서 살펴보겠습니다.

Directive를 번역하자면 지시자 라고 할 수 있겠네요.

HTML에 Angular가 간섭할 부분을 표시한다라고 이해하면 쉽습니다.

Component 역시 template이 포함된 Directive로 정의하는데요.

<app-root> 기억하시죠?

이 부분에 app-component를 표출해달라는 표시였습니다.


Component는 별도로 치고, Directive에 2가지가 더 있습니다.

Structural Directive와 Attribute Directive입니다.


2가지 Directives의 차이는 DOM을 다루는 방식입니다.

Structural Directive는 DOM을 추가하거나 제거합니다.

Attribute Directive는 이미 있는 DOM에 스타일이나 동작을 부여합니다.

Angular가 간섭해야함을 표시한다는 걸 기억해주세요.


오늘은 먼저 Attribute Directive를 살펴볼게요.

Angular의 Directive는 마치 HTML에 존재하는 속성처럼 사용됩니다.

스스로 custom directive를 정의할 수도 있습니다.

하지만 이미 만들어진 ngStyle을 보며 directive를 이해해보죠.


<h3 
  [ngStyle]="{
    'color':budget<=1000?'red':'black',
    'background-color':budget<=1000?'yellow':'transparent'}">
      My Budget: &#8361;{{budget}}</h3>


h3 태그에 ngStyle Directive를 추가하였습니다.

마치 html 속성에 property binding을 한 모양새죠.

ngStyle의 값으로 css와 같이 key, value 쌍을 넣었습니다.

그리고 조건문도 들어갔죠.

budget 값이 1000이하면 노란배경에 빨간글씨가 됩니다.


ngStyle 적용 화면



ngStyle이라는 건 원래 html에 정의되어있지 않습니다.

즉 태그에 ngStyle이라고 표시되면 Angular가 간섭을 한다는 의미죠.

ngStyle의 실제 코드를 대략 살펴보면 아래와 같습니다.


@Directive({ selector: '[ngStyle]' })
class NgStyle implements DoCheck {
  set ngStyle: {...}
  ngDoCheck()
}


마치 Component에서처럼 selector가 존재하죠.

Anguar는 저 selector를 찾아서 HTML에 스타일을 입히는 겁니다.

ngStyle 뿐 아니라 ngClass와 같이 정의된 directive가 더 있습니다.

기회가 되면 차차 사용하면서 배우구요.

다음에는 Structural Directive를 사용해보면서 익혀보겠습니다. 


소스주소: https://github.com/jsrho1023/account-book

TechTrip IT Tech/Angular