# 사전 준비사항

가장 먼저 최신버전의 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';


imports : [...
    HttpClientModule
...]
providers : [...
    SQLite,
    SQLitePorter,
...]

# CRUD Service 생성
1. ng g class services/song
dto를 생성한다.
2. services/song.ts 작성
export class Song {
  id: number;
  artist_name: string;
  song_name: string;
}

3. ng g service services/db
service 객체를 생성

4. services/db.service.ts 작성
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
  • 네이버 블러그 공유하기
  • 네이버 밴드에 공유하기
  • 페이스북 공유하기
  • 카카오스토리 공유하기