Kako izraditi TodoApp pomoću ReactJS-a i Firebase-a

Pozdrav narode, dobrodošli u ovaj vodič. Prije nego što započnemo, trebali biste se upoznati s osnovnim konceptima ReactJS-a. Ako niste, preporučio bih vam da prođete kroz ReactJS dokumentaciju.

U ovoj ćemo aplikaciji koristiti sljedeće komponente:

  1. ReactJS
  2. Korisničko sučelje materijala
  3. Firebase
  4. ExpressJS
  5. Poštar

Kako će naša aplikacija izgledati:

Arhitektura aplikacije:

Razumijevanje naših komponenata:

Možda se pitate zašto u ovoj aplikaciji koristimo firebase. Pa, pruža sigurnu autentifikaciju , bazu podataka u stvarnom vremenu , komponentu bez poslužitelja i segme za pohranu .

Ovdje koristimo Express tako da ne trebamo obrađivati ​​HTTP iznimke. Upotrijebit ćemo sve firebase pakete u našoj komponenti funkcija. To je zato što našu klijentsku aplikaciju ne želimo učiniti prevelikom, što usporava postupak učitavanja korisničkog sučelja.

Napomena: Podijelit ću ovaj vodič u četiri zasebna odjeljka. Na početku svakog odjeljka pronaći ćete git commit koji ima razvijen kôd u tom odjeljku. Također Ako želite vidjeti cjeloviti kôd, on je dostupan u ovom spremištu.

Odjeljak 1: Razvoj Todo API-ja

U ovomeodjeljak , razvit ćemo ove elemente:

  1. Konfigurirajte funkcije vatrogasne baze.
  2. Instalirajte Express framework i izradite Todo API-je.
  3. Konfiguriranje firestorea kao baze podataka.

Kôd Todo API-ja implementiran u ovom odjeljku može se naći u ovom predaju.

Konfiguriranje Firebase funkcija:

Idite na Firebase konzolu.

Odaberite opciju Dodaj projekt . Nakon toga slijedite gif dolje korak po korak da biste konfigurirali firebase projekt.

Idite na karticu funkcija i kliknite gumb Početak :

Vidjet ćete dijaloški okvir s uputama o postavljanju Firebase funkcija . Idite u svoje lokalno okruženje. Otvorite alat naredbenog retka. Da biste instalirali firebase alate u svoj stroj, upotrijebite naredbu u nastavku:

 npm install -g firebase-tools

Kad je to gotovo, upotrijebite naredbu firebase initza konfiguriranje firebase funkcija u vašem lokalnom okruženju. Odaberite sljedeće opcije prilikom inicijalizacije funkcije firebase u lokalnom okruženju:

  1. Koje značajke Firebase CLI-a želite postaviti za ovu mapu? Pritisnite razmaknicu za odabir značajki, a zatim tipku Enter za potvrdu izbora => Funkcije: Konfigurirajte i postavite Cloud funkcije
  2. Prvo, povežimo ovaj direktorij projekta s Firebase projektom ... => Koristite postojeći projekt
  3. Odaberite zadani Firebase projekt za ovaj direktorij => ime_aplikacije
  4. Koji jezik biste željeli koristiti za pisanje funkcija u oblaku? => JavaScript
  5. Želite li koristiti ESLint za hvatanje vjerojatnih grešaka i nametanje stila? => N
  6. Želite li sada instalirati ovisnosti s npm? (Y / n) => Y

Nakon završetka konfiguracije dobit ćete sljedeću poruku:

✔ Firebase initialization complete!

Ovo će biti naša struktura direktorija nakon završetka inicijalizacije:

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

Sada otvorite index.jsdirektorij ispod funkcija i kopirajte i zalijepite sljedeći kod:

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

Postavite kôd u firebase funkcije pomoću sljedeće naredbe:

firebase deploy

Nakon završetka postavljanja, na kraju naredbenog retka dobit ćete sljedeću liniju:

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

Idite na Project Console> Functions i tamo ćete pronaći URL API-ja. URL će izgledati ovako:

//-todoapp-.cloudfunctions.net/helloWorld

Kopirajte ovaj URL i zalijepite ga u preglednik. Dobit ćete sljedeći odgovor:

Hello from Firebase!

To potvrđuje da je naša Firebase funkcija ispravno konfigurirana.

Instalirajte Express Framework:

Sada instalirajmo Expressokvir u naš projekt pomoću sljedeće naredbe:

npm i express

Sada kreirajmo direktorij API-ja unutar direktorija funkcija . Unutar tog direktorija stvorit ćemo datoteku s imenom todos.js. Uklonite sve iz, index.jsa zatim kopirajte i zalijepite sljedeći kod:

//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);

Funkciju getAllTodos dodijelili smo ruti / todos . Dakle, svi API pozivi na ovoj ruti izvršit će se putem funkcije getAllTodos. Sada idite na todos.jsdatoteku u direktoriju API-ja i ovdje ćemo napisati funkciju 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); }

Ovdje smo proglasili uzorak JSON objekta. Kasnije ćemo to izvesti iz Firestorea. Ali zasad ćemo ovo vratiti. Sada rasporedite ovo na svoju funkciju firebase pomoću naredbe firebase deploy. Pitat ćeza dopuštenje za brisanje modula helloworld - samo unesite 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

Kad je to gotovo, idite na Project Console> Functions i tamo ćete pronaći URL API-ja. API će izgledati ovako:

//-todoapp-.cloudfunctions.net/api

Sada idite u preglednik i kopirajte-zalijepite URL i dodajte / todos na kraju ovog URL-a. Dobit ćete sljedeći izlaz:

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

Firebase Firestore:

Koristiti ćemo firebase firestore kao bazu podataka u stvarnom vremenu za našu aplikaciju. Sada idite na Konzola> Baza podataka u Firebase konzoli. Da biste konfigurirali firestore, slijedite gif u nastavku:

Nakon što je konfiguracija je učinio a zatim kliknite na Početak prikupljanja gumb i postavite Collection ID kao Todos . Pritisnite Next i dobit ćete sljedeći skočni prozor:

Zanemarite ključ DocumentID. Za polje, vrstu i vrijednost pogledajte JSON dolje. Ažurirajte vrijednost u skladu s tim:

{ 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 }

Pritisnite gumb za spremanje. Vidjet ćete da su zbirka i dokument stvoreni. Vratite se u lokalno okruženje. Moramo instalirati firebase-adminkoji ima firestore paket koji nam treba. Koristite ovu naredbu da biste je instalirali:

npm i firebase-admin

Stvorite direktorij nazvan util ispod direktorija funkcija .Idite u ovaj direktorij i stvorite naziv datoteke admin.js. U ovu datoteku uvest ćemo firebase admin paket i inicijalizirati objekt baze podataka firestore. Izvest ćemo ovo kako bi ga mogli koristiti drugi moduli .

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

Sad napišimo API za dohvaćanje ovih podataka. Idite u todos.jspod direktorij funkcije> API-ji . Uklonite stari kôd i kopirajte i zalijepite donji kod:

//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}); }); };

Ovdje dohvaćamo sve zadatke iz baze podataka i prosljeđujemo ih klijentu na popisu.

Aplikaciju možete pokrenuti i lokalno pomoću firebase servenaredbe, umjesto da je svaki put rasporedite . Kada pokrenete tu naredbu, možete dobiti pogrešku u vezi s vjerodajnicama. Da biste ga popravili, slijedite dolje navedene korake:

  1. Idite na Postavke projekta (ikona Postavke u gornjem lijevom dijelu)
  2. Idite na karticu računa usluga  
  3. Dolje će biti opcija generiranja novog ključa . Kliknite tu opciju i ona će preuzeti datoteku s JSON nastavkom.
  4. Te vjerodajnice moramo izvesti u našu sesiju naredbenog retka. Upotrijebite donju naredbu da biste to učinili:
export GOOGLE_APPLICATION_CREDENTIALS="/home/user/Downloads/[FILE_NAME].json"

Nakon toga pokrenite naredbu firebase serve. Ako i dalje dobivate pogrešku zatim koristite sljedeću naredbu: firebase login --reauth. Otvorit će stranicu za prijavu na Google u pregledniku. Nakon što se prijava izvrši, ona će raditi bez ikakvih pogrešaka.

URL ćete pronaći u zapisnicima vašeg alata za naredbene retke kada pokrenete naredbu za posluživanje firebasea. Otvorite ovaj URL u pregledniku i dodajte /todosnakon njega.

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

U pregledniku ćete dobiti sljedeći JSON izlaz:

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

Pisanje ostalih API-ja:

Vrijeme je da napišemo sve ostale todo API-je koji će nam trebati za našu aplikaciju.

  1. Stvori Todo stavku: Idite u index.jspod direktorij funkcija. Uvezite postOneTodo metodu pod postojećim getAllTodos. Također, toj metodi dodijelite POST rutu.
//index.js const { .., postOneTodo } = require('./APIs/todos') app.post('/todo', postOneTodo);

Idite u todos.jsunutrašnjost direktorija funkcija i dodajte novu metodu postOneTodoprema postojećoj getAllTodosmetodi.

//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); }); };

Ovom metodom dodajemo novo Todo u našu bazu podataka. Ako su elementi našeg tijela prazni, vratit ćemo odgovor od 400 ili ćemo dodati podatke.

Pokrenite naredbu firebase serve i otvorite aplikaciju poštara. Stvorite novi zahtjev i odaberite vrstu metode kao POST . Dodajte URL i tijelo tipa JSON.

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

Pritisnite gumb za slanje i dobit ćete sljedeći odgovor:

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

2. Izbriši stavku Todo: Idite u index.jspod direktorij funkcija. Uvezite metodu deleteTodo pod postojeću postOneTodo. Također, toj metodi dodijelite DELETE rutu.

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

Idite na todos.jsi dodajte novu metodu deleteTodoprema postojećoj postOneTodometodi.

//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 }); }); };

Ovom metodom brišemo Todo iz naše baze podataka. Pokrenite naredbu za služenje vatrogasne baze i idite do poštara. Stvorite novi zahtjev, odaberite vrstu metode kao IZBRIŠI i dodajte URL.

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

Pritisnite gumb za slanje i dobit ćete sljedeći odgovor:

{ "message": "Delete successfull" }

3. Uredi stavku Todo: Idite u index.jspod direktorij funkcija. Uvezite metodu editTodo pod postojećom deleteTodo. Također, toj metodi dodijelite put PUT.

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

Idite na todos.jsi dodajte novu metodu editTodoprema postojećoj deleteTodometodi.

//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 }); }); };

Ovom metodom uređujemo Todo iz naše baze podataka. Zapamtite da ovdje ne dopuštamo korisniku da uređuje polja todoId ili createdAt. Pokrenite naredbu za služenje vatrogasne baze i idite do poštara. Stvorite novi zahtjev, odaberite vrstu metode kao PUT i dodajte URL.

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

Pritisnite gumb za slanje i dobit ćete sljedeći odgovor:

{ "message": "Updated successfully" }

Do sada struktura direktorija:

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

Ovim smo dovršili prvi odjeljak prijave. Možete popiti kavu, predahnuti, a nakon toga radit ćemo na razvoju korisničkih API-ja.

Odjeljak 2: Razvoj korisničkih API-ja

U ovomeodjeljak , razvit ćemo ove komponente:

  1. API za autentifikaciju korisnika (prijava i prijava).
  2. GET i ažuriranje korisničkih podataka API.
  3. Ažurirajte API slike profila korisnika.
  4. Osiguravanje postojećeg Todo API-ja.

Kôd korisničkog API-ja implementiran u ovom odjeljku može se naći na ovom predavanju.

Pa krenimo s izradom API-ja za provjeru autentičnosti korisnika. Idite na Firebase konzolu> Autentifikacija.

Kliknite gumb Postavi način prijave . Za provjeru valjanosti korisnika koristit ćemo e-poštu i lozinku. Omogućite opciju Email / Password .

Trenutno ćemo ručno stvoriti našeg korisnika. Prvo ćemo izgraditi API za prijavu. Nakon toga ćemo izgraditi API za registraciju.

Idite na karticu Korisnici pod Autentifikacija, popunite podatke o korisniku i kliknite gumb Dodaj korisnika .

1. API za prijavu korisnika:

Prvo, moramo instalirati firebasepaket, koji se sastoji od Firebase provjere autentičnosti, pomoću sljedeće naredbe:

npm i firebase

Nakon završetka instalacije idite u direktorij functions> APIs . Ovdje ćemo stvoriti users.jsdatoteku. Sada iznutra index.jsuvozimo metodu loginUser i dodijeljujemo joj POST rutu.

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

Idite na Postavke projekta> Općenito i tamo ćete pronaći sljedeću karticu:

Odaberite web ikonu, a zatim slijedite gif dolje:

Odaberite opciju Nastavi na konzolu . Jednom kad se to učini, vidjet ćete JSON s firebase konfiguracijom. Idite na direktorij functions> util i stvorite   config.jsdatoteku. Kopirajte i zalijepite sljedeći kod u ovu datoteku:

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

Zamijenite ............vrijednostima koje dobijete u Firebase konzoli> Postavke projekta> Općenito> vaše aplikacije> Isječak Firebase SD> config .

Kopirajte i zalijepite sljedeći kod u users.jsdatoteku:

// 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'}); }) };

Ovdje koristimo firebase signInWithEmailAndPassword modul da provjerimo jesu li vjerodajnice koje su poslali korisnici ispravne. Ako su u pravu, šaljemo token tog korisnika ili status 403 s porukom "pogrešne vjerodajnice".

Sada ćemo napraviti validators.jspod funkcija> util imenik. Kopirajte i zalijepite sljedeći kod u ovu datoteku:

// 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 }; };

Ovim je završen naš LoginAPI . Pokrenite firebase servenaredbu i idite do poštara. Stvorite novi zahtjev, odaberite vrstu metode kao POST i dodajte URL i tijelo.

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" }

Pritisnite gumb za slanje zahtjeva u poštaru i dobit ćete sljedeći izlaz:

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

Ovaj ćemo token upotrijebiti u sljedećem dijelu kako bismo dobili korisničke detalje . Zapamtite da ovaj token istječe za 60 minuta . Da biste generirali novi token, ponovno upotrijebite ovaj API.

2. API za registraciju korisnika:

Zadani mehanizam provjere autentičnosti firebase omogućuje vam pohranjivanje samo podataka poput e-pošte, lozinke itd. No, trebamo više informacija kako bismo utvrdili posjeduje li taj korisnik taj zadatak kako bi mogao izvršiti operacije čitanja, ažuriranja i brisanja na njemu.

Da bismo postigli ovaj cilj stvorit ćemo novu kolekciju koja se zove korisnici . U okviru ove zbirke pohranit ćemo podatke korisnika koji će se preslikati u zadaću na temelju korisničkog imena. Svako korisničko ime bit će jedinstveno za sve korisnike na platformi.

Idite na index.js. Uvezemo metodu signUpUser i dodijelimo joj POST rutu.

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

Sada idite na validators.jsi dodajte sljedeći kod ispod validateLoginDatametode.

// 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 }; };

Sada idite na users.jsi dodajte sljedeći kod ispod loginUsermodula.

// 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' }); } }); }

Provjeravamo svoje korisničke podatke, a nakon toga šaljemo e-poštu i lozinku u modul firebase createUserWithEmailAndPassword za stvaranje korisnika. Nakon što se korisnik uspješno kreira, spremamo vjerodajnice korisnika u bazu podataka.

Ovim je završen naš API za prijavu . Pokrenite firebase servenaredbu i idite do poštara. Stvorite novi zahtjev, odaberite vrstu metode kao POST . Dodajte URL i tijelo.

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" }

Pritisnite dugme za slanje zahtjeva u poštaru i dobit ćete sljedeći izlaz:

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

Sada idite na Firebase konzolu> Baza podataka i tamo ćete vidjeti sljedeći izlaz:

Kao što vidite, zbirka našeg korisnika uspješno je stvorena s jednim dokumentom.

3. Učitaj sliku profila korisnika:

Naši će korisnici moći učitati svoju sliku profila. Da bismo to postigli, koristit ćemo kantu za pohranu. Idite na Firebase konzolu> Pohrana i kliknite gumb Početak . Slijedite GIF u nastavku za konfiguraciju:

Sada idite na karticu Pravila u odjeljku Pohrana i ažurirajte dozvolu za pristup segmentu prema slici u nastavku:

Za učitavanje slike profila koristit ćemo paket s imenom busboy. Da biste instalirali ovaj paket, upotrijebite sljedeću naredbu:

npm i busboy

Idite na index.js. Uvezite metodu uploadProfilePhoto ispod postojeće metode signUpUser. Također dodijelite POST rutu toj metodi.

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

Ovdje smo dodali sloj za provjeru autentičnosti tako da samo korisnik povezan s tim računom može prenijeti sliku. Sada stvorite datoteku nazvanu auth.jsu direktoriju functions> utils . Kopirajte i zalijepite sljedeći kod u tu datoteku:

// 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); }); };

Ovdje koristimo modul firebase verifyIdToken za provjeru tokena. Nakon toga dekodiramo korisničke podatke i prosljeđujemo ih u postojeći zahtjev.

Idite na users.jsi dodajte sljedeći kod ispod signupmetode:

// 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); };

Ovim je završen naš API za prijenos slike profila . Pokrenite firebase servenaredbu i idite do poštara. Stvorite novi zahtjev, odaberite vrstu metode kao POST , dodajte URL, a u odjeljku tijela odaberite vrstu kao obrazac-podaci.

Zahtjev je zaštićen pa ćete morati poslati i token na donositelja . Da biste poslali token na donositelja, prijavite se ponovo ako je token istekao. Nakon toga u Postman App> kartica Autorizacija> Type> Bearer Token i u odjeljak tokena zalijepite token.

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

Pritisnite dugme za slanje zahtjeva u poštaru i dobit ćete sljedeći izlaz:

{ "message": "Image uploaded successfully" }

4. Dohvatite podatke o korisniku:

Ovdje dohvaćamo podatke našeg korisnika iz baze podataka. Idite na index.jsmetodu getUserDetail i uvezite joj GET rutu.

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

Sada idite na users.jsi dodajte sljedeći kod nakon uploadProfilePhotomodula:

// 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 }); }); }

Za izvođenje korisničkih podataka koristimo modul firebase doc (). Get () . Ovim je završen naš GET API za detalje o korisniku . Pokrenite firebase servenaredbu i idite do poštara. Stvorite novi zahtjev, odaberite vrstu metode: GET i dodajte URL i tijelo.

Zahtjev je zaštićen pa ćete morati poslati i token na donositelja . Da biste poslali token na donositelja, prijavite se ponovo ako je token istekao.

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

Pritisnite dugme za slanje zahtjeva u poštaru i dobit ćete sljedeći izlaz:

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

5. Ažuriranje korisničkih podataka:

Sad dodajmo funkcionalnost za ažuriranje korisničkih podataka. Idite na index.jsi kopirajte i zalijepite sljedeći kod:

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

Sada idite na users.jsi dodajte updateUserDetailsmodul ispod postojećeg 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" }); }); }

Ovdje koristimo metodu ažuriranja firebasea . Ovim je završen naš API za ažuriranje korisničkih podataka . Slijedite isti postupak za zahtjev kao s gornjim API-jem za dobivanje podataka o korisniku s jednom izmjenom. Ovdje dodajte tijelo u zahtjev i metodu kao 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 }

Pritisnite dugme za slanje zahtjeva u poštaru i dobit ćete sljedeći izlaz:

{ "message": "Updated successfully" }

6. Osiguravanje Todo API-ja:

Da bismo osigurali Todo API tako da mu samo odabrani korisnik može pristupiti, napravit ćemo nekoliko promjena u našem postojećem kodu. Prvo ćemo ažurirati index.jskako slijedi:

// 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);

Ažurirali smo sve Todo rute dodavanjem authtako da će svi API pozivi zahtijevati token i pristupiti im može samo određeni korisnik.

Nakon toga idite na todos.jspod direktorij functions> APIs .

  1. Izradite Todo API: Otvorite todos.jsi ispod metode postOneTodo dodajte ključ korisničkog imena na sljedeći način:
const newTodoItem = { .., username: request.user.username, .. }

2. GET All Todos API: Otvorite todos.jsi pod getAllTodos metodom dodajte klauzulu where kako slijedi:

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

Pokrenite firebase servis i testirajte naš GET API. Ne zaboravite poslati žeton na donositelja. Ovdje ćete dobiti pogrešku odgovora kako slijedi:

{ "error": 9 }

Idite na naredbeni redak i vidjet ćete sljedeće redove zabilježene:

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

Otvori ovo u pregledniku i kliknite na create index.

Jednom kada je indeks izgrađen, pošaljite zahtjev ponovo i dobit ćete sljedeći izlaz:

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

3.   Izbriši Todo API: Otvorite todos.jsi ispod metode deleteTodo dodajte sljedeći uvjet. Dodajte ovaj uvjet unutar dokumenta document.get (). Then () ispod uvjeta ! Doc.exists .

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

Dosadašnja struktura direktorija:

+-- 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

Ovim smo dovršili naš API pozadinu. Napravite pauzu, popijte kavu i nakon toga započet ćemo graditi prednji kraj naše aplikacije

Odjeljak 3: Nadzorna ploča korisnika

U ovomeodjeljak , razvit ćemo ove komponente:

  1. Konfigurirajte ReactJS i korisničko sučelje.
  2. Izgradnja obrasca za prijavu i prijavu.
  3. Odjeljak za izradu računa.

Kôd nadzorne ploče korisnika implementiran u ovom odjeljku može se naći na ovom urezivanju.

1. Konfigurirajte ReactJS i korisničko sučelje:

Upotrijebit ćemo predložak za stvaranje-reagiranje-aplikacije. Daje nam temeljnu strukturu za razvoj aplikacije. Da biste ga instalirali, upotrijebite sljedeću naredbu:

npm install -g create-react-app

Idite u korijensku mapu projekta u kojoj je prisutan direktorij funkcija. Inicijalizirajte našu prednju aplikaciju pomoću sljedeće naredbe:

create-react-app view

Ne zaboravite koristiti verziju v16.13.1 odknjižnica ReactJS .

Po završetku instalacije u zapisnicima naredbenog retka vidjet ćete sljedeće:

cd view npm start Happy hacking!

Ovim smo konfigurirali našu React aplikaciju. Dobit ćete sljedeću strukturu direktorija:

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

Sada pokrenite aplikaciju pomoću naredbe npm start. Idite na preglednik //localhost:3000/i vidjet ćete sljedeći izlaz:

Sada ćemo ukloniti sve nepotrebne komponente. Idite u direktorij pogleda, a zatim uklonite sve datotekekoji imaju [Ukloni] ispred sebe. Za to pogledajte strukturu stabla direktorija u nastavku.

+-- 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

Idite na index.htmlpod javni direktorij i uklonite sljedeće retke:

Sada idite na App.jspod direktorij src i zamijenite stari kod sljedećim kodom:

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

Idite na index.jsi uklonite sljedeći uvoz:

import './index.css'

Nisam izbrisao App.cssniti ga koristim u ovoj aplikaciji. Ali ako ga želite izbrisati ili koristiti, slobodno to možete učiniti.

Idite na preglednik //localhost:3000/i dobit ćete izlaz za prazan zaslon.

Da biste instalirali UI materijala, idite u direktorij pogleda i kopirajte-zalijepite ovu naredbu u terminal:

npm install @material-ui/core

Ne zaboravite koristiti verziju v4.9.8 biblioteke UI materijala.

2. Obrazac za prijavu:

Da biste razvili obrazac za prijavu, idite na App.js. Na vrh App.jsdodajte sljedeći uvoz:

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

Koristimo Switch and Route za dodjeljivanje ruta za naš TodoApp. Trenutno ćemo dodati samo / login rutu i dodijeliti joj komponentu za prijavu.

// App.js 

Stvorite direktorij stranica u postojećem direktoriju pogleda i datoteku nazvanu login.jsispod direktorija stranica .

Uvozit ćemo komponente UI materijala i paket Axios u 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';

Na našu stranicu za prijavu dodati ćemo sljedeće stilove:

// 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' } });

Stvorit ćemo klasu koja se zove prijava koja ima obrazac i u nju će poslati rukovatelj.

// 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}  )} ); } }

Na kraju ove datoteke dodajte sljedeći izvoz:

export default withStyles(styles)(login); 

Dodajte URL naših Firebase funkcija za prikaz> package.json kako slijedi:

Zapamtite: Dodajte ključ s imenom proxy ispod postojećeg objekta JSON popisa preglednika
"proxy": "//-todoapp-.cloudfunctions.net/api"

Instalirajte paket Axios i ikonu materijala pomoću sljedećih naredbi:

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

Dodali smo put za prijavu u App.js. U login.jsizradili smo komponentu klase koja obrađuje stanje, šalje zahtjev za objavu API-ju za prijavu pomoću paketa Axios. Ako je zahtjev uspješan, tada pohranjujemo token. Ako u odgovoru dobijemo pogreške, jednostavno ih prikažemo na korisničkom sučelju.

Idite na preglednik na adresi //localhost:3000/logini vidjet ćete sljedeće korisničko sučelje za prijavu.

Pokušajte popuniti pogrešne vjerodajnice ili poslati prazan zahtjev i dobit ćete pogreške. Pošaljite valjani zahtjev. Idite na Developer console> Application . Vidjet ćete da se token korisnika sprema u lokalnu pohranu. Nakon uspješne prijave, vratit ćemo se na početnu stranicu.

3. Obrazac za prijavu:

Da biste razvili obrazac za prijavu, idite na App.jsi ažurirajte postojeću Routekomponentu pomoću donjeg retka:

// App.js 

Ne zaboravite uvesti:

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

Stvorite datoteku nazvanu signup.jsispod direktorija stranica .

Unutar signup.js uvozit ćemo Material UI i Axios paket:

// 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';

Na našu stranicu za prijavu dodati ćemo sljedeće stilove:

// 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' } }); 

Stvorit ćemo klasu koja se zove prijava koja ima obrazac i u nju će poslati rukovatelj.

// 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 ); } }

Na kraju ove datoteke dodajte sljedeći izvoz:

export default withStyles(styles)(signup); 

Logika komponente Signup jednaka je komponenti prijave. Idite na preglednik na adresi //localhost:3000/signupi vidjet ćete sljedeće korisničko sučelje za prijavu. Nakon uspješne registracije, vratit ćemo se na početnu stranicu.

Pokušajte popuniti pogrešne vjerodajnice ili poslati prazan zahtjev i dobit ćete pogreške. Pošaljite valjani zahtjev. Idite na Developer console> Application . Vidjet ćete da se token korisnika sprema u lokalnu pohranu.

4. Odjeljak računa:

Da bismo izgradili stranicu računa, prvo ćemo morati stvoriti našu početnu stranicu odakle ćemo učitati odjeljak računa . Idite na App.jsi ažurirajte sljedeću rutu:

// App.js 

Ne zaboravite na uvoz:

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

Stvorite novu datoteku s imenom home.js. Ova će datoteka biti indeks naše prijave. Odjeljak Račun i Todo učitavaju se na ovoj stranici na temelju klika na gumb.

Uvezite pakete korisničkog sučelja za materijal, paket Axios, naš prilagođeni račun, sve komponente i autorski međuopreme.

// 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'

Širinu ladice postavit ćemo na sljedeći način:

const drawerWidth = 240;

Na našu početnu stranicu dodat ćemo sljedeći stil:

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 });

Stvorit ćemo razred s imenom dom. Ova će klasa imati API poziv za dobivanje slike profila korisnika, imena i prezimena. Također će imati logiku odabrati komponentu za prikaz, Todo ili Account:

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 ? : } ); } } }

Ovdje u kodu vidjet ćete da authMiddleWare(this.props.history);se koristi. Ovaj međuprogram provjerava je li authToken null. Ako je odgovor da, onda će korisnika gurnuti natrag na login.js. To je dodano tako da naš korisnik ne može pristupiti /ruti bez prijave ili prijave. Na kraju ove datoteke dodajte sljedeći izvoz:

export default withStyles(styles)(home); 

Sad se pitate što ovaj kod home.jsradi?

 {this.state.render ?  : } 

Provjerava se stanje prikaza koje postavljamo klikom na gumb. Stvorimo direktorij komponenata, a ispod tog direktorija stvorimo dvije datoteke: account.jsi todo.js.

Stvorimo direktorij s imenom util i datoteku nazvanu auth.jspod tim direktorijumom. Kopirajte i zalijepite sljedeći kod pod auth.js:

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

Za vrijeme boravka u todo.jsdatoteku samo ćemo napisati klasu koja prikazuje tekst Zdravo, ja sam todo . Radit ćemo na našim zadacima u sljedećem odjeljku:

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));

Sada je vrijeme za odjeljak o računu. Uvezite uslužni program Material UI, clsx, axios i authmiddleWare u naš 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';

Na našu stranicu računa dodat ćemo sljedeći styling:

// 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' } });

Stvorit ćemo komponentu klase s imenom račun. Za sada samo kopirajte i zalijepite sljedeći kod:

// 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}  

Na kraju ove datoteke dodajte sljedeći izvoz:

export default withStyles(styles)(account); 

U account.jstu su puno komponenti koje se koriste. Prvo da vidimo kako izgleda naša aplikacija. Nakon toga objasnit ću sve komponente koje se koriste i zašto se koriste.

Idite na preglednik i ako je vaš token istekao, preusmjerit će vas na   loginstranicu. Dodajte svoje podatke i prijavite se ponovo. Kada to učinite, idite na karticu Račun i pronaći ćete sljedeće korisničko sučelje:

U odjeljku računa postoje 3 voditelja:

  1. componentWillMount : Ovo je ugrađena Reactova metoda životnog ciklusa. Koristimo ga za učitavanje podataka prije životnog ciklusa generiranja i ažuriranje vrijednosti stanja.
  2. ProfilePictureUpdate: Ovo je naš prilagođeni rukovatelj koji koristimo tako da kada naš korisnik klikne na gumb Prenesi fotografiju, on će poslati podatke na poslužitelj i ponovo učitati stranicu kako bi prikazao novu korisničku sliku profila.
  3. updateFormValues: Ovo je ujedno i naš prilagođeni rukovatelj za ažuriranje korisničkih podataka. Ovdje korisnik može ažurirati svoje ime, prezime i državu. Ne dopuštamo ažuriranje e-pošte i korisničkog imena jer naša pozadinska logika ovisi o tim ključevima.

Osim ova 3 rukovatelja, to je stranica obrasca sa stilom na vrhu. Evo strukture direktorija do ove točke unutar mape pogleda:

+-- 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

Ovim smo dovršili našu nadzornu ploču računa. Sada idite na kavu, napravite pauzu i u sljedećem ćemo odjeljku napraviti Todo nadzornu ploču.

Odjeljak 4: Todo nadzorna ploča

U ovomepoglavlje , reći ćemo razviti sučelje za te značajke Todos nadzornoj ploči:

  1. Dodajte zadatak:
  2. Dohvatite sve zadatke:
  3. Izbrišite zadatak
  4. Uredite zadatak
  5. Nabavite zadatak
  6. Primjena teme

Kôd nadzorne ploče Todo implementiran u ovom odjeljku može se naći na ovom urezivanju.

Idite u todos.jspod direktorij komponenata . Sljedećem uvozu dodajte postojeći uvoz:

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';

Također trebamo dodati sljedeće CSS elemente u postojeće komponente stila:

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] } });

Dodati ćemo prijelaz za skočni dijaloški okvir:

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

Uklonite postojeću klasu todo i kopirajte i zalijepite sljedeću klasu:

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}       ); } } }

Na kraju ove datoteke dodajte sljedeći izvoz:

export default withStyles(styles)(todo); 

Prvo ćemo razumjeti kako funkcionira naše korisničko sučelje, a nakon toga ćemo razumjeti kod. Idite na preglednik i dobit ćete sljedeće korisničko sučelje:

Kliknite gumb Dodaj u donjem desnom kutu i dobit ćete sljedeći zaslon:

Dodajte naslov i detalje Todoa i pritisnite gumb za slanje. Dobit ćete sljedeći zaslon:

Nakon ovoga kliknite gumb za prikaz i moći ćete vidjeti sve detalje o Todo:

Kliknite gumb Uredi i moći ćete urediti zadatak:

Kliknite gumb za brisanje i moći ćete izbrisati Todo. Sad kad smo svjesni kako nadzorna ploča radi, razumjet ćemo komponente koje se na njoj koriste.

1. Add Todo: Za implementaciju add todoa koristit ćemo dijalošku komponentu Material UI. Ova komponenta implementira funkcionalnost kuke. Koristimo klase pa ćemo ukloniti tu funkcionalnost.

// 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 }); };

Osim ovoga, promijenit ćemo i položaj gumba Add Todo.

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

Sada ćemo oznaku popisa zamijeniti obrascem unutar ovog Dijaloga. Pomoći će nam u dodavanju novog zadatka.

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

ThehandleSubmitsastoji se od logike za očitavanje buttonTypestanja. Ako je stanje prazan niz (“”), objavit će se na Add Todo API. Ako je država Edittada u tom scenariju, ažurirat će Edit Todo.

2. Nabavite Todos: Za prikaz todos-a koristit ćemo Grid containeri unutar njega postavljamo Grid item. Unutar toga koristit ćemo Cardkomponentu za prikaz podataka.

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

Mapu koristimo za prikaz stavke zadataka dok ih API šalje na popis. Upotrijebit ćemo životni ciklus componentWillMount za dobivanje i postavljanje stanja prije izvođenja rendera. Postoje 3 gumba ( prikaz, uređivanje i brisanje ), tako da će nam trebati 3 rukovatelja za rukovanje operacijom kada se klikne na gumb. O tim ćemo gumbima saznati u njihovim pododjeljcima.

3. Uredi zadatak: Za zadatak uređivanja ponovno koristimo iskačući kôd dijaloga koji se koristi u dodatku zadataka. Da bismo razlikovali klikove na gumb, koristimo buttonTypestanje. Za Add Todo   buttonTypestanje je (“”)dok je za edit todo to Edit.

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

U handleSubmitmetodi očitamo buttonTypestanje, a zatim u skladu s tim šaljemo zahtjev.

4. Delete Todo: Kada se klikne na ovaj gumb, šaljemo todo objekt našem deleteTodoHandler, a zatim šalje zahtjev dalje na pozadinu.

 this.deleteTodoHandler({ todo })}>Delete

5. Pregled zadatka: Prikazujući podatke, mi smo ih skratili tako da će korisnik dobiti uvid u to o čemu se radi. Ali ako korisnik želi znati više o tome, onda mora kliknuti gumb za prikaz.

Za to ćemo upotrijebiti Prilagođeni dijalog. Unutar toga koristimo DialogTitle i DialogContent. Prikazuje naš naslov i sadržaj. U DialougeContent koristit ćemo obrazac za prikaz sadržaja koji je korisnik objavio. (Ovo je jedno rješenje za koje sam otkrio da postoji mnogo, a slobodno možete isprobati i drugo.)

// 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. Primjena teme: Ovo je zadnji korak naše prijave. Primijenit ćemo temu na našu aplikaciju. Za to koristimo createMuiThemei ThemeProvideriz korisničkog sučelja materijala. Kopirajte i zalijepite sljedeći kod u 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.  ); }

Propustili smo primjenom teme u našoj gumb u todo.jsu CardActions. Dodajte oznaku boje za gumb za prikaz, uređivanje i brisanje.

Idite u preglednik i vidjet ćete da je sve isto, osim što je aplikacija druge boje.

I gotovi smo! Izradili smo TodoApp koristeći ReactJS i Firebase. Ako ste ga izgradili sve do ove točke, onda vam jako čestitam na ovom postignuću.

Slobodno se povežite sa mnom na Twitteru i Githubu.