ng generate component first
ng generate component second
ng generate component child
... 중략 ...
import { FirstComponent } from './first/first.component';
import { SecondComponent } from './second/second.component';
@NgModule({
declarations: [
AppComponent,
FirstComponent,
SecondComponent,
ChildComponent,
],
... 중략 ...
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>AngluarIntro</title>
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico">
</head>
<body>
<app-root></app-root>
</body>
</html>
import {Component, OnDestroy, OnInit} from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
// lifecycle OnInit, OnDestory
export class AppComponent implements OnInit, OnDestroy{
ngOnDestroy(): void {
}
ngOnInit(): void {
}
}
<div style="text-align: center">
<h1>header</h1>
</div>
<div style="text-align: center">
<h1>bottom</h1>
</div>
app level에서 라우팅 모듈 추가
ng g module app-routing --flat --module-app
... 중략 ...
import { AppRoutingModule } from './app-routing.module';
imports: [
BrowserModule,
AppRoutingModule,
],
... 중략 ...
라우터 모듈을 사용하여 기본적인 라우팅을 구성할 수 있으며 path
설정을 통해 오브젝트의 디테일 id
값을 매핑할 수 있다.
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import {FirstComponent} from "./first/first.component";
import {SecondComponent} from "./second/second.component";
// router
const routes: Routes = [
{ path: '', component: FirstComponent },
{ path: 'first', component: FirstComponent },
{ path: 'sec', component: SecondComponent},
{ path: 'sec/:id', component: SecondComponent},
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
<div style="text-align: center">
<h1>header</h1>
<nav>
<a routerLink="first">First</a><br>
<a routerLink="sec">Second</a><br>
<a routerLink="sec/5">Second-detail</a>
</nav>
</div>
<router-outlet></router-outlet>
<!-- url prams -->
<div style="text-align: center">
<h1>bottom</h1>
</div>
import {Component, OnInit} from '@angular/core';
import {ActivatedRoute} from "@angular/router";
import {Location} from "@angular/common";
@Component({
selector: 'app-second',
templateUrl: './second.component.html',
styleUrls: ['./second.component.css']
})
export class SecondComponent implements OnInit{
constructor(private route: ActivatedRoute,
private location: Location) { }
ngOnInit(): void {
// ID 값을 가져오는 코드
let ourId:string | null = this.route.snapshot.paramMap.get('id');
console.log(ourId);
}
}
컴포넌트는 앵귤러에서 애플리케이션을 구성하는 기본 단위이다. 컴포넌트에 생성한 변수를 템플릿에서 바인딩하여 사용할 수 있으며 이를 통해 동적으로 변하는 변수에 대한 처리가 가능하다.
import {Component, OnInit} from '@angular/core';
// form control
@Component({
selector: 'app-first',
templateUrl: './first.component.html',
styleUrls: ['./first.component.css']
})
export class FirstComponent {
constructor() {}
// Binding
name = 'tester';
today = new Date();
counter = 0;
myColor = 'purple'
people = <any[]>[]
btnInitCounter() {
this.counter = 0;
}
btnConsoleLog = function () {
console.log('this is test');
};
btnChangeColor(test: string) {
console.log(test);
this.myColor = 'red'
}
btnCounter() {
this.counter++;
}
peopleLocal = [
{name: 'sean', age: 24},
{name: 'you', age: 44},
{name: 'babo', age: 66},
];
}
<p>first works!</p>
<!-- variable binding -->
<h3>Hi {{ name }}</h3>
<p>Today is {{ today }}</p>
<!-- name change -->
<button (click)="name='new'">Change Name</button><br>
<!-- CSS directive -->
<h3 [ngStyle]="{'background-color': 'green'}">My color is green</h3>
<h3 [ngStyle]="{'background-color': myColor}">My color will be changed</h3>
<!-- function binding -->
<button (click)="btnConsoleLog()">Check Console Log</button><br>
<button (click)="btnChangeColor('things I wanted')">Change Color</button><br>
<button (click)="btnCounter()">Counter+</button>
<button (click)="btnInitCounter()">Init Counter</button><br>
<h3> {{ counter }} </h3>
<!-- directive -->
<div style="text-align: center">
<div *ngFor="let person of peopleLocal">
<h4 *ngIf="person.age > 30">
{{ person.name }} is {{ person.age }} years old.
</h4>
</div>
</div>
<div style="text-align: center">
<div *ngFor="let person of peopleLocal">
<h4 *ngIf="counter > 2">
{{ person.name }} is {{ person.age }} years old. (ngif counter)
</h4>
</div>
</div>
<div style="text-align: center">
<div *ngFor="let person of peopleLocal">
<!-- <h3 [ngClass]="{'red': true}" >-->
<h4 [ngClass]="{'red': person.age == 24 || person.age > 50}" >
{{ person.name }} is {{ person.age }} years old (color change)
</h4>
</div>
</div>
<!-- pipe -->
<h1>
my name is {{ name | uppercase}}
</h1>
<h1>
my name is {{ name | lowercase}}
</h1>
<h1>
my name is {{ name }}
</h1>
<h3> {{ today | date }} </h3>
<h3> {{ today | date:"short" }} </h3>
<h3> {{ today | date:"long" }} </h3>
<h3> {{ today | date:"full" }} </h3>
<h3> {{ today | date:"fullDate" }} </h3>
<h3> {{ today | date:"dd/MM/yyyy:H:m:s" }} </h3>
<h3> {{ cash | currency }} </h3>
<h3> {{ cash | currency: "EUR" }} </h3>
<p>=========================</p>
.red {
color: red;
}
기존 배열 변수로 사용했던 personLocal
을 서비스로 변환하여 사용할 수 있다. 일반적으로 백엔드 API에 데이터를 요청하는 기능을 서비스로 구현한다.
ng generate service people
import {PeopleService} from "./people.service";
... 중략 ...
// Service added
providers: [
PeopleService,
],
... 중략 ...
import { Injectable } from '@angular/core';
import {Observable} from "rxjs";
@Injectable({
providedIn: 'root'
})
export class PeopleService {
people = [
{name: 'sean', age: 24},
{name: 'you', age: 44},
{name: 'babo', age: 66},
{name: 'ddd', age: 666}
];
constructor() {}
allPeople() {
// we could call external API
return this.people;
}
}
기존 peopleLocal
배열 변수를 이용한것과 다르게 peopleService
를 통해 people
배열 변수를 호출한다.
import {Component, OnInit} from '@angular/core';
import {PeopleService} from "../people.service";
@Component({
selector: 'app-first',
templateUrl: './first.component.html',
styleUrls: ['./first.component.css']
})
export class FirstComponent implements OnInit {
constructor(private peopleService : PeopleService) {}
ngOnInit(): void {
console.log('component initiated');
this.counter = 1;
this.people = this.peopleService.allPeople();
}
... 중략 ...
... 중략 ...
<!-- Service -->
<div style="text-align: center">
<div *ngFor="let person of people">
<h4>{{ person.name }} is {{ person.age }} years old (Service)</h4>
</div>
</div>
... 중략 ...
people
변수의 값이 계속 변경된다면 앵귤러에서 변경된 값을 감지하여 업데이트해야 하는데 옵저버블이 그 역할을 수행한다. 옵저버블은 rxjs
에 존재하는 기능으로 특정 변수를 구독(subscribe
)하여 변화를 감지하고 변경된 데이터를 수신할 수 있게 해준다.
newObservable
에서 people
변수를 구독한다. (변화를 감지하여 업데이트하겠다는 의미)
import { Injectable } from '@angular/core';
import {Observable} from "rxjs";
@Injectable({
providedIn: 'root'
})
export class PeopleService {
// observable
newObservable$ = new Observable(observer => {
observer.next(this.people);
observer.next('hi')
});
people = [
{name: 'sean', age: 24},
{name: 'you', age: 44},
{name: 'babo', age: 66},
{name: 'ddd', age: 666}
];
constructor() {
}
allPeople() {
// we could call external API
return this.people;
}
}
앵귤러 라이프사이클 중 가장 초기 단계인 ngOnInit
부분에 옵저버블을 추가하여 콘솔로 옵저버블이 감지한 값을 확인할 수 있다. 추후 개발 시 적절한 라이프사이클 훅을 사용하여 변화된 값을 감지할 수 있다.
export class FirstComponent implements OnInit {
constructor(private peopleService : PeopleService) {}
people = <any[]>[]
ngOnInit(): void {
// observables
this.peopleService.newObservable$.subscribe(
data => {
console.log('obs data here : ', data)
}, error => {
console.log('error')
}
)
console.log('component initiated');
this.counter = 1;
this.people = this.peopleService.allPeople();
}
import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core';
@Component({
selector: 'app-child',
templateUrl: './child.component.html',
styleUrls: ['./child.component.css']
})
export class ChildComponent implements OnInit{
@Input() animal: string | undefined;
<p>child works!</p>
<p>child page : {{ animal }}</p>
import {Component, OnInit} from '@angular/core';
import {ActivatedRoute} from "@angular/router";
import {Location} from "@angular/common";
@Component({
selector: 'app-second',
templateUrl: './second.component.html',
styleUrls: ['./second.component.css']
})
export class SecondComponent implements OnInit{
constructor(private route: ActivatedRoute,
private location: Location) { }
ngOnInit(): void {
let ourId:string | null = this.route.snapshot.paramMap.get('id');
console.log(ourId);
}
myAnimal = 'dog';
}
<app-child>~
코드에서 자식 컴포넌트의 animal
에 부모 컴포넌트의 myAnimal
값을 지정한다. app-child
템플릿은 second
템플릿에서 호출되어 myAnimal
값인 dog
가 animal
값으로 적용되어 보여진다.
<p>second works!</p>
<app-child [animal]="myAnimal"></app-child>
<p>second page : {{ myAnimal }}</p>
import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core';
@Component({
selector: 'app-child',
templateUrl: './child.component.html',
styleUrls: ['./child.component.css']
})
export class ChildComponent implements OnInit{
@Input() animal: string | undefined;
@Output() changed = new EventEmitter();
ngOnInit(): void {
this.changed.emit('Horse');
}
}
import {Component, OnInit} from '@angular/core';
import {ActivatedRoute} from "@angular/router";
import {Location} from "@angular/common";
@Component({
selector: 'app-second',
templateUrl: './second.component.html',
styleUrls: ['./second.component.css']
})
export class SecondComponent implements OnInit{
constructor(private route: ActivatedRoute,
private location: Location) { }
ngOnInit(): void {
let ourId:string | null = this.route.snapshot.paramMap.get('id');
console.log(ourId);
}
myAnimal = 'dog';
childAnimal = '';
childChanged(event: string) {
this.childAnimal = event;
console.log(this.childAnimal)
}
}
자식 컴포넌트의 changed
에서 emit
이벤트가 발생하면 해당 이벤트의 값(Horse
)을 부모 컴포는트의 childChanged('horse')
의 인자로 전달하여 호출한다. 이를 통해 부모 컴포넌트는 childAnimal
값을 Horse
로 변경한다.
<p>second works!</p>
<app-child (changed)="childChanged($event)"></app-child>
<p> parent page (second) : {{ childAnimal }} </p>
<p>second works!</p>
<app-child [animal]="myAnimal"
(changed)="childChanged($event)"></app-child>
<p> parent page (second child -> parent) : {{ childAnimal }} </p>
... 중략 ...
export class SecondComponent implements OnInit{
name = 'show'
... 중략 ...
자주 사용되는 ngModel
은 양방향 바인딩을 기반하여 구성되었다.
<input type="text" [(ngModel)]="name" />