디렉티브는 DOM의 모양이나 동작을 지시하기위한 명령입니다

HTML Element형태로 사용되거나 Element의 속석으로 사용되게 됩니다

예를들면 전장에서 사용했던 nfFor같은 속성입니다 routing에서사용한 <router-outlet>엘리먼트도 디렉티브입니다


Directive는 크게 다음과 같은 4가지 종류로 구분 할 수 있습니다.


Component Directive

계속 봐왔던 컴포넌트입니다  selector에서 directive를 지정해 사용하게 됩니다.


Attribute Directive

HTML Element의 속성으로 사용됩니다.


Structural Directive

DOM의 구성을 제어하기 위해 사용합니다


Custom Directive

직접 만들어서 사용하는 디렉티브입니다


우선 컴포넌트를 만들고 라우팅에등록시키고 메뉴에 추가하겠습니다

명령프롬프트 ng g c directive/ngClass

명령프롬프트 ng g c directive/ngIf

명령프롬프트 ng g c directive/ngFor


app-routing.module.ts

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import {DemoComponent} from './demo/demo.component';
import {Demo2Component} from './demo2/demo2.component';
import {OnewayComponent} from './bind/oneway/oneway.component';
import {TwowayComponent} from './bind/twoway/twoway.component';
import {NgClassComponent} from './directive/ng-class/ng-class.component';
import {NgIfComponent} from './directive/ng-if/ng-if.component';
import {NgForComponent} from './directive/ng-for/ng-for.component';

const routes: Routes = [
{path: 'demo', component: DemoComponent},
{path: 'demo2', component: Demo2Component},
{path: 'oneway', component: OnewayComponent},
{path: 'twoway', component: TwowayComponent},
{path: 'ngClass', component: NgClassComponent},
{path: 'ngIf', component: NgIfComponent},
{path: 'ngFor', component: NgForComponent},
];

@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }


app.component.ts

import { Component } from '@angular/core';
import {NbMenuItem} from '@nebular/theme';

@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent {
title = 'my-app';

items = [
{
title: 'demo',
link: ['demo'],
},
{
title: 'demo2',
link: ['demo2'],
},
{
title: 'Binding',
expanded: true,
children: [
{
title: 'One-way',
link: ['oneway'],
},
{
title: 'Two-way',
link: ['twoway'],
},
],
},
{
title: 'Directive',
expanded: true,
children: [
{
title: 'ngClass',
link: ['ngClass'],
},
{
title: 'ngIf',
link: ['ngIf'],
},
{
title: 'ngFor',
link: ['ngFor'],
},
],
},
];
}



먼저 ngClass입니다 태그에 속성형태로 사용하며 

[ngClass]="자바스크립트코드" 와같이 동적으로 엘리먼트의 클래스를 추가할수있습니다


ng-class.component.ts

import { Component, OnInit } from '@angular/core';

@Component({
selector: 'app-ng-class',
templateUrl: './ng-class.component.html',
styleUrls: ['./ng-class.component.scss']
})
export class NgClassComponent implements OnInit {

className: string = 'red';
isFlag: boolean = false;

constructor() { }

ngOnInit() {
}

}


클래스명을담을변수 className과 불린값을담을변수 isFlag를 만들었습니다


ng-class.component.html

<nb-card>
<nb-card-body>
<div [ngClass]="className">[ngClass] = "컴포넌트의변수값"</div>
<button [ngClass]="{ red: isFlag }" (click)="isFlag = !isFlag">클릭</button>
</nb-card-body>
</nb-card>

첫번째사용은 클래스명이담긴 변수를 직접추가했고

두번째사용은 객체로묶어 클래스(키): 불린변수(밸류) 이런식으로 isFlag가 true일때만 클래스를 추가하겠다는겁니다

{ red: isFlag } 여기서 red는 문자열입니다, 자바스크립트 객체의 키는 항상 문자열입니다

버튼에 클릭이벤트바인딩을걸어 토글형식으로 red 클래스를 넣었다뺐다합니다


클래스가 추가되었는지확인하기위해서 css에 스타일을만듭니다

ng-class.component.scss

.red{
background-color: red;
color: white;
}


브라우저에서 확인해보면


div에 red클래스가 추가되었고 버튼클릭시 토글로 red클래스가 추가됬다삭제됬다 합니다


ngIf입니다 태그에 속성형태로 사용하며 *ngIf="불린값" 

ngIf가 true면 태그를만들고 false면 만들지않습니다


ng-if.component.ts

import { Component, OnInit } from '@angular/core';

@Component({
selector: 'app-ng-if',
templateUrl: './ng-if.component.html',
styleUrls: ['./ng-if.component.scss']
})
export class NgIfComponent implements OnInit {

str: string = 'hello';
isFlag: boolean = false;
num: number = 0;

constructor() { }

ngOnInit() {
}

}

문자형 불린형 숫자형 변수를만들었습니다


ng-if.component.html

<nb-card>
<nb-card-body>
<h1 *ngIf="str">str이 있다면 출력 str: {{ str }}</h1>
<h1 *ngIf="str == 'hello'">비교연산도 가능</h1>

<input type="checkbox" [(ngModel)]="isFlag"> 체크해야만 버튼이보임<br>
<button *ngIf="isFlag">전송</button><br>
<input [(ngModel)]="num" type="number"><br>
숫자를 올리면
<h3 *ngIf="num">보입니다.</h3>
</nb-card-body>
</nb-card>

여러형태로 테스트유형을 만들었습니다

브라우저로 확인해보면



빈문자열 "", null, undefined, 0은 false로 처리합니다



-미완-

앵귤러의 데이터바인딩은 두가지 종류가 있습니다

단방향바인딩과 양방향 바인딩


단방향 바인딩은 컴포넌트에서 뷰로 뷰에서 컴포넌트로 한방향으로만 바인딩 해주는기능입니다

양방향 바인딩은 컴포넌트 < - >뷰 정보가 바뀌면 자동으로 바인딩 해주는 기능입니다


바인딩 실습을위한 컴포넌트를 만들어줍니다 

명령프롬프트 ng g c bind/oneway

명령프롬프트 ng g c bind/twoway

※ng g c bind/oneway는 bind폴더를 만들고 그밑에 oneway 컴포넌트를 만들라는것입니다


디렉터리 구조는 이렇습니다


컴포넌트를 라우팅에 추가해줍니다

app-routing.module.ts

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import {DemoComponent} from './demo/demo.component';
import {Demo2Component} from './demo2/demo2.component';
import {OnewayComponent} from './bind/oneway/oneway.component';
import {TwowayComponent} from './bind/twoway/twoway.component';

const routes: Routes = [
{path: 'demo', component: DemoComponent},
{path: 'demo2', component: Demo2Component},
{path: 'oneway', component: OnewayComponent},
{path: 'twoway', component: TwowayComponent},
];

@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }


그리고 앱컴포넌트 사이드바에 메뉴도 추가해줍니다

app.component.ts

import { Component } from '@angular/core';
import {NbMenuItem} from '@nebular/theme';

@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent {
title = 'my-app';

items = [
{
title: 'demo',
link: ['demo'],
},
{
title: 'demo2',
link: ['demo2'],
},
{
title: 'Binding',
expanded: true,
children: [
{
title: 'One-way',
link: ['oneway'],
},
{
title: 'Two-way',
link: ['twoway'],
},
],
},
];
}


먼저 단방향 바인딩입니다

Interpolation 인터폴레이션: 컴포넌트에 정의한 변수를 템플릿에서 사용합니다 

사용법 {{ variable }}


Property binding 프로퍼티바인딩: 엘리먼트의 프로퍼티를 []를사용해 바인딩합니다

사용법 <input [value]="variable">


Event binding 이벤트바인딩: 엘리먼트의 핸들러로 컴포넌트의 메소드를 바인딩합니다

사용법 <button (click)="say()">


간단하게 종류와 사용법을알아 봤습니다 실제코드는 이렇습니다

oneway.component.ts

import { Component, OnInit } from '@angular/core';

@Component({
selector: 'app-oneway',
templateUrl: './oneway.component.html',
styleUrls: ['./oneway.component.scss']
})
export class OnewayComponent implements OnInit {

i: number = 1;
str: string = 'Hello';
flag: boolean = true;

constructor() { }

ngOnInit() {
}

say(): string {
return 'Hi';
}

clickBind(): void {
alert('클릭 바인딩');
}
}


oneway.component.html

<nb-card>
<nb-card-body>
클래스 변수 i: {{ i }}<br>
산술: {{ 1+2 }}<br>
클래스 변수 str: {{ str }}<br>
메소드 say(): {{ say() }}<br>

<br>

<input [disabled]="flag"><br>
<input [disabled]="!flag"><br>
<input [value]="say()"><br>
<button (click)="clickBind()">event</button>
</nb-card-body>
</nb-card>


그리고 nb-card 태그를 쓰기위해서 앱모듈 imports에 NbCardModule을 추가해줍니다

app.module.ts

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { DemoComponent } from './demo/demo.component';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import {NbThemeModule, NbLayoutModule, NbSidebarModule, NbMenuModule, NbCardModule} from '@nebular/theme';
import { Demo2Component } from './demo2/demo2.component';
import { OnewayComponent } from './bind/oneway/oneway.component';
import { TwowayComponent } from './bind/twoway/twoway.component';

@NgModule({
declarations: [
AppComponent,
DemoComponent,
Demo2Component,
OnewayComponent,
TwowayComponent
],
imports: [
BrowserModule,
AppRoutingModule,
BrowserAnimationsModule,
NbThemeModule.forRoot({ name: 'default' }),
NbLayoutModule,
NbSidebarModule.forRoot(),
NbMenuModule.forRoot(),
NbCardModule,
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }


그리고 브라우저를통해 확인을해보면

{{ i }} 가 컴포넌트에정의한 i = 1 이들어간것을확인할수있습니다

2줄에서는 변수를 사용하지않고 간단한산술을하였습니다

{{ }} 인터폴레이션에 적은것은 자바스크립트코드가 되기때문에 산술된결과를얻을수있습니다

문자열도 출력되구요

4줄 say() 메소드의 리턴값이 출력됩니다

여기까지 인터폴레이션을 사용했습니다


input 태그에 [disabled]="flag" 변수flag의 값을통해 disabled 처리되었고

프로퍼티바인딩도 자바스크립트코드가되기때문에 !flag를하면 disabled=false 가됩니다


마지막으로 이벤트바인딩입니다

버튼에 (click)="clickBind()" 버튼태그에 (click)으로 엘리먼트를 클릭했을때 clickBind()메소드가 동작하게됩니다


------------------------------------------------------------------------------------------------------------------------------------------------------

양방향 바인딩을 사용하기위해선 모듈에 FormsModule이 추가되있어야합니다

@angular/forms 에 있으므로 따로설치하지않고 앱모듈에 추가해줍니다


app.module.ts

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { DemoComponent } from './demo/demo.component';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import {NbThemeModule, NbLayoutModule, NbSidebarModule, NbMenuModule, NbCardModule} from '@nebular/theme';
import { Demo2Component } from './demo2/demo2.component';
import { OnewayComponent } from './bind/oneway/oneway.component';
import { TwowayComponent } from './bind/twoway/twoway.component';
import {FormsModule} from '@angular/forms';

@NgModule({
declarations: [
AppComponent,
DemoComponent,
Demo2Component,
OnewayComponent,
TwowayComponent
],
imports: [
BrowserModule,
AppRoutingModule,
BrowserAnimationsModule,
NbThemeModule.forRoot({ name: 'default' }),
NbLayoutModule,
NbSidebarModule.forRoot(),
NbMenuModule.forRoot(),
NbCardModule,
FormsModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }


글맨위에 

양방향 바인딩은 컴포넌트 < - >뷰 정보가 바뀌면 자동으로 바인딩 해주는 기능이라고 설명했습니다

사용법은 엘리먼트 태그에 [(ngModel)]="바인딩대상" 으로 사용합니다


코드를 보겠습니다

twoway.component.ts

import { Component, OnInit } from '@angular/core';

@Component({
selector: 'app-twoway',
templateUrl: './twoway.component.html',
styleUrls: ['./twoway.component.scss']
})
export class TwowayComponent implements OnInit {

value: string = 'Hi';
selectVal: string = 'one';
checkVal: boolean = false;

constructor() { }

ngOnInit() {
}

}


twoway.component.html

<nb-card>
<nb-card-body>
<input [(ngModel)]="value"><br>
실시간 value 값: {{ value }}

<br><br>

<select [(ngModel)]="selectVal">
<option *ngFor="let item of ['one','two','three','four']">
{{ item }}
</option>
</select><br>
실시간 selectVal 값: {{ selectVal }}

<br><br>

<input type="checkbox" [(ngModel)]="checkVal"> 체크밸류<br>
실시간 checkVal 값: {{ checkVal }}
</nb-card-body>
</nb-card>

※ option 태그에 *ngFor라는게 보입니다 directive 디렉티브라고 하는데 다음포스트에서 설명합니다


브라우저로 확인해보면



영문일 경우는 문제없이 잘 수행되지만 한글일 경우는 약간의 문제가 있습니다 바로바로 화면에 적용되지 않는 것이죠

영문과 다르게 한글은 조합형 문자이기 때문에 글자가 다 만들어 지기 전까지는 화면에 출력되지 않게 되는 것입니다

문제를 해결하기위해 COMPOSITION_BUFFER_MODE 설정을 변경하셔야합니다


app.module.ts

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { DemoComponent } from './demo/demo.component';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import {NbThemeModule, NbLayoutModule, NbSidebarModule, NbMenuModule, NbCardModule} from '@nebular/theme';
import { Demo2Component } from './demo2/demo2.component';
import { OnewayComponent } from './bind/oneway/oneway.component';
import { TwowayComponent } from './bind/twoway/twoway.component';
import {COMPOSITION_BUFFER_MODE, FormsModule} from '@angular/forms';

@NgModule({
declarations: [
AppComponent,
DemoComponent,
Demo2Component,
OnewayComponent,
TwowayComponent
],
imports: [
BrowserModule,
AppRoutingModule,
BrowserAnimationsModule,
NbThemeModule.forRoot({ name: 'default' }),
NbLayoutModule,
NbSidebarModule.forRoot(),
NbMenuModule.forRoot(),
NbCardModule,
FormsModule
],
providers: [
{
provide: COMPOSITION_BUFFER_MODE,
useValue: false
}
],
bootstrap: [AppComponent]
})
export class AppModule { }


다시브라우저를통해 확인해보면

입력하는대로 바인딩되는것을 확인할수있습니다


하나의 템플릿에 컴포넌트를 여러개정의할수있습니다

브라우저상에서 다른영역은 그대로있고 한영역의 컨텐츠를 갈아끼우는방식으로 교체하려면 셀렉터태그 대신 

<router-outlet></router-outlet>이라는 태그를 사용합니다


우선 app.component.html 파일을 수정합니다

<nb-layout>

<nb-layout-header fixed>
네비게이션
</nb-layout-header>


<nb-layout-column>
<h1>이것은 App 컴포넌트</h1>

<router-outlet></router-outlet>

</nb-layout-column>

<nb-layout-footer fixed>
create by dduruddung
</nb-layout-footer>


</nb-layout>


그럼 브라우저 화면은 이렇게됩니다

그리고  <router-outlet></router-outlet>태그안에 여러컴포넌트를 url에따라서 갈아끼우고 싶을때 라우팅을사용합니다

먼저 교체되는지 확인하기위해서 demo2 컴포넌트를 하나더 생성합니다


명령 프롬프트 ng g c demo2


처음 프로젝트 생성할때 라우팅모듈을사용하겠냐는 물음에 Y Enter했습니다

그래서 app-routing.module.ts 파일이 만들어져있습니다 열어보면

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';

const routes: Routes = [];

@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }

routes에 아무것도 정의되지 않았습니다

demo와 demo2컴포넌트 라우팅설정을 해줍니다


import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import {DemoComponent} from './demo/demo.component';
import {Demo2Component} from './demo2/demo2.component';

const routes: Routes = [
{path: 'demo', component: DemoComponent},
{path: 'demo2', component: Demo2Component},
];

@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }

그리고 브라우저 주소창에

http://localhost:4200/demo

http://localhost:4200/demo2

를 입력하여 변경된 화면을 확인합니다


 <router-outlet></router-outlet>태그안에 url에 따라 컴포넌트가 교체되는것을 확인했습니다


매번url을 칠수없으니 네비게이션을 만듭니다


app.module.ts

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { DemoComponent } from './demo/demo.component';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import {NbThemeModule, NbLayoutModule, NbSidebarModule, NbMenuModule} from '@nebular/theme';
import { Demo2Component } from './demo2/demo2.component';

@NgModule({
declarations: [
AppComponent,
DemoComponent,
Demo2Component
],
imports: [
BrowserModule,
AppRoutingModule,
BrowserAnimationsModule,
NbThemeModule.forRoot({ name: 'default' }),
NbLayoutModule,
NbSidebarModule.forRoot(),
NbMenuModule.forRoot(),
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }

네뷸라 사이드바모듈,메뉴모듈을 app모듈에 추가합니다


app.component.ts

import { Component } from '@angular/core';
import {NbMenuItem} from '@nebular/theme';

@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent {
title = 'my-app';

items: NbMenuItem[] = [
{
title: 'demo',
link: 'demo'
},
{
title: 'demo2',
link: 'demo2'
}
];
}

메뉴를 정의해줍니다


app.component.html

<nb-layout>

<nb-layout-header fixed>
앵귤러 배우기
</nb-layout-header>


<nb-layout-column>
<h1>이것은 App 컴포넌트</h1>

<router-outlet></router-outlet>

</nb-layout-column>

<nb-layout-footer fixed>
create by dduruddung
</nb-layout-footer>

<nb-sidebar>
<nb-menu [items]="items"></nb-menu>
</nb-sidebar>
</nb-layout>

네뷸라 사이드바를 추가해줍니다

사이드바태그안에 네뷸라메뉴를 추가해줍니다

app.component.ts에서 정의한 items를 [items]속성에 넣어줍니다

브라우저 화면은 이렇게바뀌었습니다

왼쪽 사이드바의 버튼을 클릭해 보다편하게 앱컴포넌트안에 라우터아울렛영역을 교체할수있습니다



앞으로의 실습들의 디자인이 보기좋게 네뷸라 모듈을 설치합니다


보다 자세한내용은 여기에서 확인할수있습니다

https://akveo.github.io/nebular/docs/guides/add-into-existing-project#create-a-new-project


명령 프롬프트 ng add @nebular/theme


? Which Nebular theme do you want to use: default Enter

? Use customizable scss themes? Yes Enter

? Set up browser animations for Nebular? Yes Enter


그러면 파일이 추가되고 app.module.ts, styles.scss, app.component.html 파일이 수정됩니다


app.module.ts

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { DemoComponent } from './demo/demo.component';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { NbThemeModule, NbLayoutModule } from '@nebular/theme';

@NgModule({
declarations: [
AppComponent,
DemoComponent
],
imports: [
BrowserModule,
AppRoutingModule,
BrowserAnimationsModule,
NbThemeModule.forRoot({ name: 'default' }),
NbLayoutModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }

app모듈에 네뷸라 사용을위한 모듈이 추가되었습니다


styles.scss

@import 'themes';

@import '~@nebular/theme/styles/globals';

@include nb-install() {
@include nb-theme-global();
};
/* You can add global styles to this file, and also import other style files */

전역 스타일 파일에 네뷸라테마가 추가되었습니다


app.component.html

<nb-layout>

<nb-layout-header fixed>
<!-- Insert header here -->
</nb-layout-header>

<nb-layout-column>

<h1>이것은 App 컴포넌트</h1>

<app-demo></app-demo>
<app-demo></app-demo>
<app-demo></app-demo>
<app-demo></app-demo>


<router-outlet></router-outlet>

</nb-layout-column>

<nb-layout-footer fixed>
<!-- Insert footer here -->
</nb-layout-footer>

</nb-layout>

기본 nb-layout을 알아서 수정해줬습니다


브라우저에서 확인해보면

이런 형태가 됩니다