일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | ||||
4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 |
- three.js
- blender
- 에러노트
- CSS
- 프론트엔드
- 플랫폼
- SaaS디자인
- 생성AI
- 데이터분석
- 웹디자인
- design token
- frontend
- 업무자동화
- 에어테이블
- 3d
- angular
- Airtable
- 파이썬
- 디자인 토큰
- UI
- scss
- sass
- 문서자동화
- UX
- CINEMA4D
- 디자인토큰
- 리액트
- 한글자동화
- 디자인
- chatGPT
- Today
- Total
이게 무슨 일이야!
[Angular] Dialog 개발 - 2 본문
[Angular] Dialog 개발 - 1 https://eunice6113.tistory.com/27
에서 이어서 만듭니다.
시작하기 전에,
Reference
https://poiemaweb.com/angular-service
Angular 를 너무 오래전에 공부한 탓에.. 버전이 바뀌면서 새로 추가된 기능들을 잘 이해하지 못하고 있었다
이분 블로그를 보면서 의존성 주입에 대해서 쉽게 이해할 수 있었다. 추천!
(Angular 홈페이지 매뉴얼은.. 어렵더라..)

dynamic component 생성만으로도 이미 dialog library 는 쓸 수 있긴 하다.
사실.. dialog 는 단순하게 생각하면 <div> 두개를 화면에 띄웠다 숨겼다 하면 그만인 물건이다.
근데 어쩌다가 3일이나 삽질했냐면 (...)
Angular Material > Dialog 와 비슷한 구조로 Dialog 를 생성하고 호출하고 싶었다.
(왜냐하면 기존 UI 개선 프로젝트 진행중인데 호출부가 다르면 소스를 많이 뒤엎어야 하니까 조금만 수정하려고...)
<- 도 이유였는데, Angular Material 의 interface 를 흉내내면서 내 수준에서 설계할 수 없는 수준으로 코드가 좋아지는 것 같았다. 공부도 되고 좋은 것 같다 .!
먼저 내가 흉내내고 싶었던 Angular Dialog 는 이런식으로 호출한다.
예를들어 아래 예시는 가장 단순한 Alert Dialog 인데,
이런 식의 Dialog Component 들을 디자인별로 다양하게 만들 수 있다. 호출은 똑같이 한다.
alert-dialog.component.ts
import {Component, Inject, OnInit} from '@angular/core';
import {MAT_DIALOG_DATA, MatDialogRef} from "@angular/material/dialog";
@Component({
selector: 'app-alert-dialog',
templateUrl: './alert-dialog.component.html',
styleUrls: ['./alert-dialog.component.scss']
})
export class AlertDialogComponent implements OnInit {
constructor(
public dialogRef: MatDialogRef<AlertDialogComponent>,
@Inject(MAT_DIALOG_DATA) public message: string
) {
}
ngOnInit() {
}
}
alert-dialog.component.html
<div mat-dialog-actions class="text-center">
{{message}}
</div>
<div mat-dialog-actions class="btnContainer">
<button mat-stroked-button color="primary" class="commonBtn"
(click)="this.dialogRef.close(true)">확인</button>
</div>
Dialog 안에 주입받은 애들은 Module 에 providers 에 걸려있고,
import {NgModule} from '@angular/core';
...
@NgModule({
...
providers : [
...
{ provide: MAT_DIALOG_DATA, useValue: {} },
{ provide: MatDialogRef, useValue: {} },
]
})
export class CoreModule {
}
서비스에서 이런식으로 호출을 한다.
dialog.service.ts
import {Injectable} from '@angular/core';
import {AlertDialogComponent} from '../../../shared/components/dialog/alert-dialog/alert-dialog.component';
...
import {MatDialog, MatDialogRef} from "@angular/material/dialog";
...
@Injectable({
providedIn: 'root'
})
export class DialogService {
constructor(
private dialog: MatDialog,
) {
}
openAlertDialog(message: string, size?: string, interval?: number) {
const dialogRef = this.dialog.open(AlertDialogComponent, {
width: size || '350px',
data: message
});
if(interval && interval > 0){
setTimeout(()=>{
dialogRef.close()
},10000)
}
return dialogRef.afterClosed()
}
}
그러면 저 service 주입받은 곳에서 openAlertDialog 를 불러 호출하는 식이었다.
처음에는 저 library 소스들을 아무리 뜯어봐도 이해가 안갔다. AlertDialogComponent 에서 주입받은 dialogRef 가 자기 자신을 참조에 걸어주고, 거기에 MAT_DIALOG_DATA 로 주입받은 data 값이 들어가는 식이었다.
그러다가 https://poiemaweb.com/angular-service 이분 블로그에서 의존성 주입에 대한 설명을 좀 더 읽고.. 쪼끔은 이해가 갔다.
@Injectable({
providedIn: 'root',
})
이걸로 선언한 애들은 directive 건 service 건 class 건, 싱글턴으로 프로젝트 전역에서 주입받아서 접근하고 쓸 수 있는 것이었다.
module 에 providers 에 선언된 데이터들도 전역에서 쓸 수 있는 일종의 global 변수 같은 개념이었다.
여기까지 이해한 내용을 바탕으로 injection 을 이용해서 앞서 만들었던 Dialog 를 좀 더 개선해 보았다.
어디까지나 Angular Material 흉내를 내는 수준이지, 완벽하게 따라하지는 못했지만. 필요한 기능이 생기면 차차 개선이 되리라고 본다.
앞서 만든 것 중, IptDialogDirective 는 변한 게 없다.
이번에 추가된 것은 ipt-dialog-ref.ts
- 여기에 Dialog View 참조를 걸어서 어디서든 접근할 수 있게 했다.
- @Injectable 선언하여 주입, 프로젝트 전역에서접근 가능하게 했다.
import {Inject, Injectable, ViewContainerRef} from "@angular/core";
import {IptDialogDirective} from "./ipt-dialog.directive";
@Injectable({
providedIn: 'root'
})
export class IptDialogRef<T, R = any> {
viewContainerRef:ViewContainerRef;
constructor(
@Inject(IptDialogDirective) public dialogHost: IptDialogDirective,
) {
}
}
ipt-dialog.component.ts
- ref 타입을 하나 추가해서, dialog 본체를 참조 걸 수 있게 했다. (close 함수 등을 위해 씀. 어디까지나 내 수준에서 만든거 ...)
import {ViewContainerRef} from "@angular/core";
export interface IptDialogComponent {
data: any;
ref: ViewContainerRef;
}
ipt-dialog.ts
- 이전과 달리 IptDialog 를 Sample Dialog 컴포넌트에서 상속을 받는 것이 아니고, 주입을 통해서 data 를 가져온다.
- IPT_DIALOG_DATA 를 선언하여 Dialog 에 들어갈 데이터를 주입받게 했다.
- 기존에 app.component.ts 에서 호출하던 함수를 ipt-dialog 에 open 이라는 함수로 옮겨서 이제 open 을 통해 Dialog 를 생성한다.
- @Injectable 을 선언하여 Dialog 호출하고 싶은 곳 어디서나 주입해서 쓸 수 있게 만들었다.
import {Inject, Injectable, InjectionToken, Type} from "@angular/core";
import {IptDialogRef} from "./ipt-dialog-ref";
import {IptDialogDirective} from "./ipt-dialog.directive";
import {IptDialogComponent} from "./ipt-dialog.component";
export const IPT_DIALOG_DATA = new InjectionToken<any>('ipg-dialog');
@Injectable({
providedIn: 'root'
})
export class IptDialog {
constructor(
public dialogRef: IptDialogRef<any>,
@Inject(IPT_DIALOG_DATA) public data: any,
@Inject(IptDialogDirective) public dialogHost: IptDialogDirective,
) {}
public open(component: Type<any>, data: any) {
const viewContainerRef = this.dialogHost.viewContainerRef;
viewContainerRef.clear();
const componentRef = viewContainerRef.createComponent<IptDialogComponent>(component);
componentRef.instance.data = {
...data,
};
this.dialogRef.viewContainerRef = viewContainerRef;
}
}
app.module.ts
- 전역에서 쓸 IPT_DIALOG_DATA, IptDialogRef 를 providers 에 넣어준다
...
import { IPT_DIALOG_DATA } from './ipt-dialog';
import { IptDialogRef } from './ipt-dialog-ref';
@NgModule({
imports: [BrowserModule, FormsModule],
declarations: [AppComponent, IptDialogSampleComponent, IptDialogDirective],
providers: [
{ provide: IPT_DIALOG_DATA, useValue: {} },
{ provide: IptDialogRef, useValue: {} },
],
bootstrap: [AppComponent],
})
export class AppModule {}
ipt-dialog-sample.component.ts
- IptDialog 상속을 취소한다
- @Input() data 도 이제는 데이터를 직접 주입받으니까 지워준다
- dialogRef 와 IPT_DIALOG_DATA 를 주입해준다!
import { Component, Inject, Input, OnInit } from '@angular/core';
import { IPT_DIALOG_DATA } from './ipt-dialog';
import { IptDialogRef } from './ipt-dialog-ref';
@Component({
selector: 'app-ipt-dialog-sample',
template: `
<div class="dim">
<div class="dialog">
<p>{{data.message}}</p>
<button (click)="close()"> Confirm </button>
</div>
</div>
`,
providers: [],
})
export class IptDialogSampleComponent {
// implements IptDialog {
//@Input() data: any;
constructor(
public dialogRef: IptDialogRef<IptDialogSampleComponent>,
@Inject(IPT_DIALOG_DATA) public data: any
) {}
ngOnInit(): void {}
close() {
//this.data.ref.remove();
this.dialogRef.viewContainerRef.remove();
}
}
마지막으로 app.component.ts 에서 Dialog 호출부를 바꿔준다.!
import { Component, ViewChild } from '@angular/core';
import { IptDialogDirective } from './ipt-dialog.directive';
import { IptDialogComponent } from './ipt-dialog.component';
import { IptDialogSampleComponent } from './ipt-dialog-sample.component';
import { IptDialog } from './ipt-dialog';
@Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'],
})
export class AppComponent {
constructor(private iptDialog: IptDialog) {}
// @ViewChild(IptDialogDirective, { static: true })dialogHost: IptDialogDirective;
openDialog(e) {
this.iptDialog.open(IptDialogSampleComponent, {
width: '350px',
message: 'hello ipt dialog~ welcome!',
});
// const viewContainerRef = this.dialogHost.viewContainerRef;
// viewContainerRef.clear();
// const componentRef = viewContainerRef.createComponent<IptDialogComponent>(
// IptDialogSampleComponent
// );
// componentRef.instance.data = {
// width: '350px',
// message: 'hello ipt dialog~ welcome!',
// ref: viewContainerRef,
// };
}
}
그러면 이전과 같이 동작하는 화면을 볼 수 있다.
전체 소스 참조:
이렇게 개선했기 때문에
이제는 어느 화면에서나 간단하게 iptDialog 클래스를 주입받고, 3줄 정도면 Dialog 를 부를 수 있게 됐다.
constructor(private iptDialog: IptDialog) {}
openDialog(e) {
this.iptDialog.open(IptDialogSampleComponent, {
width: '350px',
message: 'hello ipt dialog~ welcome!',
});
}
우리 프로젝트는 저 호출부를 dialog.service 에 만들어서, dialog.service 를 주입받은 곳에서 호출하게끔 되어있다.
...솔직히 아직 100% 이해는 안된다. Angular Framework의 신비랄까 ;;
완벽하게 동작원리를 이해 못하고 따라 만들어서 ...
(저.. 샘플 다이얼로그에서 IPT_DIALOG_DATA 를 찍으면 데이터가 그냥 넘어오는 그 부분이 내 머리로는 이해 안감..... 완전신기!!!)
'프론트엔드 개발' 카테고리의 다른 글
F12, 마우스 우클릭, 복사 등 막기 (0) | 2023.01.13 |
---|---|
[Angular] Dialog 개발 - 1 (0) | 2022.04.14 |