FULL CODE – Membuat Aplikasi CRUD di Ionic 6 dengan MySQL

app.module.ts

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

import { IonicModule, IonicRouteStrategy } from '@ionic/angular';

import { AppComponent } from './app.component';
import { AppRoutingModule } from './app-routing.module';

import { HttpClientModule } from '@angular/common/http';
import { CKEditorModule } from '@ckeditor/ckeditor5-angular';

@NgModule({
  declarations: [AppComponent],
  entryComponents: [],
  imports: [
    BrowserModule, 
    IonicModule.forRoot(), 
    AppRoutingModule,
    HttpClientModule,
    CKEditorModule
  ],
  providers: [{ provide: RouteReuseStrategy, useClass: IonicRouteStrategy }],
  bootstrap: [AppComponent],
})
export class AppModule {}

app-routing.module.ts

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

const routes: Routes = [
  {
    path: '',
    redirectTo: 'home',
    pathMatch: 'full'
  },
  {
    path: 'detail',
    loadChildren: () => import('./modals/detail/detail.module').then( m => m.DetailPageModule)
  },
  {
    path: 'home',
    loadChildren: () => import('./pages/home/home.module').then( m => m.HomePageModule)
  },
  {
    path: 'form-note',
    loadChildren: () => import('./modals/form-note/form-note.module').then( m => m.FormNotePageModule)
  },
];

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

note.service.ts

import { Injectable } from '@angular/core';
import { HttpClient } from "@angular/common/http";

@Injectable({
  providedIn: 'root'
})
export class NoteService {

  server:string = "http://localhost/api/";

  constructor(
    public http: HttpClient
  ) { }

  getListNote(){
    return this.http.get(this.server+'note/');
  }
 
  getDetailNote(noteId){
    return this.http.get(this.server+'note/'+noteId);
  }
 
  addNote(noteTitle,noteContent){
    return this.http.post(this.server+'note/',{
      note_title:noteTitle,
      note_content:noteContent
    });
  }
 
  editNote(noteId,noteTitle,noteContent){
    return this.http.put(this.server+'note/'+noteId,{
      note_title:noteTitle,
      note_content:noteContent
    });
  }
 
  deleteNote(noteId){
    return this.http.delete(this.server+'note/'+noteId);
  }
}

home.page.html

<ion-header>
  <ion-toolbar color="primary">
    <ion-title>Catatan</ion-title>
  </ion-toolbar>
</ion-header>
 
<ion-content>
 
    <ion-fab vertical="bottom" horizontal="end" slot="fixed">
        <ion-fab-button (click)="modalFormNote()">
            <ion-icon name="add-outline"></ion-icon>
        </ion-fab-button>
    </ion-fab>
 
    <ion-list>
        <ion-item-sliding *ngFor="let item of listsNote;let i = index">
            <ion-item lines="full" (click)="modalDetail(i)">
                <ion-label>{{item.note_title}}</ion-label>
            </ion-item>
            <ion-item-options  side="end">
                <ion-item-option color="primary" (click)="modalFormNote(i)">Edit</ion-item-option>
                <ion-item-option color="danger" (click)="deleteNote(i)">Delete</ion-item-option>
            </ion-item-options>
        </ion-item-sliding>
    </ion-list>
</ion-content>

home.page.ts

import { Component, OnInit } from '@angular/core';
import { NoteService } from '../../services/note.service';
import { ToastController, ModalController, AlertController, LoadingController } from '@ionic/angular';
import { DetailPage } from  '../../modals/detail/detail.page';
import { FormNotePage } from  '../../modals/form-note/form-note.page';
  
 
@Component({
  selector: 'app-home',
  templateUrl: './home.page.html',
  styleUrls: ['./home.page.scss'],
})
export class HomePage implements OnInit {
 
  listsNote:any;
 
  constructor(
    private noteService:NoteService,
    public toastCtrl:ToastController,
    public modalCtrl:ModalController,
    public alertCtrl:AlertController,
    public loadCtrl:LoadingController
  ) { }
 
  ngOnInit() {
     
  }
 
  ionViewWillEnter(){
    // MEMANGGIL API LIST NOTE
    this.noteService.getListNote().subscribe(
      res => {
        if(res["result"]=="success"){ // JIKA HASIL SUCCESS
 
          this.listsNote = res["data"];
         
        }else{ // JIKA HASIL ERROR
         
          this.presentToast(res["message"]);
         
        }
      },
      err => {
        console.log(err.error)
      }
    )
  }

  // FUNCTION MODAL DETAIL
  async modalDetail(i){
    const modal = await this.modalCtrl.create({
      component:DetailPage,
      componentProps:{
        dataNote:this.listsNote[i]
      }
    });
   
    return await modal.present();
  }

  // FUNCTION MODAL TAMBAH NOTE
  async modalFormNote(i=null){
    let data = i==null ? null : this.listsNote[i];

    const modal = await this.modalCtrl.create({
      component:FormNotePage,
      componentProps:{
        dataNote:data
      }
    });

    // KETIKA MODAL DI TUTUP
    modal.onWillDismiss().then(dataReturned => {
      
      if(dataReturned.role=="add"){ // JIKA DIDAPAT DARI ADD DATA

        this.listsNote.unshift(dataReturned.data);
      
      }else if(dataReturned.role=="edit"){ // JIKA DIDAPAT DARI EDIT DATA
      
        this.listsNote[i] = dataReturned.data
      
      }
      
    });

    return await modal.present();
  }

  // FUNCTION CONFIRM DELETE NOTE
  async deleteNote(i){
    // MEMBUAT ALERT PROMPT
    const alert = await this.alertCtrl.create({
      header: 'Delete',
      message: 'Hapus Catatan ?',
      buttons: [
        {
          text:"Tidak"
        },
        {
          text:"Iya",
          role:"iya"
        }
      ]
    });
   
    // RUNNING ALERT
    await alert.present();
   
    // SETELAH ALERT DITUTUP
    await alert.onDidDismiss().then(res => {
      if(res.role=="iya"){ // JIKA BUTTON YANG DITEKAN = "IYA"
        this.prosesDelete(i);
      }
    });
  }
   
  // FUNCTION ACTION DELETE NOTE
  async prosesDelete(i){
    // MEMBUAT LOADING
    const loading = await this.loadCtrl.create({
      message: 'Please wait'
    });
   
    // TAMPILKAN LOADING
    await loading.present();
   
    // MENGIRIM ID NOTE YANG INGIN DIHAPUS KE API
    await this.noteService.deleteNote(this.listsNote[i]["note_id"]).subscribe(
      res => {
   
        if(res["result"]=="success"){ // JIKA HASIL KE API SUCCESS
         
          this.listsNote.splice(i,1); // HAPUS DATA NOTE DARI ARRAY
         
        }
   
        // TAMPILKAN PESAN
        this.presentToast(res["message"]);
        
        // SEMBUNYIKAN LOADING
        loading.dismiss();
      },
      err => {
        console.log(err.error)
       
        // SEMBUNYIKAN LOADING
        loading.dismiss();
      }
    );
  }
 
  //FUNCTION PRESENT TOAST
  async presentToast(a){
    const toast = await this.toastCtrl.create({
      message: a,
      duration: 1500
    });
    toast.present();
  }
 
}

detail.page.html

<ion-header>
  <ion-toolbar color="primary">
    <ion-title>{{dataNote.note_title}}</ion-title>
    <ion-buttons slot="end" (click)="closeModal()">
        <ion-button>
            <ion-icon name="close-outline"></ion-icon>
        </ion-button>
    </ion-buttons>
  </ion-toolbar>
</ion-header>
 
<ion-content>
    <ion-card>
        <ion-card-content>
            <div [innerHTML]="dataNote.note_content"></div>
        </ion-card-content>
    </ion-card>
</ion-content>

detail.page.ts

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

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

  @Input() public dataNote:any;

  constructor(
    public modalCtrl:ModalController
  ) { }

  ngOnInit() {
  }

  //Tutup Modal
  async closeModal(data){
    await this.modalCtrl.dismiss(data);
  }

}

form-note.module.ts

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';

import { IonicModule } from '@ionic/angular';

import { FormNotePageRoutingModule } from './form-note-routing.module';

import { FormNotePage } from './form-note.page';
import { CKEditorModule } from '@ckeditor/ckeditor5-angular';

@NgModule({
  imports: [
    CommonModule,
    FormsModule,
    IonicModule,
    FormNotePageRoutingModule,
    CKEditorModule,
  ],
  declarations: [FormNotePage]
})
export class FormNotePageModule {}

form-note.page.html

<ion-header>
  <ion-toolbar color="primary">
    <ion-title>Catatan</ion-title>
    <ion-buttons slot="end" (click)="closeModal(null)">
      	<ion-button>
        	<ion-icon name="close-outline"></ion-icon>
      	</ion-button>
    </ion-buttons>
  </ion-toolbar>
</ion-header>

<ion-content padding>
	<ion-input type="text" [(ngModel)]="noteId" style="display: none;"></ion-input>
	<ion-card>
		<ion-card-content>
			<ion-item-group>
				<ion-label>Title</ion-label>
				<ion-input type='text' [(ngModel)]="noteTitle"></ion-input>
			</ion-item-group>
			<ion-item-group>
				<ion-label>Content</ion-label>
				<ckeditor [editor]="Editor" [(ngModel)]="noteContent" style="height: 500px;" [config]="{toolbar : ['heading','|','bold', 'italic','underline','|','NumberedList','BulletedList','Blockquote']}"></ckeditor>
			</ion-item-group>

			<ion-button expand="block" color="primary" (click)="saveNote()" *ngIf="noteId==''">
				Save
			</ion-button>
			<ion-button expand="block" color="primary" (click)="updateNote(noteId)" *ngIf="noteId!=''">
				Update
			</ion-button>
		</ion-card-content>
	</ion-card>
</ion-content>

form-note.page.ts

import { Component, OnInit, Input } from '@angular/core';
import { ModalController, ToastController, LoadingController } from '@ionic/angular';
import * as ClassicEditor from '@ckeditor/ckeditor5-build-classic';
import { NoteService } from '../../services/note.service';

@Component({
  selector: 'app-form-note',
  templateUrl: './form-note.page.html',
  styleUrls: ['./form-note.page.scss'],
})
export class FormNotePage implements OnInit {

  @Input() public dataNote:any;
  noteId:any;
  noteTitle:string = "";
  noteContent:any;

  public Editor = ClassicEditor;

  constructor(
    public modalCtrl:ModalController,
    private noteService:NoteService,
    public toastCtrl:ToastController,
    public loadCtrl:LoadingController
  ) { }

  ngOnInit() {
  }

  ionViewWillEnter(){
    if(this.dataNote===null){
      this.noteId = "";
    }else{
      this.noteId = this.dataNote.note_id;
      this.noteTitle = this.dataNote.note_title;
      this.noteContent = this.dataNote.note_content;
    }
    console.log(this.noteId);
  }

  // MENYIMPNAN FORM NOTE
  async saveNote(){
    const loading = await this.loadCtrl.create({
      message: 'Please wait'
    });

    await loading.present();

    await this.noteService.addNote(this.noteTitle,this.noteContent
    ).subscribe(
      res => {
        console.log(res);
        if(res["result"]=="success"){
          this.closeModal({
            note_id:res["data"]["note_id"],
            note_title:this.noteTitle,
            note_content:this.noteContent
          },"add")
        }

        this.presentToast(res["message"]);

        loading.dismiss();
      },
      err => {
        console.log(err);

        loading.dismiss();
      }
    );
  }

  // MERUBAH FORM NOTE
  async updateNote(noteId){
    const loading = await this.loadCtrl.create({
      message: 'Please wait'
    });

    await loading.present();

    await this.noteService.editNote(noteId,this.noteTitle,this.noteContent
    ).subscribe(
      res => {
        console.log(res);
        if(res["result"]=="success"){
          this.closeModal({
            note_id:this.noteId,
            note_title:this.noteTitle,
            note_content:this.noteContent
          },"edit")
        }

        this.presentToast(res["message"]);

        loading.dismiss();
      },
      err => {
        console.log(err);
        
        loading.dismiss();
      }
    );
  }


  async presentToast(a){
    const toast = await this.toastCtrl.create({
      message: a,
      duration: 1500
    });
    toast.present();
  }

  //Tutup Modal
  async closeModal(data=null,role=null){
    await this.modalCtrl.dismiss(data,role);
  }

}