It’s about time to see some Ionic 2 in action, right? Therefore I started my journey with a little Evernote light clone, which is a little app to write notes that shows you a lot of the new features that comes with Angular 2. Especially we can see how to use TypeScript, what components in Angular 2 mean and how to use page, provider and pipe.
Overall we will use Ionic 2 SqlStorage for storing data in our app while creating a tiny notes app.
Setting up a blank new app
New to Ionic 2? Check out my rapid results course Ionic 2 in One Hour!
As always we start with a blank app. Make sure you have installed the Ionic 2 beta following this guide!
We specify the blank template and also to create this project as Ionic 2 project plus TypeScript. I don’t want to go into detail about TypeScript, making a story short: I think it will be the future and how to write JavaScript code. So it’s a good idea to get used to the syntax already!
Now go to your command line and run:
ionic start devdactic-notes blank --v2 --ts cd devdactic-notes ionic g page NoteDetail ionic g provider NoteService ionic plugin add cordova-sqlite-storage
After creating the project we also use the new Ionic 2 CLI generator command to create a new page and a provider, which we will see in action later. This just helps us to easily create new files and add them to our project, very handy.
Finally we install the Cordova plugin for SqlStorage, which you need if you want to run the app on a real device later. Otherwise the SqlStorage will fall back using WebSQL which is not a complete safe place for us to store information as it can get cleaned.
After the basic setup create a folder and file at app/pipes/truncate.ts which we also need later on.
The last step now is to hook up the CSS of our HomePage into the app/theme/app.core.scss so open that file and add:
@import '../pages/home/home'; @import '../pages/note-detail/note-detail.scss';
That’s it for the basic setup. You should be able to run your app now like always, if you want take a look around and try to understand how Ionic 2 works. If you’re ready, let’s move on.
The basic use of Ionic 2 SqlStorage
We first of all start with the entry point of our app, so open the app/app.ts and insert:
import {App, Platform} from 'ionic-angular'; import {StatusBar} from 'ionic-native'; import {HomePage} from './pages/home/home'; import {NoteService} from './providers/note-service/note-service'; @App({ template: '<ion-nav [root]="rootPage"></ion-nav>', providers: [NoteService], config: {} // http://ionicframework.com/docs/v2/api/config/Config/ }) export class MyApp { rootPage: any = HomePage; constructor(platform: Platform) { platform.ready().then(() => { StatusBar.styleDefault(); }); } }
We haven’t actually changed a lot here from what’s already in there. But what we did is import our NoteService, the service that will be a provider throughout the complete app for all interactions with our Ionic 2 SqlStorage. That is also the reason why we add it inside the @App annotation.
The rest of this class just bootstraps our application using a standard inline template and by tting our HomePage as the root of our app.
As already said, the most important class for all of our database interaction is the NoteService. This class will handle reading and writing operations, while also defining a little Note class at the top to wrap some values in one object. Pretty cool this TypeScript, right?
The functions mostl speak for themselves, so open the app/providers/note-servie/note-service.ts and insert:
import {Storage, SqlStorage} from 'ionic-angular'; import {Injectable} from 'angular2/core'; export class Note { title: string; text: string; id: number; constructor(title: string, text: string, id: number) { this.title = title; this.text = text; this.id = id; } } @Injectable() export class NoteService { storage: Storage = null; // Init an empty DB if it does not exist by now! constructor() { this.storage = new Storage(SqlStorage); this.storage.query('CREATE TABLE IF NOT EXISTS notes (id INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT, text TEXT)'); } // Get all notes of our DB public getNotes() { return this.storage.query('SELECT * FROM notes'); } // Save a new note to the DB public saveNote(note: Note) { let sql = 'INSERT INTO notes (title, text) VALUES (?,?)'; return this.storage.query(sql, [note.title, note.text]); } // Update an existing note with a given ID public updateNote(note: Note) { let sql = 'UPDATE notes SET title = \"' + note.title + '\", text = \"' + note.text + '\" WHERE id = \"' + note.id + '\"'; this.storage.query(sql); } // Remoe a not with a given ID public removeNote(note: Note) { let sql = 'DELETE FROM notes WHERE id = \"' + note.id + '\"'; this.storage.query(sql); } }
Inside our constructor we create a new table inside our database if it doesn’t exist, and our variable storage
holds the reference to that database.
All of the other functions simply perform an SQL statement on our SqlStorage and return the result (sometimes).
Any questions up to this point?
The List for our Notes
So now we want to actually use our service, and the first page our user sees is the HomePage.
Inside this class we import the NoteService, the Note class, our DetailsPage which will be pushed once we create a new note or if we select a note, and finally also a pipe called Truncate.
A pipe is pretty similar to an Angular 1 directive, and we will see it in action inside our view later. Again, we have to add this pipe inside the @Page annotation to use it later.
The array of notes inside our class will be filled through loadNotes
, which is called onPageDidEnter
so every time we come to this page. If you haven’t opened it by now, open the app/pages/home/home.ts and insert:
import {Page, NavController} from 'ionic-angular'; import {NoteService, Note} from '../../providers/note-service/note-service'; import {NoteDetailPage} from '../note-detail/note-detail'; import {Truncate} from '../../pipes/truncate'; @Page({ templateUrl: 'build/pages/home/home.html', pipes: [Truncate] }) export class HomePage { notes: Note[]; constructor(public nav: NavController, public noteService: NoteService) {} // Initialise the notes by loading data from our DB private loadNotes() { this.notes = []; this.noteService.getNotes().then( data => { this.notes = []; if (data.res.rows.length > 0) { for (var i = 0; i < data.res.rows.length; i++) { let item = data.res.rows.item(i); this.notes.push(new Note(item.title, item.text, item.id)); } } }); } // Push the details page bute without an existing note public addNote() { this.nav.push(NoteDetailPage); } // Push the details page for our selected Note public noteSelected(item: Note) { this.nav.push(NoteDetailPage, {'note': item}); } // Remove the note from the DB and our current arra public removeNote(note: Note) { this.noteService.removeNote(note); let index = this.notes.indexOf(note); if (index > -1) { this.notes.splice(index, 1); } } // Load our todos once the page appears private onPageDidEnter() { this.loadNotes(); } }
As you can see we load our notes through our service and create a new Note object for each entry we get back from the database. If we want to add a note, we simply push our DetailsPage which will then generate a blank new note.
But if we select a note, we push the DetailsPage and also pass the selected note as a parameter, so the page can then handle the rest for us.
Finally, deleting a note also happens through the function of our noteService, but we also want to actually remove the element from our current array of notes!
The according view to this class simply holds a list with all of our elements. On the top navbar we also have a plus button to create a new note, and every note entry has a sliding option button which can be revealed to remove a note.
The note element consists of the title and the text attribute of a Note object, and because the text can become a bit long we use our pipe here to limit the length of the string to 20. For now insert all of this in your app/pages/home/home.html:
<ion-navbar *navbar primary> <ion-title> My Notes </ion-title> <ion-buttons end> <button (click)="addNote()"> <ion-icon name="add"></ion-icon> </button> </ion-buttons> </ion-navbar> <ion-content class="home"> <ion-list> <ion-item-sliding *ngFor="#item of notes" > <button ion-item (click)="noteSelected(item)"> <ion-item-content> <h2>{{item.title}}</h2> <p>{{item.text | truncate: 20}}</p> </ion-item-content> </button> <ion-item-options> <button danger (click)="removeNote(item)"><ion-icon name="trash"></ion-icon> Delete</button> </ion-item-options> </ion-item-sliding> </ion-list> </ion-content>
I think the view is pretty straight forward, we only need to get used to the new Angular 2 syntax.
The pipe we talked about all the time is also pretty easy to implement, we only have to code a transform
function inside that class, so open app/pipes/truncate.ts and insert:
import {Pipe} from 'angular2/core'; @Pipe({ name: 'truncate' }) export class Truncate { transform(value: string, args: string[]) : string { let limit = parseInt(args[0]); return value.length > limit ? value.substring(0, limit) + '...' : value; } }
As said before, we cut the string if it’s longer than the specified length (in our case we passed 20).
Many of these things might be new to you, but if you work with it for some days it will become your new standard (and you will get problems going back to Angular 1, have fun!).
So this is the first page, now we also need the DetailsPage to insert the actual note!
The detail view for Notes
app/pages/note-detail/note-detail.ts
We are almost finished, stay with me! This is the class now that get’s pushed onto our navigation stack with either a selected note as an argument or nothing.
Therefore, inside the constructor we try to get this Note from the navParams
and set it as the current note. If we don’t get a note, we actually create an empty note right there and store this to our database.
Why?
This will create the ID for the note, so we have already a database entry which is empty but has a unique ID. This ID is used later when we update our note.
We will also always save our current note onPageWillUnload
and in that cases show a little Toast message to our users, like in the image further down this side.
Now go ahead and open the app/pages/note-detail/note-detail.ts and insert:
import {Page, NavController, NavParams, Toast} from 'ionic-angular'; import {NoteService, Note} from '../../providers/note-service/note-service'; @Page({ templateUrl: 'build/pages/note-detail/note-detail.html' }) export class NoteDetailPage { note: Note = null; constructor(public nav: NavController, navParams: NavParams, public noteService: NoteService) { let passedNote = navParams.get('note'); // Try to initialise our note for the page if (passedNote !== undefined) { this.note = passedNote; } else { this.note = new Note('', '', null); this.saveNote(); } } // Save our note to the DB and show a message (optional) public saveNote(showBadge: boolean = false) { if (this.note.id === null) { this.noteService.saveNote(this.note).then((data) => { // Set the automatic created id to our note this.note.id = data.res["insertId"]; }); } else { this.noteService.updateNote(this.note); } if (showBadge) { let toast = Toast.create({ message: 'Note saved', duration: 3000 }); this.nav.present(toast); } } // Called when this page is popped from the nav stack private onPageWillUnload() { this.saveNote(true); } }
If you have any questions about this procedure, just let me know in the comments!
Finally, the view for this class again is pretty simple. We have our navbar at the top, and we have an input field for the title and also a textarea for our actual note.
If you want to use this in production, you need to have some automatic resizing of the textarea which is possible, but I won’t cover it here for now.
Open the app/pages/note-detail/note-detail.html and insert:
<ion-navbar *navbar primary> <ion-title> {{note.title}} </ion-title> </ion-navbar> <ion-content class="details"> <ion-list> <ion-item> <ion-input type="text" value="" placeholder="My awesome note" [(ngModel)]="note.title"></ion-input> </ion-item> </ion-list> <textarea rows="15" placeholder="This will be awesome..." style="width: 100%; border: none;" [(ngModel)]="note.text"> </textarea> </ion-content>
That’s it! You can now run the app in your browser or device and should see an app like in the image below.
Conclusion
Get a kickstart to your Ionic 2 with my course Ionic 2 in One Hour!
It’s recommended to not use the localStorage with Angular 2 if you want to make sure your data is safe inside the app, so using the Ionic 2 SqlStorage is a pretty easy alternative. With some simple SQL statements you can get the complete power of SQL right inside your Hybrid app!
For a video version of this article, check out the screencast below!
Happy coding,
Simon
The post Using Ionic 2 SqlStorage For a Simple Evernote Clone appeared first on Devdactic.