- About
An ionic 6 , web application built in typescript and react js framework ,while in terms of the backend back 4app was used as the api .
Start By
yarn installADD Api keys from parse dashboard back4app
- Select your application or create a new one
1.1 β Make ACLS public . Note this is not recommended for deployment only development
- Go to App settings on the left
- Select security and keys and get the api keys
REACT_APP_PARSE_ID=
REACT_APP_PARSE_HOST_URL=
REACT_APP_PARSE_JS_KEY=- After these simple steps Serve application and Enjoy !
Start By π
ionic serve- Project Built With
Project Requirements
Make sure you installed node and node package manager using npm -v and node -v
- Install yarn by using
npm install -g yarn
npm i -g @ionic/cli
Setup the project
ionic start todoApp --type=react --capacitorβ use yarn instead of npm
ionic config set -g yarn true
Packages to install
parse From parse β yarn pkg
@parse/react From @parse/react β yarn pkg
& Getting started with the Parse React hook for real time updates using Parse
yarn add @parse/react parseProject Structure and files to add
- public
- /assets // images
- /icons // favicon.ico for example
- index.html // the html rendered webpage
- src //root folder
- /components // where all the components reside
- /CreateToDo //1. create new folder inside ./src/components/ call it CreateToDo
- /CreateToDo.tsx //2. create new file inside /src/components/CreateToDo call it CreateToDo.tsx
- /pages //where all pages reside
- /EditToDo //3.create new folder inside ./src/pages/ call it EditToDo
- /EditToDo.tsx //4. create new file inside ./src/pages/EditToDo call it EditToDo.tsx
- /theme // Where ionic app.css styles reside
- /variables.css // ionic default css variables for dark or light mode
- App.tsx // Where the application component resides, the ionic router and also initializeParse Client
- index.tsx // Where the application renders in the index.html
- .env // Where all the Api Keys are going to be saftely stored for production
- CREATE [x]
// ADD IMPORTS
import React, { useState, useEffect } from "react";
import {
IonCol,
IonLabel,
IonInput,
IonTextarea,
IonButton,
IonIcon,
IonGrid,
IonRow,
IonItem,
IonText,
} from "@ionic/react";
import { add,paperPlaneOutline } from "ionicons/icons";
const Parse = require("parse");
// Export a default function
export default function CreateToDo() {
return (
<>>
)
} //ADD STATE VAR AND STATE ACTION AND ASSIGN PROPERTIES
const [newToDoObject, setNewToDoObject] = useState({
title: "",
description: "",
task: "",
isCompleted: false,
createdAt: new Date(),
updatedAt: new Date(),
});//ADD async arrow function to handle creating the new to object{}
const createNewToDoObject = async () => {
const newToDo = new Parse.Object("ToDo", newToDoObject);
newToDo.set(newToDoObject);
try {
const newToDoObject = await newToDo.save();
const newToDoObjJSON = JSON.stringify(newToDoObject);
alert("The New To Do Object Has been Created >>>>! " + newToDoObjJSON);
} catch (error: any) {
alert("Errro was found in createNewToDoObject " + error);
}
};
//Hanlde ToDoChg
const handleToDoCHG= (event: any)=> {
setNewToDoObject((previous : any)=> ({
...previous,
[event.target.name]: event.target.value,
}));
//html5
};- Make sure to match the html5 property name with the properties passed to the object
- Also add
onIonChange={handleToDoCHG}in each input to handle the users input
//ADD html5 name property and handleToDoCHG to handle the user inputs change
Create ToDo
Final File CreateToDo.tsx
//CreateToDo.tsx
import React, { useState, useEffect } from "react";
import {
IonCol,
IonLabel,
IonInput,
IonTextarea,
IonButton,
IonIcon,
IonGrid,
IonRow,
IonItem,
IonText,
} from "@ionic/react";
import { add, paperPlaneOutline } from "ionicons/icons";
const Parse = require("parse");
export default function CreateToDo() {
//STATE VAR AND STATE ACTION AND ASSIGN PROPERTIES
const [newToDoObject, setNewToDoObject] = useState({
title: "",
description: "",
task: "",
isCompleted: false,
createdAt: new Date(),
updatedAt: new Date(),
});
const createNewToDoObject = async () => {
const newToDo = new Parse.Object("ToDo", newToDoObject);
newToDo.set(newToDoObject);
try {
const newToDoObject = await newToDo.save();
const newToDoObjJSON = JSON.stringify(newToDoObject);
alert("The New To Do Object Has been Created >>>>! " + newToDoObjJSON);
} catch (error: any) {
alert("Errro was found in createNewToDoObject " + error);
}
};
//Hanlde ToDoChg
const handleToDoCHG = (event: any) => {
setNewToDoObject((previous: any) => ({
...previous,
[event.target.name]: event.target.value,
}));
//html5
};
return (
<>
Create ToDo
Title
Task
Description
{" "}
>
);
}- READ [x]
- For this part you can assign a new component in
./src/component/EditToDo/EdiToDo.tsx
//2-A. SET STATE VAR And SetStateAction
var [toDos, setToDos] = useState([
{
objectId: " ",
title: "",
description: "",
task: "",
isCompleted: Boolean(),
createdAt: new Date(),
updatedAt: new Date(),
},
]);
//2-B. extending the Parse object
const ToDo: Parse.Object[] = Parse.Object.extend("ToDo"); // extend todo
const parsequery: Parse.Query = new Parse.Query(ToDo);
//2-C. ASYNC Function to handle reading tasks with useCallback hook to handle each task instead of going in an infinte loop
const readTasks = useCallback(async function (): Promise
{
try {
const results: Parse.Object[] = await parsequery.find();
const mappedData = [];
for (const object of results) {
const objId: string = object.id;
const title: string = object.get("title");
const decription: string = object.get("description");
const task: string = object.get("task");
const isCompleted: boolean = object.get("isCompleted");
const createdAt: Date = object.get("createdAt");
const updatedAt: Date = object.get("updatedAt");
let resultsFix = {
objectId: objId, //string
title: title, //string
description: decription,
task: task,
isCompleted: isCompleted, //boolean
createdAt: createdAt, //date
updatedAt: updatedAt, //date
};
mappedData.push(resultsFix);
}
setToDos(mappedData);
return true;
} catch (error: any) {
console.warn("Error has been found in readTasks " + error);
return false;
}
}, []);
console.log(toDos); // 2-D. useEffect
useEffect(() => {
readTasks();
//uncomment these lines after addint the refreshTasks async arrow function
//refreshTasks();
}, [readTasks, /*refreshTasks*/]);- UPDATE [X]
//UPDATE TODO
const completeTask = async () => {
try {
const object = await parsequery.get(objId);
object.set("isCompleted", true);
object.set("objectId", objId);
object.save();
} catch (error: any) {
console.warn("Error has been found in completeTask" + error);
}
};
- DELETE [X]
//DELETE TODO
const deleteToDo = async () => {
try {
const singleObject: Parse.Object = await parsequery.get(objId);
const response: any = await singleObject.destroy();
if (response) {
alert(`${objId} To Do Has Been Deleted`);
} else {
alert(`Error: Nothing was Delted`);
}
return true;
} catch (error: any) {
console.warn("Error has been found in deleteToDo" + error);
}
};
- Refresh Tasks
/*-------------< TODO REFRESH TASKS START >---------*/
const refreshTasks = useCallback(
async function () {
var query = new Parse.Query("ToDo");
query
.find()
.then((results: Parse.Object) => {
//DEBUG
//Stringified Value of Results
//const resultsStr = JSON.stringify(results);
//console.log("Results of ToDo parse Object is >>>" + resultsStr);
//
})
.then(() => {
query.count().then((ToDoCount: Number) => {
console.log("Number of tasks is = " + ToDoCount);
});
})
.catch((error: any) => {
// error is an instance of parse.error.
console.log(error);
});
//REFRESH TASKS TO REMOVE THE DELETED ONES ID
readTasks();
return true;
},
[readTasks]
);
/*-------------< TODO REFRESH TASKS END >---------*/
Final File in ./src/components/EditToDo/EditToDo.tsx
import React from "react";
import {
IonButton,
IonCard,
IonCardContent,
IonCardHeader,
IonCardSubtitle,
IonCol,
IonIcon,
IonItem,
IonText,
IonCheckbox,
IonBadge,
IonRippleEffect,
IonRow,
IonGrid,
} from "@ionic/react";
import { close, returnDownBack } from "ionicons/icons";
import { FC, ReactElement, useCallback, useEffect, useState } from "react";
const Parse = require("parse");
const EditToDo: FC<{}> = (): ReactElement => {
//1. STATE VAR And SetStateAction
var [toDos, setToDos] = useState([
{
objectId: " ",
title: "",
description: "",
task: "",
isCompleted: Boolean(),
createdAt: new Date(),
updatedAt: new Date(),
},
]);
// extending the Parse object
const ToDo: Parse.Object[] = Parse.Object.extend("ToDo"); // extend todo
const parsequery: Parse.Query = new Parse.Query(ToDo);
//2. ASYNC Function to handle reading tasks with useCallback hook to handle each task instead of going in an infinte loop
const readTasks = useCallback(async function (): Promise
{
try {
const results: Parse.Object[] = await parsequery.find();
const mappedData = [];
for (const object of results) {
const objId: string = object.id;
const title: string = object.get("title");
const decription: string = object.get("description");
const task: string = object.get("task");
const isCompleted: boolean = object.get("isCompleted");
const createdAt: Date = object.get("createdAt");
const updatedAt: Date = object.get("updatedAt");
let resultsFix = {
objectId: objId, //string
title: title, //string
description: decription,
task: task,
isCompleted: isCompleted, //boolean
createdAt: createdAt, //date
updatedAt: updatedAt, //date
};
mappedData.push(resultsFix);
}
setToDos(mappedData);
return true;
} catch (error: any) {
console.warn("Error has been found in readTasks " + error);
return false;
}
}, []);
console.log(toDos);
/*-------------< TODO REFRESH TASKS START >---------*/
const refreshTasks = useCallback(
async function () {
var query = new Parse.Query("ToDo");
query
.find()
.then((results: Parse.Object) => {
//DEBUG
//Stringified Value of Results
//const resultsStr = JSON.stringify(results);
//console.log("Results of ToDo parse Object is >>>" + resultsStr);
//
})
.then(() => {
query.count().then((ToDoCount: Number) => {
console.log("Number of tasks is = " + ToDoCount);
});
})
.catch((error: any) => {
// error is an instance of parse.error.
console.log(error);
});
//REFRESH TASKS TO REMOVE THE DELETED ONES ID
readTasks();
return true;
},
[readTasks]
);
/*-------------< TODO REFRESH TASKS END >---------*/
// 3. useEffect
useEffect(() => {
readTasks();
refreshTasks();
}, [readTasks, refreshTasks]);
return (
<>
{toDos?.length}
{toDos?.map((todo: any, index: any) => {
// MAP OVER THE TODOS AND RETURN THE INFO
//GET ID
var objId: string = todo?.objectId;
//console.log(objId);
//DELETE TODO
const deleteToDo = async () => {
try {
const singleObject: Parse.Object = await parsequery.get(objId);
const response: any = await singleObject.destroy();
if (response) {
alert(`${objId} To Do Has Been Deleted`);
} else {
alert(`Error: Nothing was Delted`);
}
return true;
} catch (error: any) {
console.warn("Error has been found in deleteToDo" + error);
}
};
//UPDATE TODO
const completeTask = async () => {
try {
const object = await parsequery.get(objId);
object.set("isCompleted", true);
object.set("objectId", objId);
object.save();
} catch (error: any) {
console.warn("Error has been found in completeTask" + error);
}
};
return (
{[todo?.title?.toLocaleUpperCase() || " "]}
{" "}
Task :{[todo?.task?.toLocaleLowerCase() || " "]}
Description
{[todo?.description?.toLocaleLowerCase() || " "]}
Task
Completed
CreatedAt
updatedAt
{todo?.task}
{" "}
{" "}
{todo?.isCompleted.toLocaleString()}
{todo.createdAt?.toDateString()}
{todo.updatedAt?.toDateString()}
);
})}
>
);
};
export default EditToDo;
References
Omar Zeinhom . AKA ANDGOEDU 2022-2023



