Πώς να δημιουργήσετε ένα TodoApp χρησιμοποιώντας ReactJS και Firebase

Γεια σας, καλώς ήλθατε σε αυτό το σεμινάριο. Πριν ξεκινήσουμε θα πρέπει να είστε εξοικειωμένοι με τις βασικές έννοιες του ReactJS. Εάν δεν είστε, θα συνιστούσα να διαβάσετε την τεκμηρίωση του ReactJS.

Θα χρησιμοποιήσουμε τα ακόλουθα στοιχεία σε αυτήν την εφαρμογή:

  1. ReactJS
  2. Υλικό διεπαφής χρήστη
  3. Firebase
  4. ExpressJS
  5. Ταχυδρόμος

Πώς θα φαίνεται η εφαρμογή μας:

Αρχιτεκτονική εφαρμογών:

Κατανόηση των στοιχείων μας:

Ίσως αναρωτιέστε γιατί χρησιμοποιούμε το firebase σε αυτήν την εφαρμογή. Λοιπόν, παρέχει ασφαλή έλεγχο ταυτότητας , βάση δεδομένων σε πραγματικό χρόνο , στοιχείο χωρίς διακομιστή και κάδο αποθήκευσης .

Χρησιμοποιούμε το Express εδώ, ώστε να μην χρειάζεται να χειριστούμε τις εξαιρέσεις HTTP. Θα χρησιμοποιήσουμε όλα τα πακέτα firebase στο στοιχείο λειτουργιών μας. Αυτό συμβαίνει επειδή δεν θέλουμε να κάνουμε την εφαρμογή πελάτη μας πολύ μεγάλη, γεγονός που τείνει να επιβραδύνει τη διαδικασία φόρτωσης της διεπαφής χρήστη.

Σημείωση: Θα χωρίσω αυτό το σεμινάριο σε τέσσερις ξεχωριστές ενότητες. Στην αρχή κάθε ενότητας, θα βρείτε μια δέσμευση git που έχει τον κώδικα που έχει αναπτυχθεί σε αυτήν την ενότητα. Επίσης, εάν θέλετε να δείτε τον πλήρη κώδικα, τότε είναι διαθέσιμος σε αυτό το αποθετήριο.

Ενότητα 1: Ανάπτυξη Todo API

Σε αυτόενότητα , πρόκειται να αναπτύξουμε αυτά τα στοιχεία:

  1. Διαμορφώστε τις λειτουργίες του firebase.
  2. Εγκαταστήστε το Express Express και δημιουργήστε Todo API.
  3. Διαμόρφωση firestore ως βάση δεδομένων.

Ο κώδικας API Todo που εφαρμόζεται σε αυτήν την ενότητα βρίσκεται σε αυτήν τη δέσμευση.

Διαμόρφωση λειτουργιών Firebase:

Μεταβείτε στην κονσόλα Firebase.

Επιλέξτε την επιλογή Προσθήκη έργου . Μετά από αυτό ακολουθήστε το gif κάτω βήμα προς βήμα για να διαμορφώσετε το έργο firebase.

Μεταβείτε στην καρτέλα λειτουργιών και κάντε κλικ στο κουμπί Έναρξη :

Θα δείτε ένα πλαίσιο διαλόγου με οδηγίες σχετικά με τον τρόπο ρύθμισης των λειτουργιών Firebase . Μεταβείτε στο τοπικό σας περιβάλλον. Ανοίξτε ένα εργαλείο γραμμής εντολών. Για να εγκαταστήσετε τα εργαλεία firebase στο μηχάνημά σας χρησιμοποιήστε την παρακάτω εντολή:

 npm install -g firebase-tools

Μόλις γίνει αυτό, χρησιμοποιήστε την εντολή firebase initγια να διαμορφώσετε τις λειτουργίες του firebase στο τοπικό σας περιβάλλον. Ορίστε τις ακόλουθες επιλογές κατά την εκκίνηση της λειτουργίας firebase στο τοπικό περιβάλλον:

  1. Ποιες λειτουργίες Firebase CLI θέλετε να ρυθμίσετε για αυτόν τον φάκελο; Πατήστε το πλήκτρο Space για να επιλέξετε δυνατότητες και, στη συνέχεια, Enter για να επιβεβαιώσετε τις επιλογές σας => Λειτουργίες: Διαμόρφωση και ανάπτυξη Cloud Functions
  2. Αρχικά, ας συνδέσουμε αυτόν τον κατάλογο έργου με ένα έργο Firebase…. => Χρησιμοποιήστε ένα υπάρχον έργο
  3. Επιλέξτε ένα προεπιλεγμένο έργο Firebase για αυτόν τον κατάλογο => όνομα_εφαρμογής
  4. Ποια γλώσσα θα θέλατε να χρησιμοποιήσετε για να γράψετε το Cloud Functions; => JavaScript
  5. Θέλετε να χρησιμοποιήσετε το ESLint για να εντοπίσετε πιθανά σφάλματα και να εφαρμόσετε το στυλ; => Ν
  6. Θέλετε να εγκαταστήσετε εξαρτήσεις με npm τώρα; (Y / n) => Υ

Αφού ολοκληρωθεί η διαμόρφωση, θα λάβετε το ακόλουθο μήνυμα:

✔ Firebase initialization complete!

Αυτή θα είναι η δομή του καταλόγου μας μόλις ολοκληρωθεί η προετοιμασία:

+-- firebase.json +-- functions | +-- index.js | +-- node_modules | +-- package-lock.json | +-- package.json

Τώρα ανοίξτε τον index.jsκατάλογο λειτουργιών και αντιγράψτε-επικολλήστε τον ακόλουθο κώδικα:

const functions = require('firebase-functions'); exports.helloWorld = functions.https.onRequest((request, response) => { response.send("Hello from Firebase!"); });

Αναπτύξτε τον κώδικα στις λειτουργίες firebase χρησιμοποιώντας την ακόλουθη εντολή:

firebase deploy

Μόλις ολοκληρωθεί η ανάπτυξη, θα λάβετε την ακόλουθη γραμμή σύνδεσης στο τέλος της γραμμής εντολών σας:

> ✔ Deploy complete! > Project Console: //console.firebase.google.com/project/todoapp-/overview

Μεταβείτε στο Project Console> Functions και εκεί θα βρείτε τη διεύθυνση URL του API. Η διεύθυνση URL θα έχει την εξής μορφή:

//-todoapp-.cloudfunctions.net/helloWorld

Αντιγράψτε αυτήν τη διεύθυνση URL και επικολλήστε την στο πρόγραμμα περιήγησης. Θα λάβετε την ακόλουθη απάντηση:

Hello from Firebase!

Αυτό επιβεβαιώνει ότι η λειτουργία Firebase έχει ρυθμιστεί σωστά.

Εγκαταστήστε το Express Framework:

Τώρα ας εγκαταστήσουμε το Expressπλαίσιο στο έργο μας χρησιμοποιώντας την ακόλουθη εντολή:

npm i express

Τώρα ας δημιουργήσουμε έναν κατάλογο API μέσα στον κατάλογο λειτουργιών . Μέσα σε αυτόν τον κατάλογο, θα δημιουργήσουμε ένα αρχείο με το όνομα todos.js. Καταργήστε τα πάντα από index.jsκαι, στη συνέχεια, αντιγράψτε-επικολλήστε τον ακόλουθο κώδικα:

//index.js const functions = require('firebase-functions'); const app = require('express')(); const { getAllTodos } = require('./APIs/todos') app.get('/todos', getAllTodos); exports.api = functions.https.onRequest(app);

Έχουμε εκχωρήσει τη συνάρτηση getAllTodos στη διαδρομή / todos . Έτσι, όλες οι κλήσεις API σε αυτήν τη διαδρομή θα εκτελούνται μέσω της λειτουργίας getAllTodos. Τώρα μεταβείτε στο todos.jsαρχείο στον κατάλογο APIs και εδώ θα γράψουμε τη συνάρτηση getAllTodos.

//todos.js exports.getAllTodos = (request, response) => { todos = [ { 'id': '1', 'title': 'greeting', 'body': 'Hello world from sharvin shah' }, { 'id': '2', 'title': 'greeting2', 'body': 'Hello2 world2 from sharvin shah' } ] return response.json(todos); }

Εδώ έχουμε δηλώσει ένα δείγμα αντικειμένου JSON. Αργότερα θα το αντλήσουμε από το Firestore. Αλλά προς το παρόν θα το επιστρέψουμε. Τώρα αναπτύξτε αυτό στη λειτουργία firebase χρησιμοποιώντας την εντολή firebase deploy. Θα ρωτήσειγια άδεια διαγραφής της ενότητας helloworld - απλώς πληκτρολογήστε y .

The following functions are found in your project but do not exist in your local source code: helloWorld Would you like to proceed with deletion? Selecting no will continue the rest of the deployments. (y/N) y

Μόλις γίνει αυτό, μεταβείτε στο Project Console> Functions και εκεί θα βρείτε τη διεύθυνση URL του API. Το API θα μοιάζει με αυτό:

//-todoapp-.cloudfunctions.net/api

Τώρα μεταβείτε στο πρόγραμμα περιήγησης και αντιγράψτε-επικολλήστε τη διεύθυνση URL και προσθέστε / todos στο τέλος αυτής της διεύθυνσης URL. Θα λάβετε την ακόλουθη έξοδο:

[ { 'id': '1', 'title': 'greeting', 'body': 'Hello world from sharvin shah' }, { 'id': '2', 'title': 'greeting2', 'body': 'Hello2 world2 from sharvin shah' } ]

Firebase Firestore:

Θα χρησιμοποιήσουμε ένα firebase firestore ως βάση δεδομένων σε πραγματικό χρόνο για την εφαρμογή μας. Τώρα μεταβείτε στην Κονσόλα> Βάση δεδομένων στο Firebase Console. Για να διαμορφώσετε το firestore ακολουθήστε το παρακάτω gif:

Μόλις η διαμόρφωση γίνεται στη συνέχεια κάντε κλικ στο Έναρξη συλλογής κουμπί και να ρυθμίσετε Collection ID , όπως todos . Κάντε κλικ στο Επόμενο και θα λάβετε το ακόλουθο αναδυόμενο παράθυρο:

Αγνοήστε το κλειδί DocumentID. Για το πεδίο, τον τύπο και την τιμή , ανατρέξτε στο JSON παρακάτω. Ενημερώστε ανάλογα την τιμή:

{ Field: title, Type: String, Value: Hello World }, { Field: body, Type: String, Value: Hello folks I hope you are staying home... }, { Field: createtAt, type: timestamp, value: Add the current date and time here }

Πατήστε το κουμπί αποθήκευσης. Θα δείτε ότι η συλλογή και το έγγραφο έχει δημιουργηθεί. Επιστρέψτε στο τοπικό περιβάλλον. Πρέπει να εγκαταστήσουμε το firebase-adminοποίο έχει το πακέτο firestore που χρειαζόμαστε. Χρησιμοποιήστε αυτήν την εντολή για να την εγκαταστήσετε:

npm i firebase-admin

Δημιουργήστε έναν κατάλογο με όνομα util στον κατάλογο λειτουργιών .Μεταβείτε σε αυτόν τον κατάλογο και δημιουργήστε ένα όνομα αρχείου admin.js. Σε αυτό το αρχείο θα εισαγάγουμε το πακέτο διαχειριστή firebase και θα αρχικοποιήσουμε το αντικείμενο βάσης δεδομένων firestore. Θα το εξάγουμε έτσι ώστε άλλες μονάδες να μπορούν να το χρησιμοποιήσουν.

//admin.js const admin = require('firebase-admin'); admin.initializeApp(); const db = admin.firestore(); module.exports = { admin, db };

Τώρα ας γράψουμε ένα API για τη λήψη αυτών των δεδομένων. Πηγαίνετε στο todos.jsπλαίσιο του λειτουργίες> APIs κατάλογο. Καταργήστε τον παλιό κώδικα και αντιγράψτε-επικολλήστε τον παρακάτω κώδικα:

//todos.js const { db } = require('../util/admin'); exports.getAllTodos = (request, response) => { db .collection('todos') .orderBy('createdAt', 'desc') .get() .then((data) => { let todos = []; data.forEach((doc) => { todos.push({ todoId: doc.id, title: doc.data().title, body: doc.data().body, createdAt: doc.data().createdAt, }); }); return response.json(todos); }) .catch((err) => { console.error(err); return response.status(500).json({ error: err.code}); }); };

Εδώ παίρνουμε όλα τα todos από τη βάση δεδομένων και τα προωθούμε στον πελάτη σε μια λίστα.

Μπορείτε επίσης να εκτελέσετε την εφαρμογή τοπικά χρησιμοποιώντας firebase serveεντολή αντί να την αναπτύξετε κάθε φορά. Όταν εκτελείτε αυτήν την εντολή, ενδέχεται να λάβετε ένα σφάλμα σχετικά με τα διαπιστευτήρια. Για να το διορθώσετε, ακολουθήστε τα παρακάτω βήματα:

  1. Μεταβείτε στις Ρυθμίσεις έργου (εικονίδιο Ρυθμίσεις στην επάνω αριστερή πλευρά)
  2. Μεταβείτε στην καρτέλα λογαριασμών υπηρεσίας  
  3. Κάτω θα υπάρχει η επιλογή Δημιουργίας νέου κλειδιού . Κάντε κλικ σε αυτήν την επιλογή και θα κατεβάσει ένα αρχείο με επέκταση JSON.
  4. Πρέπει να εξάγουμε αυτά τα διαπιστευτήρια στη συνεδρία γραμμής εντολών. Χρησιμοποιήστε την παρακάτω εντολή για να το κάνετε αυτό:
export GOOGLE_APPLICATION_CREDENTIALS="/home/user/Downloads/[FILE_NAME].json"

Μετά από αυτό εκτελέστε την εντολή firebase serve. Εάν εξακολουθείτε να λαμβάνετε το σφάλμα, στη συνέχεια, χρησιμοποιήστε την ακόλουθη εντολή: firebase login --reauth. Θα ανοίξει τη σελίδα σύνδεσης Google σε ένα πρόγραμμα περιήγησης. Μόλις ολοκληρωθεί η σύνδεση, τότε θα λειτουργεί χωρίς κανένα σφάλμα.

Θα βρείτε μια διεύθυνση URL στα αρχεία καταγραφής του εργαλείου γραμμής εντολών όταν εκτελείτε μια εντολή firebase serve. Ανοίξτε αυτήν τη διεύθυνση URL στο πρόγραμμα περιήγησης και επισυνάψτε /todosτην.

✔ functions[api]: http function initialized (//localhost:5000/todoapp-//api).

Θα λάβετε την ακόλουθη έξοδο JSON στο πρόγραμμα περιήγησής σας:

[ { "todoId":"W67t1kSMO0lqvjCIGiuI", "title":"Hello World", "body":"Hello folks I hope you are staying home...", "createdAt":{"_seconds":1585420200,"_nanoseconds":0 } } ]

Σύνταξη άλλων API:

Ήρθε η ώρα να γράψετε όλα τα άλλα API που θα απαιτήσουμε για την εφαρμογή μας.

  1. Δημιουργία αντικειμένου Todo: Μεταβείτε στον index.jsκατάλογο λειτουργιών. Εισαγωγή μεθόδου postOneTodo με το υπάρχον getAllTodos. Επίσης, αντιστοιχίστε τη διαδρομή POST σε αυτήν τη μέθοδο.
//index.js const { .., postOneTodo } = require('./APIs/todos') app.post('/todo', postOneTodo);

Μεταβείτε στον todos.jsκατάλογο λειτουργιών και προσθέστε μια νέα μέθοδο postOneTodoμε την υπάρχουσα getAllTodosμέθοδο.

//todos.js exports.postOneTodo = (request, response) => { if (request.body.body.trim() === '') { return response.status(400).json({ body: 'Must not be empty' }); } if(request.body.title.trim() === '') { return response.status(400).json({ title: 'Must not be empty' }); } const newTodoItem = { title: request.body.title, body: request.body.body, createdAt: new Date().toISOString() } db .collection('todos') .add(newTodoItem) .then((doc)=>{ const responseTodoItem = newTodoItem; responseTodoItem.id = doc.id; return response.json(responseTodoItem); }) .catch((err) => { response.status(500).json({ error: 'Something went wrong' }); console.error(err); }); };

Σε αυτήν τη μέθοδο, προσθέτουμε ένα νέο Todo στη βάση δεδομένων μας. Εάν τα στοιχεία του σώματός μας είναι κενά τότε θα επιστρέψουμε 400 απαντήσεις αλλιώς θα προσθέσουμε τα δεδομένα.

Εκτελέστε την εντολή firebase serve και ανοίξτε την εφαρμογή ταχυδρόμου. Δημιουργήστε ένα νέο αίτημα και επιλέξτε τον τύπο μεθόδου ως POST . Προσθέστε τη διεύθυνση URL και ένα σώμα τύπου JSON.

URL: //localhost:5000/todoapp-//api/todo METHOD: POST Body: { "title":"Hello World", "body": "We are writing this awesome API" }

Πατήστε το κουμπί αποστολής και θα λάβετε την ακόλουθη απάντηση:

{ "title": "Hello World", "body": "We are writing this awesome API", "createdAt": "2020-03-29T12:30:48.809Z", "id": "nh41IgARCj8LPWBYzjU0" }

2. Διαγραφή στοιχείου Todo: Μεταβείτε στον index.jsκατάλογο λειτουργιών. Εισαγάγετε τη μέθοδο deleteTodo στο υπάρχον postOneTodo. Επίσης, αντιστοιχίστε τη διαδρομή ΔΙΑΓΡΑΦΗ σε αυτήν τη μέθοδο.

//index.js const { .., deleteTodo } = require('./APIs/todos') app.delete('/todo/:todoId', deleteTodo);

Μεταβείτε στο todos.jsκαι προσθέστε μια νέα μέθοδο deleteTodoστην υπάρχουσα postOneTodoμέθοδο.

//todos.js exports.deleteTodo = (request, response) => { const document = db.doc(`/todos/${request.params.todoId}`); document .get() .then((doc) => { if (!doc.exists) { return response.status(404).json({ error: 'Todo not found' }) } return document.delete(); }) .then(() => { response.json({ message: 'Delete successfull' }); }) .catch((err) => { console.error(err); return response.status(500).json({ error: err.code }); }); };

Σε αυτήν τη μέθοδο, διαγράφουμε ένα Todo από τη βάση δεδομένων μας. Εκτελέστε την εντολή firebase serve και μεταβείτε στον ταχυδρόμο. Δημιουργήστε ένα νέο αίτημα, επιλέξτε τον τύπο μεθόδου ως ΔΙΑΓΡΑΦΗ και προσθέστε τη διεύθυνση URL.

URL: //localhost:5000/todoapp-//api/todo/ METHOD: DELETE

Πατήστε το κουμπί αποστολής και θα λάβετε την ακόλουθη απάντηση:

{ "message": "Delete successfull" }

3. Επεξεργασία στοιχείου Todo: Μεταβείτε στον index.jsκατάλογο λειτουργιών. Εισαγάγετε τη μέθοδο editTodo κάτω από το υπάρχον deleteTodo. Επίσης, αντιστοιχίστε τη διαδρομή PUT σε αυτήν τη μέθοδο.

//index.js const { .., editTodo } = require('./APIs/todos') app.put('/todo/:todoId', editTodo);

Μεταβείτε στο todos.jsκαι προσθέστε μια νέα μέθοδο editTodoστην υπάρχουσα deleteTodoμέθοδο.

//todos.js exports.editTodo = ( request, response ) => { if(request.body.todoId || request.body.createdAt){ response.status(403).json({message: 'Not allowed to edit'}); } let document = db.collection('todos').doc(`${request.params.todoId}`); document.update(request.body) .then(()=> { response.json({message: 'Updated successfully'}); }) .catch((err) => { console.error(err); return response.status(500).json({ error: err.code }); }); };

Σε αυτήν τη μέθοδο, επεξεργαζόμαστε ένα Todo από τη βάση δεδομένων μας. Θυμηθείτε εδώ ότι δεν επιτρέπουμε στον χρήστη να επεξεργάζεται τα πεδία todoId ή createAt. Εκτελέστε την εντολή firebase serve και μεταβείτε στον ταχυδρόμο. Δημιουργήστε ένα νέο αίτημα, επιλέξτε τον τύπο μεθόδου ως PUT και προσθέστε τη διεύθυνση URL.

URL: //localhost:5000/todoapp-//api/todo/ METHOD: PUT

Πατήστε το κουμπί αποστολής και θα λάβετε την ακόλουθη απάντηση:

{ "message": "Updated successfully" }

Δομή καταλόγου έως τώρα:

+-- firebase.json +-- functions | +-- API | +-- +-- todos.js | +-- util | +-- +-- admin.js | +-- index.js | +-- node_modules | +-- package-lock.json | +-- package.json | +-- .gitignore

Με αυτό, ολοκληρώσαμε την πρώτη ενότητα της αίτησης. Μπορείτε να πάρετε λίγο καφέ, να κάνετε ένα διάλειμμα και μετά από αυτό θα εργαστούμε για την ανάπτυξη των API χρήστη.

Ενότητα 2: Ανάπτυξη API χρήστη

Σε αυτόενότητα , πρόκειται να αναπτύξουμε αυτά τα στοιχεία:

  1. API ελέγχου ταυτότητας χρήστη (Σύνδεση και εγγραφή).
  2. GET και ενημέρωση API λεπτομερειών χρήστη.
  3. Ενημερώστε το API εικόνας προφίλ χρήστη.
  4. Εξασφάλιση του υπάρχοντος API Todo.

Ο κωδικός API χρήστη που εφαρμόζεται σε αυτήν την ενότητα βρίσκεται σε αυτήν τη δέσμευση.

Ας αρχίσουμε λοιπόν να δημιουργούμε το API ελέγχου ταυτότητας χρήστη. Μεταβείτε στην κονσόλα Firebase> Έλεγχος ταυτότητας.

Κάντε κλικ στο κουμπί Ρύθμιση μεθόδου σύνδεσης . Θα χρησιμοποιήσουμε email και κωδικό πρόσβασης για επικύρωση χρήστη. Ενεργοποιήστε την επιλογή Email / Κωδικός πρόσβασης .

Αυτήν τη στιγμή θα δημιουργήσουμε με μη αυτόματο τρόπο τον χρήστη μας. Αρχικά, θα δημιουργήσουμε το API σύνδεσης. Μετά από αυτό θα δημιουργήσουμε το Sign-Up API.

Μεταβείτε στην καρτέλα Χρήστες στην ενότητα Έλεγχος ταυτότητας, συμπληρώστε τα στοιχεία χρήστη και κάντε κλικ στο κουμπί Προσθήκη χρήστη .

1. API σύνδεσης χρήστη:

Πρώτον, πρέπει να εγκαταστήσουμε το firebaseπακέτο, το οποίο αποτελείται από τη βιβλιοθήκη ελέγχου ταυτότητας Firebase, χρησιμοποιώντας την ακόλουθη εντολή:

npm i firebase

Μόλις ολοκληρωθεί η εγκατάσταση, μεταβείτε στον κατάλογο λειτουργιών> APIs . Εδώ θα δημιουργήσουμε ένα users.jsαρχείο. Τώρα στο εσωτερικό index.jsεισάγουμε μια μέθοδο loginUser και εκχωρούμε τη διαδρομή POST σε αυτήν.

//index.js const { loginUser } = require('./APIs/users') // Users app.post('/login', loginUser);

Μεταβείτε στις Ρυθμίσεις Έργου> Γενικά και εκεί θα βρείτε την ακόλουθη κάρτα:

Επιλέξτε το εικονίδιο Web και ακολουθήστε το παρακάτω gif:

Ορίστε την επιλογή συνέχισης στην κονσόλα . Μόλις γίνει αυτό, θα δείτε ένα JSON με διαμόρφωση firebase. Μεταβείτε στον κατάλογο λειτουργιών> util και δημιουργήστε ένα   config.jsαρχείο. Αντιγράψτε-επικολλήστε τον ακόλουθο κώδικα σε αυτό το αρχείο:

// config.js module.exports = { apiKey: "............", authDomain: "........", databaseURL: "........", projectId: ".......", storageBucket: ".......", messagingSenderId: "........", appId: "..........", measurementId: "......." };

Αντικαταστήστε ............με τις τιμές που λαμβάνετε στην Firebase console> Ρυθμίσεις έργου> Γενικά> οι εφαρμογές σας> Firebase SD snippet> config .

Αντιγράψτε-επικολλήστε τον ακόλουθο κώδικα στο users.jsαρχείο:

// users.js const { admin, db } = require('../util/admin'); const config = require('../util/config'); const firebase = require('firebase'); firebase.initializeApp(config); const { validateLoginData, validateSignUpData } = require('../util/validators'); // Login exports.loginUser = (request, response) => { const user = { email: request.body.email, password: request.body.password } const { valid, errors } = validateLoginData(user); if (!valid) return response.status(400).json(errors); firebase .auth() .signInWithEmailAndPassword(user.email, user.password) .then((data) => { return data.user.getIdToken(); }) .then((token) => { return response.json({ token }); }) .catch((error) => { console.error(error); return response.status(403).json({ general: 'wrong credentials, please try again'}); }) };

Εδώ χρησιμοποιούμε μια μονάδα firebase signInWithEmailAndPassword για να ελέγξουμε εάν τα διαπιστευτήρια που έχουν υποβληθεί από τον χρήστη είναι σωστά. Εάν έχουν δίκιο, στέλνουμε το διακριτικό αυτού του χρήστη ή αλλιώς μια κατάσταση 403 με μήνυμα "λάθος διαπιστευτήρια"

Τώρα ας δημιουργήσουμε validators.jsκάτω από τον κατάλογο λειτουργιών> util . Αντιγράψτε-επικολλήστε τον ακόλουθο κώδικα σε αυτό το αρχείο:

// validators.js const isEmpty = (string) => { if (string.trim() === '') return true; else return false; }; exports.validateLoginData = (data) => { let errors = {}; if (isEmpty(data.email)) errors.email = 'Must not be empty'; if (isEmpty(data.password)) errors.password = 'Must not be empty'; return { errors, valid: Object.keys(errors).length === 0 ? true : false }; };

Με αυτό ολοκληρώνεται το LoginAPI . Εκτελέστε την firebase serveεντολή και μεταβείτε στον ταχυδρόμο. Δημιουργήστε ένα νέο αίτημα, επιλέξτε τον τύπο μεθόδου ως POST και προσθέστε τη διεύθυνση URL και το σώμα.

URL: //localhost:5000/todoapp-//api/login METHOD: POST Body: { "email":"Add email that is assigned for user in console", "password": "Add password that is assigned for user in console" }

Πατήστε το κουμπί αποστολής αιτήματος στον ταχυδρόμο και θα λάβετε την ακόλουθη έξοδο:

{ "token": ".........." }

Θα χρησιμοποιήσουμε αυτό το διακριτικό σε ένα προσεχές μέρος για να λάβουμε τις λεπτομέρειες του χρήστη . Θυμηθείτε ότι αυτό το διακριτικό λήγει σε 60 λεπτά . Για να δημιουργήσετε ένα νέο διακριτικό χρησιμοποιήστε ξανά αυτό το API.

2. API εγγραφής χρήστη:

Ο προεπιλεγμένος μηχανισμός ελέγχου ταυτότητας του firebase σάς επιτρέπει μόνο να αποθηκεύετε πληροφορίες όπως email, κωδικό πρόσβασης, κ.λπ.

Για την επίτευξη αυτού του στόχου πρόκειται να δημιουργήσουμε μια νέα συλλογή που ονομάζεται χρήστες . Κάτω από αυτήν τη συλλογή, θα αποθηκεύσουμε τα δεδομένα του χρήστη που θα αντιστοιχιστούν στο todo με βάση το όνομα χρήστη. Κάθε όνομα χρήστη θα είναι μοναδικό για όλους τους χρήστες της πλατφόρμας.

Πηγαίνετε στο index.js. Εισάγουμε μια μέθοδο signUpUser και εκχωρούμε τη διαδρομή POST σε αυτήν.

//index.js const { .., signUpUser } = require('./APIs/users') app.post('/signup', signUpUser);

Τώρα μεταβείτε στο validators.jsκαι προσθέστε τον ακόλουθο κώδικα κάτω από τη validateLoginDataμέθοδο.

// validators.js const isEmail = (email) => { const emailRegEx = /^(([^()\[\]\\.,;:\[email protected]"]+(\.[^()\[\]\\.,;:\[email protected]"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; if (email.match(emailRegEx)) return true; else return false; }; exports.validateSignUpData = (data) => { let errors = {}; if (isEmpty(data.email)) { errors.email = 'Must not be empty'; } else if (!isEmail(data.email)) { errors.email = 'Must be valid email address'; } if (isEmpty(data.firstName)) errors.firstName = 'Must not be empty'; if (isEmpty(data.lastName)) errors.lastName = 'Must not be empty'; if (isEmpty(data.phoneNumber)) errors.phoneNumber = 'Must not be empty'; if (isEmpty(data.country)) errors.country = 'Must not be empty'; if (isEmpty(data.password)) errors.password = 'Must not be empty'; if (data.password !== data.confirmPassword) errors.confirmPassword = 'Passowrds must be the same'; if (isEmpty(data.username)) errors.username = 'Must not be empty'; return { errors, valid: Object.keys(errors).length === 0 ? true : false }; };

Τώρα μεταβείτε στο users.jsκαι προσθέστε τον ακόλουθο κώδικα κάτω από τη loginUserλειτουργική μονάδα.

// users.js exports.signUpUser = (request, response) => { const newUser = { firstName: request.body.firstName, lastName: request.body.lastName, email: request.body.email, phoneNumber: request.body.phoneNumber, country: request.body.country, password: request.body.password, confirmPassword: request.body.confirmPassword, username: request.body.username }; const { valid, errors } = validateSignUpData(newUser); if (!valid) return response.status(400).json(errors); let token, userId; db .doc(`/users/${newUser.username}`) .get() .then((doc) => { if (doc.exists) { return response.status(400).json({ username: 'this username is already taken' }); } else { return firebase .auth() .createUserWithEmailAndPassword( newUser.email, newUser.password ); } }) .then((data) => { userId = data.user.uid; return data.user.getIdToken(); }) .then((idtoken) => { token = idtoken; const userCredentials = { firstName: newUser.firstName, lastName: newUser.lastName, username: newUser.username, phoneNumber: newUser.phoneNumber, country: newUser.country, email: newUser.email, createdAt: new Date().toISOString(), userId }; return db .doc(`/users/${newUser.username}`) .set(userCredentials); }) .then(()=>{ return response.status(201).json({ token }); }) .catch((err) => { console.error(err); if (err.code === 'auth/email-already-in-use') { return response.status(400).json({ email: 'Email already in use' }); } else { return response.status(500).json({ general: 'Something went wrong, please try again' }); } }); }

Επικυρώνουμε τα δεδομένα των χρηστών μας και, στη συνέχεια, στέλνουμε ένα email και έναν κωδικό πρόσβασης στη μονάδα firebase createUserWithEmailAndPassword για τη δημιουργία του χρήστη. Μόλις ο χρήστης δημιουργηθεί με επιτυχία, αποθηκεύουμε τα διαπιστευτήρια χρήστη στη βάση δεδομένων.

Με αυτό ολοκληρώνεται το API SignUp . Εκτελέστε την firebase serveεντολή και μεταβείτε στον ταχυδρόμο. Δημιουργήστε ένα νέο αίτημα, επιλέξτε τον τύπο μεθόδου ως POST . Προσθέστε τη διεύθυνση URL και το σώμα.

URL: //localhost:5000/todoapp-//api/signup METHOD: POST Body: { "firstName": "Add a firstName here", "lastName": "Add a lastName here", "email":"Add a email here", "phoneNumber": "Add a phone number here", "country": "Add a country here", "password": "Add a password here", "confirmPassword": "Add same password here", "username": "Add unique username here" }

Πατήστε το κουμπί αποστολής αιτήματος στον ταχυδρόμο και θα λάβετε την ακόλουθη έξοδο:

{ "token": ".........." }

Τώρα μεταβείτε στην κονσόλα Firebase> Βάση δεδομένων και εκεί θα δείτε την ακόλουθη έξοδο:

Όπως μπορείτε να δείτε, η συλλογή των χρηστών μας δημιουργήθηκε με επιτυχία με ένα έγγραφο σε αυτό.

3. Ανεβάστε την εικόνα προφίλ χρήστη:

Οι χρήστες μας θα μπορούν να ανεβάζουν την εικόνα προφίλ τους. Για να το επιτύχουμε αυτό θα χρησιμοποιούμε κάδο αποθήκευσης. Μεταβείτε στην κονσόλα Firebase> Storage και κάντε κλικ στο κουμπί Έναρξη . Ακολουθήστε το παρακάτω GIF για τη διαμόρφωση:

Τώρα μεταβείτε στην καρτέλα Κανόνες στην ενότητα Αποθήκευση και ενημερώστε το δικαίωμα πρόσβασης στο κάδο σύμφωνα με την παρακάτω εικόνα:

Για να ανεβάσετε την εικόνα προφίλ θα χρησιμοποιήσουμε το πακέτο με το όνομα busboy. Για να εγκαταστήσετε αυτό το πακέτο, χρησιμοποιήστε την ακόλουθη εντολή:

npm i busboy

Πηγαίνετε στο index.js. Εισαγάγετε τη μέθοδο uploadProfilePhoto κάτω από την υπάρχουσα μέθοδο signUpUser. Εκχωρήστε επίσης τη διαδρομή POST σε αυτήν τη μέθοδο.

//index.js const auth = require('./util/auth'); const { .., uploadProfilePhoto } = require('./APIs/users') app.post('/user/image', auth, uploadProfilePhoto);

Εδώ έχουμε προσθέσει ένα επίπεδο ελέγχου ταυτότητας, ώστε μόνο ένας χρήστης που σχετίζεται με αυτόν τον λογαριασμό να μπορεί να ανεβάσει την εικόνα. Τώρα δημιουργήστε ένα αρχείο που ονομάζεται auth.jsστον κατάλογο λειτουργιών> utils . Αντιγράψτε-επικολλήστε τον ακόλουθο κώδικα σε αυτό το αρχείο:

// auth.js const { admin, db } = require('./admin'); module.exports = (request, response, next) => { let idToken; if (request.headers.authorization && request.headers.authorization.startsWith('Bearer ')) { idToken = request.headers.authorization.split('Bearer ')[1]; } else { console.error('No token found'); return response.status(403).json({ error: 'Unauthorized' }); } admin .auth() .verifyIdToken(idToken) .then((decodedToken) => { request.user = decodedToken; return db.collection('users').where('userId', '==', request.user.uid).limit(1).get(); }) .then((data) => { request.user.username = data.docs[0].data().username; request.user.imageUrl = data.docs[0].data().imageUrl; return next(); }) .catch((err) => { console.error('Error while verifying token', err); return response.status(403).json(err); }); };

Εδώ χρησιμοποιούμε τη λειτουργική μονάδα firebase verifikasiIdToken για την επαλήθευση του διακριτικού. Μετά από αυτό αποκωδικοποιούμε τα στοιχεία χρήστη και τα διαβιβάζουμε στο υπάρχον αίτημα.

Μεταβείτε στο users.jsκαι προσθέστε τον ακόλουθο κώδικα κάτω από τη signupμέθοδο:

// users.js deleteImage = (imageName) => { const bucket = admin.storage().bucket(); const path = `${imageName}` return bucket.file(path).delete() .then(() => { return }) .catch((error) => { return }) } // Upload profile picture exports.uploadProfilePhoto = (request, response) => { const BusBoy = require('busboy'); const path = require('path'); const os = require('os'); const fs = require('fs'); const busboy = new BusBoy({ headers: request.headers }); let imageFileName; let imageToBeUploaded = {}; busboy.on('file', (fieldname, file, filename, encoding, mimetype) => { if (mimetype !== 'image/png' && mimetype !== 'image/jpeg') { return response.status(400).json({ error: 'Wrong file type submited' }); } const imageExtension = filename.split('.')[filename.split('.').length - 1]; imageFileName = `${request.user.username}.${imageExtension}`; const filePath = path.join(os.tmpdir(), imageFileName); imageToBeUploaded = { filePath, mimetype }; file.pipe(fs.createWriteStream(filePath)); }); deleteImage(imageFileName); busboy.on('finish', () => { admin .storage() .bucket() .upload(imageToBeUploaded.filePath, { resumable: false, metadata: { metadata: { contentType: imageToBeUploaded.mimetype } } }) .then(() => { const imageUrl = `//firebasestorage.googleapis.com/v0/b/${config.storageBucket}/o/${imageFileName}?alt=media`; return db.doc(`/users/${request.user.username}`).update({ imageUrl }); }) .then(() => { return response.json({ message: 'Image uploaded successfully' }); }) .catch((error) => { console.error(error); return response.status(500).json({ error: error.code }); }); }); busboy.end(request.rawBody); };

Με αυτό ολοκληρώνεται το API εικόνων προφίλ μεταφόρτωσης . Εκτελέστε την firebase serveεντολή και μεταβείτε στον ταχυδρόμο. Δημιουργήστε ένα νέο αίτημα, επιλέξτε τον τύπο μεθόδου ως POST , προσθέστε τη διεύθυνση URL και στην κύρια ενότητα επιλέξτε τύπος ως μορφή φόρμας.

Το αίτημα προστατεύεται, οπότε θα πρέπει να στείλετε και το διακριτικό του κομιστή . Για να στείλετε το διακριτικό κομιστή, συνδεθείτε ξανά εάν το διακριτικό έχει λήξει. Μετά από αυτό στην εφαρμογή Postman> καρτέλα Εξουσιοδότηση> Τύπος> Διακριτικό Bearer και στην ενότητα διακριτικών επικολλήστε το διακριτικό.

URL: //localhost:5000/todoapp-//api/user/image METHOD: GET Body: { REFER THE IMAGE down below }

Πατήστε το κουμπί αποστολής αιτήματος στον ταχυδρόμο και θα λάβετε την ακόλουθη έξοδο:

{ "message": "Image uploaded successfully" }

4. Λάβετε στοιχεία χρήστη:

Εδώ παίρνουμε τα δεδομένα του χρήστη μας από τη βάση δεδομένων. Μεταβείτε στο index.jsκαι εισαγάγετε τη μέθοδο getUserDetail και εκχωρήστε τη διαδρομή GET σε αυτήν.

// index.js const { .., getUserDetail } = require('./APIs/users') app.get('/user', auth, getUserDetail);

Τώρα μεταβείτε στο users.jsκαι προσθέστε τον ακόλουθο κώδικα μετά τη uploadProfilePhotoλειτουργική μονάδα:

// users.js exports.getUserDetail = (request, response) => { let userData = {}; db .doc(`/users/${request.user.username}`) .get() .then((doc) => { if (doc.exists) { userData.userCredentials = doc.data(); return response.json(userData); } }) .catch((error) => { console.error(error); return response.status(500).json({ error: error.code }); }); }

Χρησιμοποιούμε τη μονάδα firebase doc (). Get () για να αντλήσουμε τα στοιχεία του χρήστη. Με αυτό ολοκληρώνεται το API λεπτομερειών χρήστη GET . Εκτελέστε την firebase serveεντολή και μεταβείτε στον ταχυδρόμο. Δημιουργήστε ένα νέο αίτημα, επιλέξτε τον τύπο μεθόδου: GET και προσθέστε τη διεύθυνση URL και το σώμα.

Το αίτημα προστατεύεται, οπότε θα πρέπει να στείλετε και το διακριτικό του κομιστή . Για να στείλετε το διακριτικό κομιστή, συνδεθείτε ξανά εάν το διακριτικό έχει λήξει.

URL: //localhost:5000/todoapp-//api/user METHOD: GET

Πατήστε το κουμπί αποστολής αιτήματος στον ταχυδρόμο και θα λάβετε την ακόλουθη έξοδο:

{ "userCredentials": { "phoneNumber": "........", "email": "........", "country": "........", "userId": "........", "username": "........", "createdAt": "........", "lastName": "........", "firstName": "........" } }

5. Ενημέρωση στοιχείων χρήστη:

Τώρα ας προσθέσουμε τη λειτουργικότητα για να ενημερώσουμε τα στοιχεία του χρήστη. Μεταβείτε στο index.jsκαι αντιγράψτε-επικολλήστε τον ακόλουθο κώδικα:

// index.js const { .., updateUserDetails } = require('./APIs/users') app.post('/user', auth, updateUserDetails);

Τώρα μεταβείτε στο users.jsκαι προσθέστε την updateUserDetailsενότητα κάτω από το υπάρχον getUserDetails:

// users.js exports.updateUserDetails = (request, response) => { let document = db.collection('users').doc(`${request.user.username}`); document.update(request.body) .then(()=> { response.json({message: 'Updated successfully'}); }) .catch((error) => { console.error(error); return response.status(500).json({ message: "Cannot Update the value" }); }); }

Εδώ χρησιμοποιούμε τη μέθοδο ενημέρωσης firebase . Με αυτό ολοκληρώνεται το API Ενημέρωσης λεπτομερειών χρήστη . Ακολουθήστε την ίδια διαδικασία για ένα αίτημα όπως με το API Λήψη λεπτομερειών χρήστη παραπάνω με μία αλλαγή. Προσθέστε το σώμα στο αίτημα εδώ και μέθοδο ως POST.

URL: //localhost:5000/todoapp-//api/user METHOD: POST Body : { // You can edit First Name, last Name and country // We will disable other Form Tags from our UI }

Πατήστε το κουμπί αποστολής αιτήματος στον ταχυδρόμο και θα λάβετε την ακόλουθη έξοδο:

{ "message": "Updated successfully" }

6. Διασφάλιση API Todo:

Για να ασφαλίσουμε το API Todo, ώστε να έχει πρόσβαση μόνο ο επιλεγμένος χρήστης, θα κάνουμε μερικές αλλαγές στον υπάρχοντα κώδικα. Πρώτον, θα ενημερώσουμε τα index.jsεξής ως εξής:

// index.js // Todos app.get('/todos', auth, getAllTodos); app.get('/todo/:todoId', auth, getOneTodo); app.post('/todo',auth, postOneTodo); app.delete('/todo/:todoId',auth, deleteTodo); app.put('/todo/:todoId',auth, editTodo);

Έχουμε ενημερώσει όλες τις διαδρομές Todo προσθέτοντας authέτσι ώστε όλες οι κλήσεις API να απαιτούν ένα διακριτικό και να έχουν πρόσβαση μόνο από τον συγκεκριμένο χρήστη.

Μετά που πηγαίνουν στο todos.jsπλαίσιο του λειτουργίες> APIs κατάλογο.

  1. Δημιουργία Todo API: Ανοίξτε το todos.jsκαι κάτω από τη μέθοδο postOneTodo προσθέστε το κλειδί ονόματος χρήστη ως εξής:
const newTodoItem = { .., username: request.user.username, .. }

2. GET All Todos API: Ανοίξτε το todos.jsκαι κάτω από τη μέθοδο getAllTodos προσθέστε τη ρήτρα όπου ως εξής:

db .collection('todos') .where('username', '==', request.user.username) .orderBy('createdAt', 'desc')

Εκτελέστε την υπηρεσία Firebase και δοκιμάστε το GET API μας. Μην ξεχάσετε να στείλετε το κουπόνι. Εδώ θα λάβετε ένα σφάλμα απόκρισης ως εξής:

{ "error": 9 }

Μεταβείτε στη γραμμή εντολών και θα δείτε τις ακόλουθες γραμμές καταγεγραμμένες:

i functions: Beginning execution of "api"> Error: 9 FAILED_PRECONDITION: The query requires an index. You can create it here: > at callErrorFromStatus

Ανοίξτε το στο πρόγραμμα περιήγησης και κάντε κλικ στο δημιουργία ευρετηρίου.

Μόλις δημιουργηθεί το ευρετήριο, στείλτε ξανά το αίτημα και θα λάβετε την ακόλουθη έξοδο:

[ { "todoId": "......", "title": "......", "username": "......", "body": "......", "createdAt": "2020-03-30T13:01:58.478Z" } ]

3.   Διαγραφή Todo API: Ανοίξτε το todos.jsκαι κάτω από τη μέθοδο deleteTodo προσθέστε την ακόλουθη συνθήκη. Προσθέστε αυτήν τη συνθήκη στο ερώτημα document.get (). Τότε () κάτω από την κατάσταση ! Doc.exists .

.. if(doc.data().username !== request.user.username){ return response.status(403).json({error:"UnAuthorized"}) }

Δομή καταλόγου έως τώρα:

+-- firebase.json +-- functions | +-- API | +-- +-- todos.js | +-- +-- users.js | +-- util | +-- +-- admin.js | +-- +-- auth.js | +-- +-- validators.js | +-- index.js | +-- node_modules | +-- package-lock.json | +-- package.json | +-- .gitignore

Με αυτό ολοκληρώσαμε το backend API μας. Κάντε ένα διάλειμμα, πιείτε έναν καφέ και μετά θα αρχίσουμε να χτίζουμε το μπροστινό μέρος της εφαρμογής μας

Ενότητα 3: Πίνακας ελέγχου χρήστη

Σε αυτόενότητα , πρόκειται να αναπτύξουμε αυτά τα στοιχεία:

  1. Ρύθμιση παραμέτρων ReactJS και υλικής διεπαφής.
  2. Φόρμα σύνδεσης και εγγραφής στο κτίριο.
  3. Ενότητα δημιουργίας λογαριασμού.

Ο κωδικός Πίνακα ελέγχου χρήστη που εφαρμόζεται σε αυτήν την ενότητα βρίσκεται σε αυτήν τη δέσμευση.

1. Διαμορφώστε το ReactJS και το UI υλικού:

Θα χρησιμοποιήσουμε το πρότυπο create-react-app. Μας δίνει μια θεμελιώδη δομή για την ανάπτυξη της εφαρμογής. Για να το εγκαταστήσετε, χρησιμοποιήστε την ακόλουθη εντολή:

npm install -g create-react-app

Μεταβείτε στον ριζικό φάκελο του έργου όπου υπάρχει ο κατάλογος λειτουργιών. Αρχικοποιήστε την εφαρμογή διεπαφής χρησιμοποιώντας την ακόλουθη εντολή:

create-react-app view

Θυμηθείτε να χρησιμοποιήσετε την έκδοση v16.13.1 τουτη βιβλιοθήκη ReactJS .

Μόλις ολοκληρωθεί η εγκατάσταση, θα δείτε τα ακόλουθα στα αρχεία καταγραφής της γραμμής εντολών σας:

cd view npm start Happy hacking!

Με αυτό, έχουμε διαμορφώσει την εφαρμογή React. Θα λάβετε την ακόλουθη δομή καταλόγου:

+-- firebase.json +-- functions { This Directory consists our API logic } +-- view { This Directory consists our FrontEnd Compoenents } +-- .firebaserc +-- .gitignore

Τώρα εκτελέστε την εφαρμογή χρησιμοποιώντας την εντολή npm start. Μεταβείτε στο πρόγραμμα περιήγησης //localhost:3000/και θα δείτε την ακόλουθη έξοδο:

Τώρα θα αφαιρέσουμε όλα τα περιττά στοιχεία. Μεταβείτε στον κατάλογο προβολής και, στη συνέχεια, καταργήστε όλα τα αρχείαπου έχουν [Κατάργηση] μπροστά τους. Για αυτό, ανατρέξτε στη δομή του δέντρου καταλόγου παρακάτω.

+-- README.md [ Remove ] +-- package-lock.json +-- package.json +-- node_modules +-- .gitignore +-- public | +-- favicon.ico [ Remove ] | +-- index.html | +-- logo192.png [ Remove ] | +-- logo512.png [ Remove ] | +-- manifest.json | +-- robots.txt +-- src | +-- App.css | +-- App.test.js | +-- index.js | +-- serviceWorker.js | +-- App.js | +-- index.css [ Remove ] | +-- logo.svg [ Remove ] | +-- setupTests.js

Μεταβείτε στον index.htmlκάτω δημόσιο κατάλογο και καταργήστε τις ακόλουθες γραμμές:

Τώρα μεταβείτε στον App.jsκατάλογο src και αντικαταστήστε τον παλιό κώδικα με τον ακόλουθο κώδικα:

import React from 'react'; function App() { return ( ); } export default App;

Μεταβείτε στο index.jsκαι καταργήστε την ακόλουθη εισαγωγή:

import './index.css'

Δεν έχω διαγράψει το App.cssούτε το χρησιμοποιώ σε αυτήν την εφαρμογή. Αλλά αν θέλετε να το διαγράψετε ή να το χρησιμοποιήσετε, είστε ελεύθεροι να το κάνετε.

Μεταβείτε στο πρόγραμμα περιήγησης //localhost:3000/και θα έχετε έξοδο κενής οθόνης.

Για να εγκαταστήσετε το υλικό UI μεταβείτε στον κατάλογο προβολής και αντιγράψτε-επικολλήστε αυτήν την εντολή στο τερματικό:

npm install @material-ui/core

Θυμηθείτε να χρησιμοποιήσετε την έκδοση v4.9.8 της βιβλιοθήκης Υλικό UI.

2. Φόρμα σύνδεσης:

Για να αναπτύξετε τη φόρμα σύνδεσης μεταβείτε στη διεύθυνση App.js. Στην κορυφή της App.jsπροσθήκης των παρακάτω εισαγωγών:

import { BrowserRouter as Router, Route, Switch } from 'react-router-dom'; import login from './pages/login';

Χρησιμοποιούμε το διακόπτη και τη διαδρομή για να ορίσουμε διαδρομές για το TodoApp. Αυτήν τη στιγμή θα προσθέσουμε μόνο τη διαδρομή / σύνδεση και θα εκχωρήσουμε ένα στοιχείο σύνδεσης σε αυτήν.

// App.js 

Δημιουργήστε έναν κατάλογο σελίδων κάτω από τον υπάρχοντα κατάλογο προβολής και ένα αρχείο που ονομάζεται login.jsκάτω από τον κατάλογο σελίδων .

Θα εισαγάγουμε στοιχεία Υλικού UI και το πακέτο Axios στα login.js:

// login.js // Material UI components import React, { Component } from 'react'; import Avatar from '@material-ui/core/Avatar'; import Button from '@material-ui/core/Button'; import CssBaseline from '@material-ui/core/CssBaseline'; import TextField from '@material-ui/core/TextField'; import Link from '@material-ui/core/Link'; import Grid from '@material-ui/core/Grid'; import LockOutlinedIcon from '@material-ui/icons/LockOutlined'; import Typography from '@material-ui/core/Typography'; import withStyles from '@material-ui/core/styles/withStyles'; import Container from '@material-ui/core/Container'; import CircularProgress from '@material-ui/core/CircularProgress'; import axios from 'axios';

Θα προσθέσουμε τα ακόλουθα στυλ στη σελίδα σύνδεσης:

// login.js const styles = (theme) => ({ paper: { marginTop: theme.spacing(8), display: 'flex', flexDirection: 'column', alignItems: 'center' }, avatar: { margin: theme.spacing(1), backgroundColor: theme.palette.secondary.main }, form: { width: '100%', marginTop: theme.spacing(1) }, submit: { margin: theme.spacing(3, 0, 2) }, customError: { color: 'red', fontSize: '0.8rem', marginTop: 10 }, progess: { position: 'absolute' } });

Θα δημιουργήσουμε μια κλάση με όνομα σύνδεσης που έχει μια φόρμα και θα υποβάλει χειριστή μέσα σε αυτήν.

// login.js class login extends Component { constructor(props) { super(props); this.state = { email: '', password: '', errors: [], loading: false }; } componentWillReceiveProps(nextProps) { if (nextProps.UI.errors) { this.setState({ errors: nextProps.UI.errors }); } } handleChange = (event) => { this.setState({ [event.target.name]: event.target.value }); }; handleSubmit = (event) => { event.preventDefault(); this.setState({ loading: true }); const userData = { email: this.state.email, password: this.state.password }; axios .post('/login', userData) .then((response) => { localStorage.setItem('AuthToken', `Bearer ${response.data.token}`); this.setState({ loading: false, }); this.props.history.push('/'); }) .catch((error) => { this.setState({ errors: error.response.data, loading: false }); }); }; render() { const { classes } = this.props; const { errors, loading } = this.state; return ( Login      Sign In {loading && }     {"Don't have an account? Sign Up"}    {errors.general && (  {errors.general}  )} ); } }

Στο τέλος αυτού του αρχείου προσθέστε την ακόλουθη εξαγωγή:

export default withStyles(styles)(login); 

Προσθέστε τη διεύθυνση URL των λειτουργιών firebase για προβολή> package.json ως εξής:

Θυμηθείτε: Προσθέστε ένα κλειδί με όνομα διακομιστή μεσολάβησης κάτω από την υπάρχουσα λίστα προγραμμάτων περιήγησης JSON
"proxy": "//-todoapp-.cloudfunctions.net/api"

Εγκαταστήστε το πακέτο εικονιδίων Axios και υλικού χρησιμοποιώντας τις ακόλουθες εντολές:

// Axios command: npm i axios // Material Icons: npm install @material-ui/icons

Έχουμε προσθέσει μια διαδρομή σύνδεσης στο App.js. Στο login.jsέχουμε δημιουργήσει ένα στοιχείο κλάσης που χειρίζεται την κατάσταση, στέλνει το αίτημα ανάρτησης στο API σύνδεσης χρησιμοποιώντας το πακέτο Axios. Εάν το αίτημα είναι επιτυχές, τότε αποθηκεύουμε το διακριτικό. Εάν λάβουμε σφάλματα στην απάντηση, απλώς τα αποδίδουμε στη διεπαφή χρήστη.

Μεταβείτε στο πρόγραμμα περιήγησης στο //localhost:3000/loginκαι θα δείτε την ακόλουθη διεπαφή σύνδεσης.

Δοκιμάστε να συμπληρώσετε λανθασμένα διαπιστευτήρια ή να στείλετε ένα κενό αίτημα και θα λάβετε τα λάθη. Στείλτε ένα έγκυρο αίτημα. Μεταβείτε στην Κονσόλα προγραμματιστή> Εφαρμογή . Θα δείτε ότι το διακριτικό χρηστών αποθηκεύεται στον Τοπικό χώρο αποθήκευσης. Μόλις η σύνδεση είναι επιτυχής, θα επιστρέψουμε στην αρχική σελίδα.

3. Φόρμα εγγραφής:

Για να αναπτύξετε τη φόρμα εγγραφής μεταβείτε App.jsκαι ενημερώστε το υπάρχον Routeστοιχείο με την παρακάτω γραμμή:

// App.js 

Μην ξεχάσετε να εισαγάγετε:

// App.js import signup from './pages/signup';

Δημιουργήστε ένα αρχείο με όνομα signup.jsστον κατάλογο σελίδων .

Μέσα στο signup.js θα εισαγάγουμε το πακέτο υλικού UI και Axios:

// signup.js import React, { Component } from 'react'; import Avatar from '@material-ui/core/Avatar'; import Button from '@material-ui/core/Button'; import CssBaseline from '@material-ui/core/CssBaseline'; import TextField from '@material-ui/core/TextField'; import Link from '@material-ui/core/Link'; import Grid from '@material-ui/core/Grid'; import LockOutlinedIcon from '@material-ui/icons/LockOutlined'; import Typography from '@material-ui/core/Typography'; import Container from '@material-ui/core/Container'; import withStyles from '@material-ui/core/styles/withStyles'; import CircularProgress from '@material-ui/core/CircularProgress'; import axios from 'axios';

Θα προσθέσουμε τα ακόλουθα στυλ στη σελίδα εγγραφής μας:

// signup.js const styles = (theme) => ({ paper: { marginTop: theme.spacing(8), display: 'flex', flexDirection: 'column', alignItems: 'center' }, avatar: { margin: theme.spacing(1), backgroundColor: theme.palette.secondary.main }, form: { width: '100%', // Fix IE 11 issue. marginTop: theme.spacing(3) }, submit: { margin: theme.spacing(3, 0, 2) }, progess: { position: 'absolute' } }); 

Θα δημιουργήσουμε μια τάξη με την ονομασία εγγραφής που έχει μια φόρμα και θα υποβάλει χειριστή μέσα σε αυτήν.

// signup.js class signup extends Component { constructor(props) { super(props); this.state = { firstName: '', lastName: '', phoneNumber: '', country: '', username: '', email: '', password: '', confirmPassword: '', errors: [], loading: false }; } componentWillReceiveProps(nextProps) { if (nextProps.UI.errors) { this.setState({ errors: nextProps.UI.errors }); } } handleChange = (event) => { this.setState({ [event.target.name]: event.target.value }); }; handleSubmit = (event) => { event.preventDefault(); this.setState({ loading: true }); const newUserData = { firstName: this.state.firstName, lastName: this.state.lastName, phoneNumber: this.state.phoneNumber, country: this.state.country, username: this.state.username, email: this.state.email, password: this.state.password, confirmPassword: this.state.confirmPassword }; axios .post('/signup', newUserData) .then((response) => { localStorage.setItem('AuthToken', `${response.data.token}`); this.setState({ loading: false, }); this.props.history.push('/'); }) .catch((error) => { this.setState({ errors: error.response.data, loading: false }); }); }; render() { const { classes } = this.props; const { errors, loading } = this.state; return ( Sign up                              Sign Up {loading && }     Already have an account? Sign in ); } }

Στο τέλος αυτού του αρχείου προσθέστε την ακόλουθη εξαγωγή:

export default withStyles(styles)(signup); 

Η λογική για το στοιχείο εγγραφής είναι η ίδια με το στοιχείο σύνδεσης. Μεταβείτε στο πρόγραμμα περιήγησης στο //localhost:3000/signupκαι θα δείτε την ακόλουθη διεπαφή εγγραφής. Μόλις η εγγραφή είναι επιτυχής, θα επιστρέψουμε στην αρχική σελίδα.

Δοκιμάστε να συμπληρώσετε λανθασμένα διαπιστευτήρια ή να στείλετε ένα κενό αίτημα και θα λάβετε τα λάθη. Στείλτε ένα έγκυρο αίτημα. Μεταβείτε στην Κονσόλα προγραμματιστή> Εφαρμογή . Θα δείτε ότι το διακριτικό χρηστών αποθηκεύεται στον Τοπικό χώρο αποθήκευσης.

4. Τμήμα λογαριασμού:

Για να δημιουργήσουμε τη σελίδα λογαριασμού θα πρέπει πρώτα να δημιουργήσουμε την αρχική μας σελίδα από όπου θα φορτώσουμε την ενότητα λογαριασμού . Μεταβείτε στο App.jsκαι ενημερώστε την ακόλουθη διαδρομή:

// App.js 

Μην ξεχάσετε την εισαγωγή:

// App.js import home from './pages/home';

Δημιουργήστε ένα νέο αρχείο με το όνομα home.js. Αυτό το αρχείο θα είναι το ευρετήριο της εφαρμογής μας. Οι ενότητες Λογαριασμός και Todo φορτώνουν και οι δύο σε αυτήν τη σελίδα με βάση το κλικ στο κουμπί.

Εισαγάγετε τα πακέτα υλικών UI, το πακέτο Axios, τον προσαρμοσμένο λογαριασμό μας, τα εξαρτήματα todo και το μεσαίο λογισμικό ελέγχου

// home.js import React, { Component } from 'react'; import axios from 'axios'; import Account from '../components/account'; import Todo from '../components/todo'; import Drawer from '@material-ui/core/Drawer'; import AppBar from '@material-ui/core/AppBar'; import CssBaseline from '@material-ui/core/CssBaseline'; import Toolbar from '@material-ui/core/Toolbar'; import List from '@material-ui/core/List'; import Typography from '@material-ui/core/Typography'; import Divider from '@material-ui/core/Divider'; import ListItem from '@material-ui/core/ListItem'; import ListItemIcon from '@material-ui/core/ListItemIcon'; import ListItemText from '@material-ui/core/ListItemText'; import withStyles from '@material-ui/core/styles/withStyles'; import AccountBoxIcon from '@material-ui/icons/AccountBox'; import NotesIcon from '@material-ui/icons/Notes'; import Avatar from '@material-ui/core/avatar'; import ExitToAppIcon from '@material-ui/icons/ExitToApp'; import CircularProgress from '@material-ui/core/CircularProgress'; import { authMiddleWare } from '../util/auth'

Θα ρυθμίσουμε το συρτάρι μας Πλάτος ως εξής:

const drawerWidth = 240;

Θα προσθέσουμε το ακόλουθο στυλ στην αρχική μας σελίδα:

const styles = (theme) => ({ root: { display: 'flex' }, appBar: { zIndex: theme.zIndex.drawer + 1 }, drawer: { width: drawerWidth, flexShrink: 0 }, drawerPaper: { width: drawerWidth }, content: { flexGrow: 1, padding: theme.spacing(3) }, avatar: { height: 110, width: 100, flexShrink: 0, flexGrow: 0, marginTop: 20 }, uiProgess: { position: 'fixed', zIndex: '1000', height: '31px', width: '31px', left: '50%', top: '35%' }, toolbar: theme.mixins.toolbar });

Θα δημιουργήσουμε μια τάξη με το όνομα σπίτι. Αυτή η τάξη θα έχει μια κλήση API για λήψη της εικόνας προφίλ του χρήστη, Όνομα και Επώνυμο. Επίσης, θα έχει λογική να επιλέξει ποιο στοιχείο θα εμφανιστεί, είτε Todo είτε Λογαριασμός:

class home extends Component { state = { render: false }; loadAccountPage = (event) => { this.setState({ render: true }); }; loadTodoPage = (event) => { this.setState({ render: false }); }; logoutHandler = (event) => { localStorage.removeItem('AuthToken'); this.props.history.push('/login'); }; constructor(props) { super(props); this.state = { firstName: '', lastName: '', profilePicture: '', uiLoading: true, imageLoading: false }; } componentWillMount = () => { authMiddleWare(this.props.history); const authToken = localStorage.getItem('AuthToken'); axios.defaults.headers.common = { Authorization: `${authToken}` }; axios .get('/user') .then((response) => { console.log(response.data); this.setState({ firstName: response.data.userCredentials.firstName, lastName: response.data.userCredentials.lastName, email: response.data.userCredentials.email, phoneNumber: response.data.userCredentials.phoneNumber, country: response.data.userCredentials.country, username: response.data.userCredentials.username, uiLoading: false, profilePicture: response.data.userCredentials.imageUrl }); }) .catch((error) => { if(error.response.status === 403) { this.props.history.push('/login') } console.log(error); this.setState({ errorMsg: 'Error in retrieving the data' }); }); }; render() { const { classes } = this.props; if (this.state.uiLoading === true) { return ( {this.state.uiLoading && } ); } else { return ( TodoApp 

{' '} {this.state.firstName} {this.state.lastName}

{' '} {' '} {' '} {' '} {' '} {' '} {this.state.render ? : } ); } } }

Εδώ στον κώδικα, θα δείτε ότι authMiddleWare(this.props.history);χρησιμοποιείται. Αυτό το μεσαίο λογισμικό ελέγχει εάν το authToken είναι μηδενικό. Εάν ναι, θα ωθήσει τον χρήστη πίσω στο login.js. Αυτό προστίθεται έτσι ώστε ο χρήστης μας να μην έχει πρόσβαση στη /διαδρομή χωρίς εγγραφή ή σύνδεση. Στο τέλος αυτού του αρχείου προσθέστε την ακόλουθη εξαγωγή:

export default withStyles(styles)(home); 

Τώρα αναρωτιέστε τι κάνει αυτός ο κώδικας home.js;

 {this.state.render ?  : } 

Ελέγχει την κατάσταση απόδοσης που θέτουμε στο κουμπί κλικ. Ας δημιουργήσουμε τον κατάλογο στοιχείων και κάτω από αυτόν τον κατάλογο δημιουργήστε δύο αρχεία: account.jsκαι todo.js.

Ας δημιουργήσουμε έναν κατάλογο με το όνομα util και το αρχείο που ονομάζεται auth.jsκάτω από αυτόν τον κατάλογο. Αντιγράψτε-επικολλήστε τον ακόλουθο κώδικα κάτω από auth.js:

export const authMiddleWare = (history) => { const authToken = localStorage.getItem('AuthToken'); if(authToken === null){ history.push('/login') } }

Προς το παρόν είναι μέσα στο todo.jsαρχείο θα γράψουμε απλά μια τάξη που καθιστά το κείμενο Γεια σας είμαι todo . Θα εργαστούμε για τα todos μας στην επόμενη ενότητα:

import React, { Component } from 'react' import withStyles from '@material-ui/core/styles/withStyles'; import Typography from '@material-ui/core/Typography'; const styles = ((theme) => ({ content: { flexGrow: 1, padding: theme.spacing(3), }, toolbar: theme.mixins.toolbar, }) ); class todo extends Component { render() { const { classes } = this.props; return ( Hello I am todo   ) } } export default (withStyles(styles)(todo));

Τώρα είναι ώρα για την ενότητα λογαριασμού. Εισαγάγετε το βοηθητικό πρόγραμμα Material UI, clsx, axios και authmiddleWare στο account.js.

// account.js import React, { Component } from 'react'; import withStyles from '@material-ui/core/styles/withStyles'; import Typography from '@material-ui/core/Typography'; import CircularProgress from '@material-ui/core/CircularProgress'; import CloudUploadIcon from '@material-ui/icons/CloudUpload'; import { Card, CardActions, CardContent, Divider, Button, Grid, TextField } from '@material-ui/core'; import clsx from 'clsx'; import axios from 'axios'; import { authMiddleWare } from '../util/auth';

Θα προσθέσουμε το ακόλουθο στυλ στη σελίδα του λογαριασμού μας:

// account.js const styles = (theme) => ({ content: { flexGrow: 1, padding: theme.spacing(3) }, toolbar: theme.mixins.toolbar, root: {}, details: { display: 'flex' }, avatar: { height: 110, width: 100, flexShrink: 0, flexGrow: 0 }, locationText: { paddingLeft: '15px' }, buttonProperty: { position: 'absolute', top: '50%' }, uiProgess: { position: 'fixed', zIndex: '1000', height: '31px', width: '31px', left: '50%', top: '35%' }, progess: { position: 'absolute' }, uploadButton: { marginLeft: '8px', margin: theme.spacing(1) }, customError: { color: 'red', fontSize: '0.8rem', marginTop: 10 }, submitButton: { marginTop: '10px' } });

Θα δημιουργήσουμε ένα στοιχείο κλάσης με όνομα λογαριασμό. Προς το παρόν απλώς αντιγράψτε-επικολλήστε τον ακόλουθο κώδικα:

// account.js class account extends Component { constructor(props) { super(props); this.state = { firstName: '', lastName: '', email: '', phoneNumber: '', username: '', country: '', profilePicture: '', uiLoading: true, buttonLoading: false, imageError: '' }; } componentWillMount = () => { authMiddleWare(this.props.history); const authToken = localStorage.getItem('AuthToken'); axios.defaults.headers.common = { Authorization: `${authToken}` }; axios .get('/user') .then((response) => { console.log(response.data); this.setState({ firstName: response.data.userCredentials.firstName, lastName: response.data.userCredentials.lastName, email: response.data.userCredentials.email, phoneNumber: response.data.userCredentials.phoneNumber, country: response.data.userCredentials.country, username: response.data.userCredentials.username, uiLoading: false }); }) .catch((error) => { if (error.response.status === 403) { this.props.history.push('/login'); } console.log(error); this.setState({ errorMsg: 'Error in retrieving the data' }); }); }; handleChange = (event) => { this.setState({ [event.target.name]: event.target.value }); }; handleImageChange = (event) => { this.setState({ image: event.target.files[0] }); }; profilePictureHandler = (event) => { event.preventDefault(); this.setState({ uiLoading: true }); authMiddleWare(this.props.history); const authToken = localStorage.getItem('AuthToken'); let form_data = new FormData(); form_data.append('image', this.state.image); form_data.append('content', this.state.content); axios.defaults.headers.common = { Authorization: `${authToken}` }; axios .post('/user/image', form_data, { headers: { 'content-type': 'multipart/form-data' } }) .then(() => { window.location.reload(); }) .catch((error) => { if (error.response.status === 403) { this.props.history.push('/login'); } console.log(error); this.setState({ uiLoading: false, imageError: 'Error in posting the data' }); }); }; updateFormValues = (event) => { event.preventDefault(); this.setState({ buttonLoading: true }); authMiddleWare(this.props.history); const authToken = localStorage.getItem('AuthToken'); axios.defaults.headers.common = { Authorization: `${authToken}` }; const formRequest = { firstName: this.state.firstName, lastName: this.state.lastName, country: this.state.country }; axios .post('/user', formRequest) .then(() => { this.setState({ buttonLoading: false }); }) .catch((error) => { if (error.response.status === 403) { this.props.history.push('/login'); } console.log(error); this.setState({ buttonLoading: false }); }); }; render() { const { classes, ...rest } = this.props; if (this.state.uiLoading === true) { return ( {this.state.uiLoading && }  ); } else { return ( {this.state.firstName} {this.state.lastName}  

Στο τέλος αυτού του αρχείου προσθέστε την ακόλουθη εξαγωγή:

export default withStyles(styles)(account); 

Στη account.jsυπάρχουν πολλά συστατικά που χρησιμοποιούνται. Πρώτα ας δούμε πώς φαίνεται η εφαρμογή μας. Μετά από αυτό θα εξηγήσω όλα τα συστατικά που χρησιμοποιούνται και γιατί χρησιμοποιούνται.

Μεταβείτε στο πρόγραμμα περιήγησης και εάν το διακριτικό σας έχει λήξει, θα σας ανακατευθύνει στη   loginσελίδα. Προσθέστε τα στοιχεία σας και συνδεθείτε ξανά. Μόλις το κάνετε αυτό, μεταβείτε στην καρτέλα Λογαριασμός και θα βρείτε την ακόλουθη διεπαφή χρήστη:

Υπάρχουν 3 διαχειριστές στην ενότητα λογαριασμού:

  1. componentWillMount : Αυτή είναι η ενσωματωμένη μέθοδος κύκλου ζωής της React. Το χρησιμοποιούμε για να φορτώσουμε τα δεδομένα πριν από τον κύκλο ζωής της απόδοσης και να ενημερώσουμε τις τιμές της κατάστασής μας.
  2. ProfilePictureUpdate: Αυτός είναι ο προσαρμοσμένος χειριστής μας που χρησιμοποιούμε έτσι ώστε όταν ο χρήστης μας κάνει κλικ στο κουμπί Μεταφόρτωση φωτογραφίας, τότε θα στείλει τα δεδομένα σε έναν διακομιστή και θα φορτώσει ξανά τη σελίδα για να εμφανίσει τη νέα εικόνα προφίλ του χρήστη.
  3. updateFormValues: Αυτός είναι επίσης ο προσαρμοσμένος χειριστής μας για την ενημέρωση των στοιχείων του χρήστη. Εδώ, ο χρήστης μπορεί να ενημερώσει το όνομα, το επώνυμο και τη χώρα του. Δεν επιτρέπουμε ενημερώσεις email και ονόματος χρήστη, επειδή η λογική του backend εξαρτάται από αυτά τα κλειδιά.

Εκτός από αυτούς τους 3 χειριστές, είναι μια σελίδα φόρμας με στυλ πάνω από αυτό. Εδώ είναι η δομή καταλόγου έως αυτό το σημείο μέσα στο φάκελο προβολής:

+-- public +-- src | +-- components | +-- +-- todo.js | +-- +-- account.js | +-- pages | +-- +-- home.js | +-- +-- login.js | +-- +-- signup.js | +-- util | +-- +-- auth.js | +-- README.md | +-- package-lock.json | +-- package.json | +-- .gitignore

Με αυτό ολοκληρώσαμε τον Πίνακα ελέγχου λογαριασμού. Τώρα πάμε για καφέ, κάνε ένα διάλειμμα και στην επόμενη ενότητα, θα φτιάξουμε το Todo Dashboard.

Ενότητα 4: Πίνακας ελέγχου Todo

Σε αυτόενότητα , πρόκειται να αναπτύξουμε το περιβάλλον εργασίας χρήστη για αυτές τις δυνατότητες του Todos Dashboard:

  1. Προσθέστε ένα Todo:
  2. Λάβετε όλα τα todos:
  3. Διαγραφή ενός todo
  4. Επεξεργασία todo
  5. Πάρτε ένα todo
  6. Εφαρμογή θέματος

Ο κωδικός Todo Dashboard που εφαρμόζεται σε αυτήν την ενότητα βρίσκεται σε αυτήν τη δέσμευση.

Πηγαίνετε στο todos.jsπλαίσιο του συστατικά κατάλογο. Προσθέστε τις ακόλουθες εισαγωγές στις υπάρχουσες εισαγωγές:

import Button from '@material-ui/core/Button'; import Dialog from '@material-ui/core/Dialog'; import AddCircleIcon from '@material-ui/icons/AddCircle'; import AppBar from '@material-ui/core/AppBar'; import Toolbar from '@material-ui/core/Toolbar'; import IconButton from '@material-ui/core/IconButton'; import CloseIcon from '@material-ui/icons/Close'; import Slide from '@material-ui/core/Slide'; import TextField from '@material-ui/core/TextField'; import Grid from '@material-ui/core/Grid'; import Card from '@material-ui/core/Card'; import CardActions from '@material-ui/core/CardActions'; import CircularProgress from '@material-ui/core/CircularProgress'; import CardContent from '@material-ui/core/CardContent'; import MuiDialogTitle from '@material-ui/core/DialogTitle'; import MuiDialogContent from '@material-ui/core/DialogContent'; import axios from 'axios'; import dayjs from 'dayjs'; import relativeTime from 'dayjs/plugin/relativeTime'; import { authMiddleWare } from '../util/auth';

Πρέπει επίσης να προσθέσουμε τα ακόλουθα στοιχεία CSS στα υπάρχοντα στοιχεία στυλ:

const styles = (theme) => ({ .., // Existing CSS elements title: { marginLeft: theme.spacing(2), flex: 1 }, submitButton: { display: 'block', color: 'white', textAlign: 'center', position: 'absolute', top: 14, right: 10 }, floatingButton: { position: 'fixed', bottom: 0, right: 0 }, form: { width: '98%', marginLeft: 13, marginTop: theme.spacing(3) }, toolbar: theme.mixins.toolbar, root: { minWidth: 470 }, bullet: { display: 'inline-block', margin: '0 2px', transform: 'scale(0.8)' }, pos: { marginBottom: 12 }, uiProgess: { position: 'fixed', zIndex: '1000', height: '31px', width: '31px', left: '50%', top: '35%' }, dialogeStyle: { maxWidth: '50%' }, viewRoot: { margin: 0, padding: theme.spacing(2) }, closeButton: { position: 'absolute', right: theme.spacing(1), top: theme.spacing(1), color: theme.palette.grey[500] } });

Θα προσθέσουμε τη μετάβαση για το αναδυόμενο πλαίσιο διαλόγου:

const Transition = React.forwardRef(function Transition(props, ref) { return ; });

Καταργήστε την υπάρχουσα τάξη todo και αντιγράψτε-επικολλήστε την ακόλουθη τάξη:

class todo extends Component { constructor(props) { super(props); this.state = { todos: '', title: '', body: '', todoId: '', errors: [], open: false, uiLoading: true, buttonType: '', viewOpen: false }; this.deleteTodoHandler = this.deleteTodoHandler.bind(this); this.handleEditClickOpen = this.handleEditClickOpen.bind(this); this.handleViewOpen = this.handleViewOpen.bind(this); } handleChange = (event) => { this.setState({ [event.target.name]: event.target.value }); }; componentWillMount = () => { authMiddleWare(this.props.history); const authToken = localStorage.getItem('AuthToken'); axios.defaults.headers.common = { Authorization: `${authToken}` }; axios .get('/todos') .then((response) => { this.setState({ todos: response.data, uiLoading: false }); }) .catch((err) => { console.log(err); }); }; deleteTodoHandler(data) { authMiddleWare(this.props.history); const authToken = localStorage.getItem('AuthToken'); axios.defaults.headers.common = { Authorization: `${authToken}` }; let todoId = data.todo.todoId; axios .delete(`todo/${todoId}`) .then(() => { window.location.reload(); }) .catch((err) => { console.log(err); }); } handleEditClickOpen(data) { this.setState({ title: data.todo.title, body: data.todo.body, todoId: data.todo.todoId, buttonType: 'Edit', open: true }); } handleViewOpen(data) { this.setState({ title: data.todo.title, body: data.todo.body, viewOpen: true }); } render() { const DialogTitle = withStyles(styles)((props) => { const { children, classes, onClose, ...other } = props; return (  {children} {onClose ? (    ) : null}  ); }); const DialogContent = withStyles((theme) => ({ viewRoot: { padding: theme.spacing(2) } }))(MuiDialogContent); dayjs.extend(relativeTime); const { classes } = this.props; const { open, errors, viewOpen } = this.state; const handleClickOpen = () => { this.setState({ todoId: '', title: '', body: '', buttonType: '', open: true }); }; const handleSubmit = (event) => { authMiddleWare(this.props.history); event.preventDefault(); const userTodo = { title: this.state.title, body: this.state.body }; let options = {}; if (this.state.buttonType === 'Edit') { options = { url: `/todo/${this.state.todoId}`, method: 'put', data: userTodo }; } else { options = { url: '/todo', method: 'post', data: userTodo }; } const authToken = localStorage.getItem('AuthToken'); axios.defaults.headers.common = { Authorization: `${authToken}` }; axios(options) .then(() => { this.setState({ open: false }); window.location.reload(); }) .catch((error) => { this.setState({ open: true, errors: error.response.data }); console.log(error); }); }; const handleViewClose = () => { this.setState({ viewOpen: false }); }; const handleClose = (event) => { this.setState({ open: false }); }; if (this.state.uiLoading === true) { return ( {this.state.uiLoading && }  ); } else { return ( {this.state.buttonType === 'Edit' ? 'Edit Todo' : 'Create a new Todo'}   {this.state.buttonType === 'Edit' ? 'Save' : 'Submit'}                {this.state.todos.map((todo) => (     {todo.title}   {dayjs(todo.createdAt).fromNow()}   {`${todo.body.substring(0, 65)}`}     this.handleViewOpen({ todo })}> {' '} View{' '}   this.handleEditClickOpen({ todo })}> Edit   this.deleteTodoHandler({ todo })}> Delete     ))}    {this.state.title}       ); } } }

Στο τέλος αυτού του αρχείου προσθέστε την ακόλουθη εξαγωγή:

export default withStyles(styles)(todo); 

Πρώτα θα καταλάβουμε πώς λειτουργεί το περιβάλλον εργασίας μας και μετά θα κατανοήσουμε τον κώδικα. Μεταβείτε στο πρόγραμμα περιήγησης και θα λάβετε την ακόλουθη διεπαφή χρήστη:

Κάντε κλικ στο κουμπί Προσθήκη στην κάτω δεξιά γωνία και θα εμφανιστεί η ακόλουθη οθόνη:

Προσθέστε τον τίτλο και τις λεπτομέρειες του Todo και πατήστε το κουμπί υποβολής. Θα λάβετε την ακόλουθη οθόνη:

Μετά από αυτό κάντε κλικ στο κουμπί προβολής και θα μπορείτε να δείτε τις πλήρεις λεπτομέρειες του Todo:

Κάντε κλικ στο κουμπί Επεξεργασία και θα μπορείτε να επεξεργαστείτε το πρόγραμμα:

Κάντε κλικ στο κουμπί διαγραφής και θα μπορείτε να διαγράψετε το Todo. Τώρα καθώς γνωρίζουμε πώς λειτουργεί το Dashboard, θα κατανοήσουμε τα στοιχεία που χρησιμοποιούνται σε αυτό.

1. Προσθήκη Todo: Για την εφαρμογή του add todo θα χρησιμοποιήσουμε το στοιχείο διαλόγου του υλικού UI. Αυτό το στοιχείο εφαρμόζει μια λειτουργία αγκίστρου. Χρησιμοποιούμε τις τάξεις, ώστε να καταργήσουμε αυτήν τη λειτουργικότητα.

// This sets the state to open and buttonType flag to add: const handleClickOpen = () => { this.setState({ todoId: '', title: '', body: '', buttonType: '', open: true }); }; // This sets the state to close: const handleClose = (event) => { this.setState({ open: false }); };

Εκτός από αυτό θα αλλάξουμε επίσης την τοποθέτηση του κουμπιού Προσθήκη Todo.

// Position our button floatingButton: { position: 'fixed', bottom: 0, right: 0 }, 

Τώρα θα αντικαταστήσουμε την ετικέτα λίστας με μια φόρμα μέσα σε αυτόν τον διάλογο. Θα μας βοηθήσει στην προσθήκη του νέου todo.

// Show Edit or Save depending on buttonType state {this.state.buttonType === 'Edit' ? 'Save' : 'Submit'} // Our Form to add a todo    // TextField here   // TextField here   

ολαβή Υποβολήαποτελείται από λογική για την ανάγνωση της buttonTypeκατάστασης. Εάν η κατάσταση είναι κενή συμβολοσειρά, (“”)τότε θα δημοσιεύσει στο API Προσθήκη Todo. Εάν η κατάσταση είναι Editτότε σε αυτό το σενάριο, θα ενημερώσει το Edit Todo.

2. Λήψη Todos: Για την εμφάνιση των todos θα χρησιμοποιήσουμε το Grid containerκαι μέσα σε αυτό, τοποθετούμε το Grid item. Μέσα σε αυτό, θα χρησιμοποιήσουμε ένα Cardστοιχείο για την εμφάνιση των δεδομένων.

 {this.state.todos.map((todo) => (    // Here will show Todo with view, edit and delete button   ))} 

Χρησιμοποιούμε τον χάρτη για να εμφανίσουμε το στοιχείο εκκρεμών εργασιών καθώς το API τους στέλνει σε μια λίστα. Θα χρησιμοποιήσουμε τον κύκλο ζωής του συστατικούWillMount για να πάρουμε και να ορίσουμε την κατάσταση πριν από την εκτέλεση της απόδοσης. Υπάρχουν 3 κουμπιά ( προβολή, επεξεργασία και διαγραφή ), οπότε χρειαζόμαστε 3 χειριστές για να χειριστούμε τη λειτουργία όταν κάνετε κλικ στο κουμπί. Θα μάθουμε για αυτά τα κουμπιά στις αντίστοιχες υποενότητες τους.

3. Επεξεργασία Todo: Για την επεξεργασία todo, χρησιμοποιούμε τον αναδυόμενο κώδικα διαλόγου που χρησιμοποιείται στο add todo. Για να γίνει διάκριση μεταξύ των κλικ των κουμπιών χρησιμοποιούμε μια buttonTypeκατάσταση. Για την Προσθήκη Todo η   buttonTypeκατάσταση είναι (“”)ενώ για επεξεργασία πρέπει να είναι Edit.

handleEditClickOpen(data) { this.setState({ .., buttonType: 'Edit', .. }); }

Στη handleSubmitμέθοδο διαβάζουμε την buttonTypeκατάσταση και μετά στέλνουμε το αίτημα αναλόγως.

4. Διαγραφή Todo: Όταν πατηθεί αυτό το κουμπί στέλνουμε το αντικείμενο todo στο deleteTodoHandler και στη συνέχεια στέλνει το αίτημα περαιτέρω στο backend.

 this.deleteTodoHandler({ todo })}>Delete

5. Προβολή Todo: Όταν εμφανίζουμε τα δεδομένα, τα έχουμε περικόψει έτσι ώστε ο χρήστης να έχει μια ματιά για το τι πρέπει να κάνει. Αλλά εάν ένας χρήστης θέλει να μάθει περισσότερα για αυτό, τότε πρέπει να κάνει κλικ στο κουμπί προβολής.

Για αυτό, θα χρησιμοποιήσουμε τον προσαρμοσμένο διάλογο. Εντός αυτού, χρησιμοποιούμε DialogTitle και DialogContent. Εμφανίζει τον τίτλο και το περιεχόμενό μας. Στο DialougeContent θα χρησιμοποιήσουμε τη φόρμα για να εμφανίσουμε το περιεχόμενο που έχει δημοσιεύσει ο χρήστης. (Αυτή είναι μια λύση που βρήκα ότι υπάρχουν πολλές και είστε ελεύθεροι να δοκιμάσετε άλλες.)

// This is used to remove the underline of the Form InputProps={{ disableUnderline: true }} // This is used so that user cannot edit the data readonly

6. Εφαρμογή θέματος: Αυτό είναι το τελευταίο βήμα της εφαρμογής μας. Θα εφαρμόσουμε ένα θέμα στην αίτησή μας. Γι 'αυτό χρησιμοποιούμε createMuiThemeκαι ThemeProviderαπό υλικό UI. Αντιγράψτε-επικολλήστε τον ακόλουθο κώδικα σε App.js:

import { ThemeProvider as MuiThemeProvider } from '@material-ui/core/styles'; import createMuiTheme from '@material-ui/core/styles/createMuiTheme'; const theme = createMuiTheme({ palette: { primary: { light: '#33c9dc', main: '#FF5722', dark: '#d50000', contrastText: '#fff' } } }); function App() { return (  // Router and switch will be here.  ); }

Χάσαμε την εφαρμογή ενός θέματος στο κουμπί μας todo.jsστο CardActions. Προσθέστε την ετικέτα χρώματος για το κουμπί προβολής, επεξεργασίας και διαγραφής.

Μεταβείτε στο πρόγραμμα περιήγησης και θα διαπιστώσετε ότι όλα είναι ίδια εκτός από το ότι η εφαρμογή έχει διαφορετικό χρώμα.

Και τελειώσαμε! Έχουμε δημιουργήσει ένα TodoApp χρησιμοποιώντας ReactJS και Firebase. Εάν το έχετε φτιάξει μέχρι τώρα, τότε σας συγχαίρω πολύ για αυτό το επίτευγμα.

Μη διστάσετε να συνδεθείτε μαζί μου στο Twitter και το Github.