# 사전 준비사항
가장 먼저 최신버전의 Node.js가 설치되어 있고, 커맨드창에서 npm install 명령어가 사용가능하여야 한다.
# 새로운 Ionic Application 생성
1. npm install -g cordova ionic
cordova cli와 ionic cli를 global로 설치한다.
2. ionic -v
ionic cli가 정상적으로 설치되었는지 확인한다.
3. ionic start ionic-sqlite-app blank --type=angular
ionic cli를 통해 빈 ionic 프로젝트를 생성한다.
4. cd ionic-sqlite-app && ionic serve
ionic serve 명령어를 통해, 정상적으로 localhost:8100이 실행되는지 체크한다.
# dump Sql문 작성하기
1. assets/dump.sql 문 생성
2. 아래 문장 입력
```
CREATE TABLE IF NOT EXISTS songtable(
id INTEGER PRIMARY KEY AUTOINCREMENT,
artist_name TEXT,
song_name TEXT
);
INSERT or IGNORE INTO songtable(id, artist_name, song_name) VALUES (1, 'Justin Bieber', 'Yummy');
INSERT or IGNORE INTO songtable(id, artist_name, song_name) VALUES (2, 'Jonas Brothers', 'What A Man Gotta Do');
INSERT or IGNORE INTO songtable(id, artist_name, song_name) VALUES (3, 'Life Is Good', 'Future');
INSERT or IGNORE INTO songtable(id, artist_name, song_name) VALUES (4, 'Lauv', 'Tattoos Together');
INSERT or IGNORE INTO songtable(id, artist_name, song_name) VALUES (5, 'Heavy Steppers', 'Whateva');
INSERT or IGNORE INTO songtable(id, artist_name, song_name) VALUES (6, 'DigDat 2020', 'Ei8ht Mile');
INSERT or IGNORE INTO songtable(id, artist_name, song_name) VALUES (7, 'Blackbear', 'me & ur ghost');
INSERT or IGNORE INTO songtable(id, artist_name, song_name) VALUES (8, 'Hailee Steinfeld', 'Wrong Direction');
```
# Routing 구성하기
1. ng g page song
angular cli로 새로운 page[Page 역할을 하는 Component임] 객체를 생성
2. app-router.module.ts 상에서, 적당하게, Routing이 되었는지 확인
'song' 경로를 'song/:id' 경로로 바꾸어줌
# Sqlite 관련 패키지 설치
npm install @ionic-native/sqlite ionic cordova plugin add cordova-sqlite-storage npm install @ionic-native/sqlite-porter ionic cordova plugin add uk.co.workingedge.cordova.plugin.sqliteporter
프로젝트 경로상에서, ionic-native 개별 모듈을 설치하여, Native API에 접근 가능하도록 함
sqlporter 플러그인은 sql이나 json을 통해 sql database로 부터 import & export를 하도록 도와준다.
# app.module.ts에 plugin 적용
// plugins
import { SQLite } from '@ionic-native/sqlite/ngx';
import { HttpClientModule } from '@angular/common/http';
import { SQLitePorter } from '@ionic-native/sqlite-porter/ngx';
import { Injectable } from '@angular/core';
import { Platform } from '@ionic/angular';
import { Song } from './song';
import { HttpClient } from '@angular/common/http';
import { BehaviorSubject, Observable } from 'rxjs';
import { SQLitePorter } from '@ionic-native/sqlite-porter/ngx';
import { SQLite, SQLiteObject } from '@ionic-native/sqlite/ngx';
@Injectable({
providedIn: 'root'
})
export class DbService {
private storage: SQLiteObject; //Sql Db 객체
songsList = new BehaviorSubject([]); //조회용 객체 리스트 선언(rxJs)
private isDbReady: BehaviorSubject<boolean> = new BehaviorSubject(false); //조회완료체크용(rxJs)
constructor(
private platform: Platform,
private sqlite: SQLite,
private httpClient: HttpClient,
private sqlPorter: SQLitePorter,
) {
this.platform.ready().then(() => { //해당 service load 완료시 호출
this.sqlite.create({
name: 'positronx_db.db', //db 생성
location: 'default'
})
.then((db: SQLiteObject) => {
this.storage = db; //생성된 db를 storage로 정의
this.getFakeData(); //초기 dummy.sql로 data 일괄 저장 후, 조회 수행
});
});
}
dbState() { //db 상태(실시간) 반환 - listener 형태
return this.isDbReady.asObservable();
}
fetchSongs(): Observable<Song[]> { //실시간 Songs 반환 - listener 형태
return this.songsList.asObservable();
}
// Render fake data
getFakeData() {
this.httpClient.get( //sql Text 불러오기
'assets/dump.sql',
{responseType: 'text'}
).subscribe(data => {
this.sqlPorter.importSqlToDb(this.storage, data) //db로 sql Text 쿼리
.then(_ => {
this.getSongs(); //쿼리 완료 후 메인재조회, 비동기 호출
this.isDbReady.next(true); //DB 완료 지정
})
.catch(error => console.error(error));
});
}
// Get list
getSongs(){
return this.storage.executeSql('SELECT * FROM songtable', []).then(res => {
let items: Song[] = []; //조회결과를 담아올 그릇 선언
if (res.rows.length > 0) { //결과가 있을 경우
for (var i = 0; i < res.rows.length; i++) {
items.push({
id: res.rows.item(i).id,
artist_name: res.rows.item(i).artist_name,
song_name: res.rows.item(i).song_name
}); //결과값 복사
}
}
this.songsList.next(items); //결과값 비동기 반환
});
}
// Add
addSong(artist_name, song_name) {
let data = [artist_name, song_name];
return this.storage.executeSql('INSERT INTO songtable (artist_name, song_name) VALUES (?, ?)', data)
.then(res => {
this.getSongs(); //생성 완료 후 재조회, 비동기 호출
});
}
// Get single object
getSong(id): Promise<Song> {
return this.storage.executeSql('SELECT * FROM songtable WHERE id = ?', [id]).then(res => {
return {
id: res.rows.item(0).id,
artist_name: res.rows.item(0).artist_name,
song_name: res.rows.item(0).song_name
} //조회 완료 후 비동기 Promise 객체 호출
});
}
// Update
updateSong(id, song: Song) {
let data = [song.artist_name, song.song_name];
return this.storage.executeSql(`UPDATE songtable SET artist_name = ?, song_name = ? WHERE id = ${id}`, data)
.then(data => {
this.getSongs(); //업데이트 완료 후 재조회, 비동기 호출
})
}
// Delete
deleteSong(id) {
return this.storage.executeSql('DELETE FROM songtable WHERE id = ?', [id])
.then(_ => {
this.getSongs(); //삭제 완료 후 재조회, 비동기 호출
});
}
}
# Add, Display & Delete Data from SQLite Database
개별 Page 파일에 ReactiveFormModule Angular 모듈 import 수행
1. home.module.ts & song.module.ts 파일 내 하기 코드 삽입
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
@NgModule({
imports: [
ReactiveFormsModule,
FormsModule
]
})
2. home.page.ts 파일 수정
import { Component, OnInit } from '@angular/core';
import { FormGroup, FormBuilder } from "@angular/forms";
import { DbService } from './../services/db.service'; //DbService 요소 dependency
import { ToastController } from '@ionic/angular';
import { Router } from "@angular/router";
@Component({
selector: 'app-home',
templateUrl: 'home.page.html',
styleUrls: ['home.page.scss'],
})
export class HomePage implements OnInit {
mainForm: FormGroup;
Data: any[] = []
constructor(
private db: DbService, //DbService 요소 dependency
public formBuilder: FormBuilder,
private toast: ToastController,
private router: Router
) { }
ngOnInit() {
this.db.dbState().subscribe((res) => { //Observable 객체는 subscribe(구독) 가능(변경시 호출)
if(res){ //값이 True일 경우
this.db.fetchSongs().subscribe(item => {
this.Data = item //DbService 내에 songlist 변수 반환(값 변경시)
})
}
});
this.mainForm = this.formBuilder.group({ //입력 폼 View 그룹화
artist: [''],
song: ['']
})
}
storeData() {
this.db.addSong( //DbService 내에 addSong함수 호출,
this.mainForm.value.artist, //입력완료후, 자동으로 fetchSongs()까지 영향 준다.
this.mainForm.value.song
).then((res) => {
this.mainForm.reset(); //입력 폼 View 초기화
})
}
deleteSong(id){
this.db.deleteSong(id).then(async(res) => { //DbService 내에 deleteSong함수 호출,
let toast = await this.toast.create({ //res가 올때까지 기다리다가 생성
message: 'Song deleted',
duration: 2500
});
toast.present(); //Toast View 보여주기
})
}
}
3. home.page.html 수정
ion-header>
<ion-toolbar>
<ion-title>Ionic 4 SQLite CRUD Example</ion-title>
</ion-toolbar>
</ion-header>
<ion-content>
<ion-list lines="full">
<form [formGroup]="mainForm" (ngSubmit)="storeData()">
<ion-item>
<ion-label position="floating">Artist name</ion-label>
<ion-input formControlName="artist" type="text" required></ion-input>
</ion-item>
<ion-item>
<ion-label position="floating">Song name</ion-label>
<ion-input formControlName="song" type="text" required>
</ion-input>
</ion-item>
<ion-row>
<ion-col>
<ion-button type="submit" color="primary" shape="full" expand="block">
Add Song
</ion-button>
</ion-col>
</ion-row>
</form>
</ion-list>
<ion-list>
<ion-list-header>
Songs
</ion-list-header>
<ion-item lines="inset" *ngFor="let data of Data">
<ion-label>
<h5>{{data.artist_name}}</h5>
<p>{{data.song_name}}</p>
</ion-label>
<div class="item-note" item-end>
<ion-icon name="create" style="zoom:2.0" [routerLink]="['/song/', data.id]"></ion-icon>
<ion-icon name="trash" style="zoom:2.0" (click)="deleteSong(data.id)"></ion-icon>
</div>
</ion-item>
</ion-list>
</ion-content>
# SQL DB에서 Data 수정하기
1. song.page.html 수정
<ion-header>
<ion-toolbar>
<ion-buttons slot="start">
<ion-back-button></ion-back-button>
</ion-buttons>
<ion-title>Edit Song</ion-title>
</ion-toolbar>
</ion-header>
<ion-content>
<ion-list lines="full">
<form [formGroup]="editForm" (ngSubmit)="saveForm()">
<ion-item>
<ion-label position="floating">Artist name</ion-label>
<ion-input formControlName="artist_name" type="text" required></ion-input>
</ion-item>
<ion-item>
<ion-label position="floating">Song name</ion-label>
<ion-input formControlName="song_name" type="text" required>
</ion-input>
</ion-item>
<ion-row>
<ion-col>
<ion-button type="submit" color="primary" shape="full" expand="block">
Update
</ion-button>
</ion-col>
</ion-row>
</form>
</ion-list>
</ion-content>
2. song.page.ts 수정
import { Component, OnInit } from '@angular/core';
import { FormGroup, FormBuilder } from "@angular/forms";
import { DbService } from './../services/db.service'
import { ActivatedRoute, Router } from "@angular/router";
@Component({
selector: 'app-song',
templateUrl: './song.page.html',
styleUrls: ['./song.page.scss'],
})
export class SongPage implements OnInit {
editForm: FormGroup;
id: any;
constructor(
private db: DbService, //DB Service에 dependency
private router: Router,
public formBuilder: FormBuilder,
private actRoute: ActivatedRoute
) {
this.id = this.actRoute.snapshot.paramMap.get('id'); //path 리스트 param에서 id 속성 가져오는 방법
this.db.getSong(this.id).then(res => { //DbService 내 getSong함수 호출
this.editForm.setValue({ //결과 View에 보여주기
artist_name: res['artist_name'],
song_name: res['song_name']
})
})
}
ngOnInit() {
this.editForm = this.formBuilder.group({
artist_name: [''], //빈리스트
song_name: ['']
})
}
saveForm(){
this.db.updateSong(this.id, this.editForm.value) //DbService 내 updateSong함수 호출
.then((res) => {
console.log(res)
this.router.navigate(['/home']); //페이지 이동하는 방법
})
}
}
# Run Ionic SQLite App in Device
안드로이드 스튜디오 상의 가상 장치를 실행
1. ionic cordova platform add android
2. ionic cordova run android --livereload
- 시스템 환경변수에 gradle 경로가 저장되어 있어야 한다. android vm이 필요하다. studio가 설치되어 있더라도, gradle을 별도로 설치해야할 수도 있다.
# 디버깅
- gradle 을 찾지 못함 : https://gradle.org/install/
- minsdk version 오류 : In case of Hybrid/Ionic app such issue could get fixed by changing minSdkVersion in config.xml is what I did.
#출처
- https://www.positronx.io/ionic-sqlite-database-crud-app-example-tutorial/
'[DEV] App Dev ∕ Mobile > Framework ∕ Ionic' 카테고리의 다른 글
ServiceWorker과 PWA(Progressive Web App) (0) | 2020.08.07 |
---|---|
Manifest.json과 PWA(Progressive Web App) (0) | 2020.08.06 |
Bug Report (0) | 2020.08.06 |
Ionic 전체 프로젝트 구조 설계 (0) | 2020.08.05 |
Ionic 기초 (0) | 2020.08.04 |
최근댓글