Lect. univ. dr. MARIETA GÂTA [611705]

UNIVERSITATEA  TEHNICĂ DIN CLUJ‐NAPOCA
CENTRUL  UNIVERSITAR  NORD DIN BAIA MARE
FACULTATEA  DE ȘTIINȚE
SPECIALIZAREA  INFORMATIC Ă 
APLICA ȚIE WEB DE 
PUBLICITATE  
LUCRARE  DE LICENȚĂ 
Absolvent: [anonimizat]:
Lect. univ. dr. MARIETA  GÂTA 
 
Baia Mare 
2018

APLICA ȚIE WEB DE PUBLICITATE  
 
2

APLICA ȚIE WEB DE PUBLICITATE  
 

  Cuprins
Introducere  ……………………………………………………………………………………………………..  5 
Tehnologii  si limbaje folosite ………………………………………………………………………………  6 
1.1. Angular …………………………………………………………………………………………………  6 
1.2. Ngrx/Store  …………………………………………………………………………………………….  9 
1.3. TypeScript  ……………………………………………………………………………………………  11 
1.4. Pug ……………………………………………………………………………………………………..  12 
1.5. SCSS ……………………………………………………………………………………………………  13 
1.6. NodeJS ………………………………………………………………………………………………..  14 
1.7. MongoDB  …………………………………………………………………………………………….  15 
Descrierea  Aplicației ……………………………………………………………………………………….  16 
2.1. Prezentare  ……………………………………………………………………………………………  16 
2.1.1. Client …………………………………………………………………………………………….  16 
2.1.2. Server ……………………………………………………………………………………………  17 
2.1.3. Baza de date …………………………………………………………………………………..  18 
2.2. Arhitectura  …………………………………………………………………………………………..  18 
2.2.1. Client …………………………………………………………………………………………….  18 
2.2.2. Server ……………………………………………………………………………………………  20 
2.2.3. Baza de date …………………………………………………………………………………..  27 
2.2.3.1. User ……………………………………………………………………………………………  28 
2.2.3.2. Anunt …………………………………………………………………………………………  30 
2.2.3.3. Atribut ………………………………………………………………………………………..  31 
2.2.3.4. Atribut_detaliu  …………………………………………………………………………….  33 
2.2.3.5. Categorie  …………………………………………………………………………………….  34 
2.2.3.6. Subcategorie  ……………………………………………………………………………….  34 
2.2.3.7. Convo …………………………………………………………………………………………  35 
2.2.3.8. Message ……………………………………………………………………………………..  36 
2.2.3.9. Imagine ………………………………………………………………………………………  37 
2.2.3.10 Judet …………………………………………………………………………………………  38 
2.2.3.11 Localitate  ……………………………………………………………………………………  38

APLICA ȚIE WEB DE PUBLICITATE  
 

  2.2.3.12 Role …………………………………………………………………………………………..  39
 
2.2.3.13 Valuta ………………………………………………………………………………………..  40 
Aplicația ………………………………………………………………………………………………………..  42 
3.1. Autentificare,  Autorizare  și Înregistrare  ……………………………………………………  42 
3.2. Panoul de administrator  ………………………………………………………………………..  45 
3.2.1 Gestionarea  resurselor  …………………………………………………………………………  45 
3.3. Clientul ………………………………………………………………………………………………..  51 
3.3.1. Acasă ………………………………………………………………………………………………..  52 
3.3.2. Anunț Nou …………………………………………………………………………………………  54 
3.3.3. Anunțuri ……………………………………………………………………………………………  58 
3.3.4. Detalii anunț ……………………………………………………………………………………..  62 
3.3.5. Mesageria  Instantă …………………………………………………………………………….  65 
3.3.6. Anunțurile Tale …………………………………………………………………………………..  67 
3.3.7. Setări ………………………………………………………………………………………………..  68 
Concluzii ……………………………………………………………………………………………………….  69 
4.1 Dezvoltări în viitor ………………………………………………………………………………….  69 
Bibliografie  …………………………………………………………………………………………………….  70

APLICA ȚIE WEB DE PUBLICITATE  
 

  Introducere
 
  În ultimul deceniu mica publicitate,  ca majoritatea  formelor  de media 
scrisa, s‐a extins pe internet.  La început doar ziarele online au oferit acest 
serviciu însă în curând au apărut noi grupuri pe piața, realizând  potențialul 
acestei nișe.  
  Aplicațiile online de mica publicitate  nu pretind un „cost pe linie” pentru 
anunțurile publicate,  utilizatorii  având posibilitatea  de a își descrie produsele  
date spre vânzare în detaliu. De asemenea  platforma  pune la dispoziția 
utilizatorilor  anunțuri de pe o arie geografica  largă, fiind vorba adesea de o țara 
întreaga.  Un alt avantaj major este faptul ca utilizatorul  poate filtra foarte ușor 
anunțurile de pe platform ă după anumite caracteristici/atribute  de care este 
interesat.  El are posibilitatea  de a introduce  termeni de căutare care se pot regăsi 
în titlul sau descrierea  unui anunț, acest lucru fiind imposibil  când vine vorba 
de mica publicitate  regăsita în ziarele fizice. 
  Aplicația creată de mine are ca scop implementarea  acestor avantaje într‐
un mod care duce la o experien ță cât mai placută în timpul utilizării și aduce un 
plus de funcționalitate,  care lipsește din platformele  active în domeniu.

APLICA ȚIE WEB DE PUBLICITATE  
 

  Capitolul 1
Tehnologii si limbaje folosite
 
1.1. Angular
  
  Angular este o platforma  destinat dezvoltării aplicațiilor web de  tip 
,,single page”, pe partea de client. Este dezvoltat ă de echipa Angular de la 
Google împreun ă cu o comunitate  de dezvoltatori  independen ți și este o 
rescriere  completă a primei versiuni,  AngularJS  . 
  Folosește ca limbaj de dezvoltare  o versiune  proprie a limbajul 
TypeScript,  numită “TypeScript  with Decorators”.  Angular implementeaz ă 
funcționalități de bază și opționale sub forma unui set de librării TypeScript  ce 
se pot importa în aplicații. 
  La baza unei aplicații Angular stă modulul.  Un modul declară un context, 
folosit în faza de compilare,  pentru un set de componente  dedicate unui 
domeniu  al aplicației, unui „workflow”  sau unui set de capabilit ăți strâns 
legate. Un modul asociază componentele  care fac parte din el cu alte elemente,  
precum serviciile,  pentru a forma unități funcționale.  O aplicație Angular este 
definită de un set de module.  Fiecare aplicație Angular are cel puțin un modul 
de baza, numit AppModule,   pentru a oferi un punct de pornire, și de obicei 
importă mai multe module pentru diferitele  funcționalități pe care le oferă 
aplicația. Similar cu modulele  de JavaScript,  modulele  de Angular pot exporta

APLICA ȚIE WEB DE PUBLICITATE  
 

  funcționalitate  iar aceasta poate fi importat ă  în alte module, stând la dispoziția 
componentelor  care alcătuiesc modulul  respectiv.  Organizarea  codului în 
module distincte  și independente,  bazate pe funcționalitate,  ajută la dezvoltarea  
aplicațiilor complexe  și mentenan ța acestora.  Această separare  a funcționalității 
ne permite
 să folosim “lazy‐loading”  pentru a minimiza  cantitatea  de cod care 
se încarcă în memorie  în momentul  pornirii aplicației. Elementele  care nu țin de 
secțiunea aplicației care se acceseaz ă fiind ignorate.  
  Componentele  definesc view‐uri, reprezentând  elementele  pe care le 
vede utilizatorul.  Angular poate manipula  aceste elementele  în funcție de logica 
din cod și datele furnizate.  Acestea folosesc servicii, care pun la dispoziție 
funcționalități bine definite, care nu țin însă de componente.  Serviciile  se pot 
injecta în componente  ca dependin țe, ducând la modularitatea  și eficiența 
codului. Fiecare aplicație are cel puțin o component ă, o component ă de bază, 
care realizeaz ă conexiunea  dintre ierarhia de componente  Angular și DOM‐ul 
paginii. Fiecare component ă definește o clasă de TypeScript,  care conține date 
și logică, și este asociată cu un template  de HTML care reprezint ă elementele  
propriu‐zise care vor fi afișate utilizatorului.  Clasa este decorată cu decoratorul  
@Component  care îl identifica  și furnizeaz ă metadatele  necesare  în faza de 
compilare.  
  Template ‐urile combină sintaxa HTML cu sintaxa Angular,  care poate  
manipula  elementele  HTML înainte ca acestea să fie afișate. Directivele  pun la 
dispoziție logica programabil ă iar sintaxa de data binding conecteaz ă datele 
aplicației și DOM‐ul.  „Event binding”  permite aplicației sa reacționeze la 
inputul utilizatorului,  actualizând  datele aplicației. „Property  binding”  ne 
permite să interpolăm valori din datele componentelor  direct în HTML. Înainte

APLICA ȚIE WEB DE PUBLICITATE  
 

  ca un template  să fie afișat utilizatorului,  Angular evalueaz ă directivele  și 
proceseaz ă sintaxa de binding pentru a modifica  elementele  corespunz ătoare 
din DOM, în concordan ță cu logica și datele aplicației. De asemenea,  Angular 
ne pune la dispoziție „two‐way data binding”,  ceea ce înseamnă că modific
ările 
făcute la nivelul DOM‐ului, precum decizii ale utilizatorului,  sunt reflectate  în 
timp real în datele aplicației. Template ‐urile pot folosi „pipes” care transforma  
valorile menite afișării. Un pipe se folosește, de exemplu,  în cazul în care dorim 
să afișăm o dată formatat ă în funcție de locația utilizatorului  fără să modificam  
data în mod direct, doar în afișaj. Angular vine cu un set de pipes predefinite  
însă putem defini cu ușurința un pipe nou. 
  Pentru logica care nu este asociată cu un view anume, și pe care dorim să 
o folosim în mai multe componente  sau servicii se folosește o clasă de tip service. 
O clasă de tip service este decorată cu decoratorul  @Injectable  dacă se dorește 
injectarea  dependin țelor în aceasta. În lipsa decoratorului  injectarea  
dependin țelor în serviciu nu este posibilă și va genera o eroare in momentul  
rulării aplicației. „Dependency  injection”  duce la minimizarea  codul din 
componente  și previne repetarea  redundant ă a secvențelor de cod similare,  
delegând  serviciilor  acțiuni folosite des precum cereri HTTP către server, 
validarea  inputului  de la utilizator,  tratarea erorilor etc. 
  Modulul  Router ne pune la dispoziție un serviciu care ne permite să 
definim rutele de navigare  pentru diferitele  stări ale aplicației. Acest Router 
creează legătura dintre o cale URL și view‐urile din aplicație. Pentru a definii 
reguli de navigare,  asociem căi de navigare  cu componentele  din aplicație. 
Odată definite, putem aplica logica pentru a afișa/ascunde  aceste rute sau 
pentru restricționarea accesul la ele. Când un utilizator  interacționează cu

APLICA ȚIE WEB DE PUBLICITATE  
 

  pagina într‐un mod care ar duce la deschiderea  unei pagini noi în browser,  
precum click‐ul pe un link, router‐ul intercepteaz ă comportamentul  standard  al 
browserului  și, în schimb, afișează sau ascunde view‐uri. Dacă router‐ul 
stabilește că starea curenta al aplicației necesită o
 funcționalitate  anume, iar 
modulul  care definește funcționalitatea  respectiv ă nu a fost încărcat, router‐ul 
poate încărca modulul  respectiv.  Acest proces se numește „lazy‐loading”.   
Routerul  introduce  înregistrări în istoricul de navigare  a browserului,  astfel 
încât butoanele  de înapoi și înainte să funcționeze in mod obișnuit. 
 
1.2. Ngrx/Store
 
  O problemă majora cu care se confrunt ă aplicațiile „single page” este 
faptul că atunci când acestea devin foarte complexe  gestiunea  datelor devine  
foarte dificilă. Când vine vorba de ecosistemul  Angular,  această dificultate  se 
trage din faptul că Angular ne permite să facem „two‐way data binding”,  
modelul din controller  fiind strâns legat de modelul din view și vice‐versa 
Această legătură înseamnă că orice modificare  efectuata  de către utilizator  
asupra datelor din view va duce automat la modificarea  datelor 
corespunz ătoare din controller.  Odată cu creșterea complexit ății datelor aceste 
modificări pot avea efecte secundare  nedorite,  o modificare  având potențialul 
să provoace,  pe cont propriu, o altă modificare.  
  Una dintre soluțiile pentru această problemă este Ngrx/Store.  Store 
funcționează pe baza unei „singure  surse de adevăr”, un loc central și unic unde 
sunt stocate datele aplicației. Orice modificare  a datelor se face doar prin

APLICA ȚIE WEB DE PUBLICITATE  
 
10 
  intermediul  unor acțiuni bine definite iar datele stocate în Store respectă 
conceptul  de imutabilitate,  modificarea  lor în mod direct fiind imposibil ă. Ca 
urmare, orice modificare  se poate supraveghea  cu ușurința în faza de 
dezvoltare.  
      
 
  
Figura 1.2.1 Diferența dintre „two‐way data binding”  și Store 
 
  O altă problemă pe care o rezolvă Store este cea de comunicare  între 
componente.  Dacă ar fi să ne folosim de funcționalitatea  de bază pe care ne‐o 
pune la dispoziție Angular,  pentru a realiza comunicarea  dintre doua 
componente  este necesară o legătură „event emitter ‐ event listener”.  Această 
legătură este ușor de realizat atunci când avem o pereche de componente  cu 
relația ,,părinte‐copil,, dar devine foarte dificilă atunci când componentele  sunt 
separate din punct de vedere ierarhic. Store rezolvă această problemă  prin 
faptul că ne pune la dispoziție un serviciu injectabil.  Acest serviciu este injectabil  
în orice component ă din cadrul aplicației iar prin intermediul  său avem acces la

APLICA ȚIE WEB DE PUBLICITATE  
 
11 
  un „Observable”  care emite noua stare a aplicației, stare stocată în Store, de 
fiecare dată când se fac modificări asupra acesteia prin intermediul  acțiunilor. 
Acțiunile care modifică starea din Store pot fi lansate din orice component ă iar 
toate componentele  care „ascultă” pentru modificări
 asupra stării vor primii 
automat noua stare, indiferent  de relația dintre componenta  care a efectuat 
acțiunea si cea care ascultă. 
 
1.3. TypeScript
   TypeScript  este un limbaj de programare  a cărui scop este extinderea  
capabilit ăților limbajului  Java Scrip. TypeScript  oferă typing static clase și 
interfețe. Cel mai mare beneficiu,  atunci când vine vorba de TypeScript,  este 
faptul că acesta verifica corectitudinea  codului în timpul scrierii acestuia.  Acest 
lucru face ca 
erorile cel mai des întâlnite,  precum greșelile de sintaxa, utilizarea  
unei variabile  nedeclarate  sau inaccesibile  etc să prevină compilarea  codului și 
să genereze  mesaje de eroare.  În cazul limbajului  JavaScript,  aceste erori nu vor 
fi depistate  decât în faza de rulare al aplicației. Un alt avantaj major al limbajului  
este faptul că este compatibil  cu marea majoritate  a IDE‐urilor, oferindu ‐ne  
„intellisense”,  ceva ce lipsește în totalitate  când vine vorba de JavaScript.  
  O capabilitate  majoră a limbajului  este cea de typing static. Aceasta ne 
permite să definim explicit tipul unei variabile  și genereaz ă erori în cazul în care 
încercam  să sa stocam într‐o variabilă o valoare incompatibila  sau în cazul în 
care efectuăm un apel de funcție cu parametri  de tip greșit. Astfel, se elimină

APLICA ȚIE WEB DE PUBLICITATE  
 
12 
  situațiile în care se realizeaz ă un apel de funcție cu un parametru  de tip 
neașteptat. 
  TypeScript  nu poate fi rulat direct de către browser,  fiind necesară 
compilarea  acestuia în JavaScript.   
 
TypeScript  
class Greeter {
msg: string;
constructor (message: string) {
this.msg = message ;
}
greet() {
return `Hello ${ this.msg};
}
}

 JavaScript  
var Greeter = (function () {
function Greeter(message) {
this. msg = message ;
}
Greeter.prototype .greet = function () {
return `Hello ${ this.msg};
};
return Greeter ;
})();

 
1.4. Pug
Pug este un „templating  engine” care ne pune la dispoziție o sintaxă 
scurta, simplă  și curată care face scrierea de cod HTML mai rapidă. Pug 
folosește spațiul alb pentru a stabili ierarhia elementelor  HTML.

APLICA ȚIE WEB DE PUBLICITATE  
 
13 
  Pug: 
doctype html
html(lang="en")
head
title Pug
body
h1 Pug
#container.col
p.large-text Paragraf

 HTML 
<!DOCTYPE html >
<html lang="en">
<head>
<title>Pug</title>
</head>
<body>
<h1>Pug</h1>
<div class="col" id="container ">
<p class="l-text”> Paragraf </p>
</div>
</body>
</html>

1.5. SCSS
 
  SCSS este un limbaj de „style sheet” care extinde limbajul CSS și ne pune 
la dispoziție o sintaxă îmbunătățită, posibilitatea  de a declara variabile,  
posibilitatea  de a folosi logică „if‐else” și posibilitatea  de a folosi funcții. 
 
SCSS 
$color: #ffffff;
$width: 800px;

body{
width: $width;
color: $color;

.content{
width: $width; CSS 
body{
width: 800px;
color: #ffffff;
}

body .content{
width:750px; background :#ffffff;
}

APLICA ȚIE WEB DE PUBLICITATE  
 
14 
   background :$color;

&:hover{
background : red;
}
}
}
 body .content:hover{
background : red;
}

 
1.6. NodeJS
 
  NodeJS este un „runtime”  de JavaScript  construit  peste Chrome V8 Java 
Script Engine. Acesta este combinat  cu un „event loop” ne blocator care rulează 
pe un singur fir de execuție și un API de Input/Output.  
  În cazul serverelor  tradiționale, pentru fiecare cerere primita de la client 
sau pentru fiecare conexiune  noua se creează un fir de execuție nou care preia 
cererea, o proceseaz ă,  și trimite înapoi un răspuns. Însă, acest mod de tratare a 
cererilor nu este cel mai eficient deoarece  prezența unui număr foarte mare de 
fire de execuție intr‐un sistem încărcat duce la o încetinire  considerabil ă a 
acestuia,  el fiind nevoit să gestioneze  programarea  firelor de execuție și 
schimbarea  contextului.  
  În schimb, NodeJS rulează pe un singur fir de execuție iar fiecare cerere 
primiă de la client va duce la execuția unei funcții „callback”  de JavaScript.  
Funcția callback poate trata cererea într‐un mod care nu blocheaz ă firul de 
execuție iar, daca este necesar, Node poate să creeze fire de execuție noi pentru 
executarea  operațiilor intense din punct de vedere a puterii de procesare  
necesare.  Ca urmare, un server Node folosește mult mai puțină memorie  pentru

APLICA ȚIE WEB DE PUBLICITATE  
 
15 
  gestionarea  cererilor în compara ție cu cele mai populare  arhitecturi  care se 
bazează în totalitate  pe crearea de fire de execuție noi, precum Apache HTTP 
Server, IIS, ASP.NET  si Ruby on Rails. 
  Figura următoare reprezint ă codul necesar pentru a rula un server HTTP, 
folosind sintaxa ,,ES6 
arrow function”  (funcții anonime  Lambda)  pentru 
funcțiile „callback”.  
 
const http = require('http'); 

const port = 3000; 

http.createServer ().listen(port, ()=>{ 
console.log(`Serverul ruleaza pe adresa: http://localhost: ${port}`); 
});
 
1.7. MongoDB
 
  MongoDB  este o bază de date non‐relațională care stocheaz ă datele în 
documente  cu format BSON (Binary JSON). Aceste documente  sunt stocate în 
colecții, care le dau context. Interogarea  datelor se face folosind MongoDB  query 
language.  Câmpurile  pot varia între documentele  unei colecții iar declararea  în 
prealabil  a structurii  unei colecții nu este necesară. Adăugarea proprietăților noi 
pentru o înregistrare  din colecție se face fără modificarea  documentelor  
existente  în colecția respectiv ă. 
  Datele din documente  sunt reprezentate  in format JSON ceea ce face ca  
stocarea structurilor  complexe,  precum array‐uri, să fie ușoară.

APLICA ȚIE WEB DE PUBLICITATE  
 
16 
   
 
Capitolul 2  
Descrierea Aplica ției
 
2.1. Prezentare
2.1.1. Client
 
  Pe partea de client aplicația este scrisă folosind platforma  Angular,  
versiunea  5 și este un website de tip „single page”. Deoarece  este o aplicație de 
tip ,,single‐page,, aceasta se ocupa în întregime  de partea de navigare.  Clientul 
nu trimite o cerere în mod tradițional pentru celelalte pagini care alcătuiesc 
aplicația iar experien ța utilizatorului  în timpul utilizării este una mult mai 
plăcută, deoarece  browser‐ul nu încarcă vizibil pagina de fiecare dată când se 
navigheaz ă. De asemenea,  modul în care Angular manipuleaz ă DOM‐ul 
permite modificarea  dinamică a datelor din afișaj, fără să fie nevoie reîncărcarea 
paginii. 
  Pentru gestionarea  datelor pe partea de client aplicația folosește 
Ngrx/Store.  Am ales să folosesc Ngrx/Store  pentru a îmbunătăți performan ța 
aplicației și pentru a ușura transmiterea  datelor între componente.  Datele 
folosite în mai multe componente  sunt aduse o singură data din server și stocate

APLICA ȚIE WEB DE PUBLICITATE  
 
17 
  in Store. Eventualele  modificări asupra acestora sunt persistate  în baza de date 
iar afișajul este actualizat  pentru a reflecta noua stare. 
  Pentru partea de ,,templating,,  folosesc Pug, un templating  engine, pentru 
că am componente  cu template ‐uri lungi iar Pug reduce cantitatea  de cod. 
Deoarece  configura ț
ia de Webpack  cu care vine Angular nu conține regulile 
necesare  compilării template ‐ului Pug în HTML folosesc un pachet numit „pug‐
ng‐html‐loader”. Acest pachet este inserat în configura ția Webpack.  
  Pentru stilizare folosesc SCSS deoarece  îmi permite să folosesc variabile  
și mixins. O altă tehnologie  folosită pentru partea de stilizare este Angular 
Material,  o adaptare  specială pentru Angular 2+ a principiilor  Material Design 
de la Google. Acest pachet include diferite componente  HTML, similar cu 
Bootstrap.  
 
2.1.2. Server
 
  Pe partea de server folosesc NodeJS împreuna  cu platforma  Express. 
Severul este scris folosind sintaxa de import ES6, iar acesta este compilat  
folosind Webpack.  Am ales să construiesc  severul pe modelul REST API pentru 
o integrare  ușoară cu aplicații mobile, pe viitor. Serverul poate fi pornit în trei 
moduri diferite: development,  testing și producție. Fiecare mod tratează erorile 
în mod diferit, are o configura ție diferită  și are o baza de date proprie. 
Autorizarea  pe server se face cu ajutorul unor funcții intermediare  pe rute. 
Aceste funcții verifică tokenuri  de tip JSON Web Token primite de la utilizatorii  
autentifica ți pe client.

APLICA ȚIE WEB DE PUBLICITATE  
 
18 
   
 
2.1.3. Baza de date
  
  Ca bază de date folosesc MongoDB  iar pentru a interacționa cu acesta 
folosesc un pachet de NodeJS numit Mongoose.  Acesta extinde sintaxa 
limbajului  de interogare  MongoDB  si ne permite să definim structura  colecțiilor 
prin intermediul  unei scheme. Această schemă este compilat ă la prima pornire 
a serverului  și garanteaz ă integritatea  datelor inserate/modificate.  În cadrul 
unei scheme putem defini dacă o proprietate  este obligatorie,  lungimea  minima, 
maxima,  o valoare implicită etc. Am ales să folosesc MongoDB  deoarece  este 
ușor de integrat cu serverul de NodeJS și partea de client. Această ușurință 
reiese din faptul că MongoDB  ne permite sa stocam strcturi complexe  iar 
pachetul  Mongoose  creează  un obiect Model pentru fiecare document  din baza 
de date în momentul  interogării. Acest obiect are metode care ne permit să 
efectuam  operațiile cele mai des folosite, precum cele de CRUD, cu ușurință. 
 
2.2. Arhitectura
2.2.1. Client
  
   Pe partea de client aplicația este împărțită în două module principale:  
Client și Admin. Aceste module importă modulul  Shared.

APLICA ȚIE WEB DE PUBLICITATE  
 
19 
     Modulul  Shared exportă componentele  și modulele  folosite in cadrul 
întregii aplicații. Acest modul importă modulele  necesare  din pachetul  
„@angular/material”  și declară componentele  comune celor două module, 
precum componentele:  ConfirmDialog,  IconPicker,  CategoryCard  și 
UploadGrid.    
   Modulul  Client este unul dintre modulele  principale,  conținutul lui fiind 
partea cu care
 interacționează marea majoritate  a utilizatorilor.  În cadrul acestui 
modul se află componentele  care alcătuiesc pagina de acasă, pagina de filtrare 
anunțuri, pagina de afișare a detaliilor  unui anunț anume, paginile de setări etc. 
Acest modul, fiind un modul principal,  are propriul modul de rutare, în care 
sunt definite rutele pe partea de client. La baza modului  stă componenta  
ClientComponent  iar componentele  care sunt încărcate în interiorul  acesteia 
sunt stabilite de către routerul de Angular.  
   Modulul  Admin este cel de al doilea modul principal.  Acest modul 
declară componentele  care alcătuiesc panoul de administrare  al aplicației. În 
cadrul panului de administrare  se pot adaugă categorii,  subcategorii,  atribute 
pentru subcategorii,  detalii pentru atribute etc. 
   Cele două module, sunt înregistrate  în modulul  principal  al aplicației, 
numit AppModule.  Pentru rutare, ele sunt înregistrate  în modulul  principal  de 
rutare.  
  
const appRoutes : Routes = [ 
{ path: '',loadChildren : 'app/client/client.module#ClientModule '}, 
{ path: 'admin', canActivate : [AdminGuard ], loadChildren :
'app/admin/admin.module#AdminModule '}, 
{ path: '404', component : NotFoundComponent }];

APLICA ȚIE WEB DE PUBLICITATE  
 
20 
    
   Datorită faptului că aceste module sunt „lazy‐loaded”,  performan ță 
aplicației crește deoarece  modulele  nu sunt încărcate în memoria  clientului  
decât în momentul  în care acesta acceseaz ă rutele specifice  modului  respectiv.  
De exemplu,  modulul  Admin nu va fi încărcat dacă utilizatorul  nu încearcă să 
acceseze
 ruta „/admin”.  
 Un element central al aplicației, atunci când vine vorba de partea de 
client, este Store‐ul. Acesta reprezint ă locul central unde este stocată starea 
aplicației. Store‐ul din cadrul aplicației conține patru reductori  principali:  
anunț, auth, category  și valută. Aceste reductori  sunt importate  într‐un reductor  
principal  care alcătuiește Store‐ul. Toți acești reductori  conțin acțiuni și efecte 
pentru operațiile uzuale CRUD și o acțiune pentru tratarea erorilor. Reductorul  
auth conține acțiuni pentru înregistrare,  autentificare  și ieșire din cont împreuna  
cu starea legată de utilizatorul  curent, precum numele de utilizator,  
identificatorul  unic din bază de date (_id) și emailul acestuia,  dacă acesta este 
autentificat  etc. Acest Store este injectat în majoritatea  componentelor  din cadrul 
aplicației, atât din modulul  Admin cât și din modulul  Client. Componentele  
efectueaz ă modificări asupra stării din Store numai prin intermediul  acțiunilor 
predefinite.  
  
2.2.2. Server
 
  Serverul este construit  dupa modelul REST API pentru a facilita 
integrarea  cu aplicațiile mobile, pe viitor.

APLICA ȚIE WEB DE PUBLICITATE  
 
21 
  Pe partea de rutare, serverul are doar două rute pentru servirea fișierelor 
statice. Ruta „/images”  pentru servirea imaginilor  și ruta „/*” care va trimite 
fișierul index.html  pentru fiecare rută nedefinit ă. Acest lucru se face în cazul în 
care aplicația client este una „single page” deoarece
 aceasta va gestiona rutarea 
în totalitate.  Restul rutelor sunt prefixate  cu „api/[num ăr versiune]/”,  în 
concordan ță cu standardul  REST API.   
Fiecare element este separat în module, fiecare modul reprezentând  o 
colecție din baza de date. Un modul conține cel puțin patru fișiere cu roluri 
diferite: 
 Model, care conține definiția schemei Mongoose  pentru entitatea  
respectiv ă 
 Controller,  care conține funcții asincrone,  cu rolul de a trata cererile 
primite pe rutele definite ca rute API, și funcții sincrone,  cu diferite 
roluri. 
 Validation,  care exportă un obiect validator  folosit ca parametru  
pentru o funcție intermediara  de validare pe rute.. 
 Routes, care conține definițiile de rute pentru modulul  respectiv  
 
 Un model se definește prin apelarea funcției „model”,  care primește ca 
parametru  numele modelului,  un obiect de tip Schema, care definește structura  
modelului  și, opțional, numele colecției. O schemă se definește prin apelarea 
constructorului  Schema din cadrul pachetului  Mongoose.  Acest constructor  
primește ca parametru  un obiect JSON care conține definițiile proprietățile care 
vor face parte din fiecare înregistrare  din colecția respectiv ă. Pentru fiecare 
proprietate  putem definii tipul, dacă valoarea introdus ă trebuie să fie unică la

APLICA ȚIE WEB DE PUBLICITATE  
 
22 
  nivelul colecției, lungime minimă, lungime maximă, dacă este obligatoriu,  dacă 
dorim să eliminăm spațiile albe din valoare introdus ă etc. Aceste proprietăți,  
spre deosebire  de SQL, pot conține structuri  de date complexe  precum array‐uri 
sau obiecte JSON.  Următoarea secvență de cod
 reprezint ă definirea  modelului  
pentru Localitate:  
 
 
import mongoose , {Schema} from 'mongoose '; 

const LocalitateSchema = new Schema({ 
nume :{ 
type : String, 
required : [true, 'Nume is required! '], 
trim : true, 
}, 
coordonate : { 
type : [Number], //[longitudine, latitudine]  
index : '2dsphere ', 
required : true 
}, 
judet :{ 
type : Schema.Types.ObjectId , 
ref : 'Judet' 

}, {timestamps : true}); 

LocalitateSchema .index({coordonate : '2dsphere '}); 

export default mongoose .model('Localitate ', LocalitateSchema , 'localitate ');

APLICA ȚIE WEB DE PUBLICITATE  
 
23 
     Fiecare model are asociat un controller,  care exportă funcții asincrone  în 
cadrul cărora se gestioneaz ă cererile primite de la client. Routerul  Express 
injecteaz ă automat doi parametri  în aceste funcții: primul parametru  reprezint ă 
obiectul cerere și al doilea un obiect răspuns. Obiectul  care reprezinta  cererea 
venită de pe client va conține, printre altele, datele transmise  de acesta. Aceste 
date se află pe proprietăți diferite ale obiectului,  în funcție de metoda HTTP 
pentru care a fost declarată ruta. De exemplu,  pentru o cerere de tip POST, 
,,request.body”  va conține datele primite de la client. 
 În următoarea secvență de cod avem funcția care gestioneaz ă un apel 
către ruta „/api/v1/loca ții/:str”. Unde :str reprezint ă textul introdus  de către 
utilizator  în câmpul de selecție a locației. Acel text se află pe proprietatea  
„params”  a obiectului  req. Dacă există acea proprietate  apelăm funcția „find” 
pe modelul Localitate  definit anterior.  Această funcție primește ca parametru  
un obiect JSON. În cadrul acestui obiect JSON cheia reprezint ă numele 
câmpului  din colecție și valoare asociată cheii valoarea de query. În acest caz 
valoarea de query este un obiect JSON prin care specificăm că dorim ca 
înregistrările pe care le căutăm să conțină în câmpul „nume” textul introdus  de 
utilizator,  indiferent  de poziția în cadrul câmpului.  
Acestui apel de funcție îi adăugăm un apel către funcția „populate”  cu 
parametrul  „judet”. Funcția, „populate”  este similară cu „join” din SQL. În 
cadrul colecției „localitate”,  în câmpul județ, înregistrările conțin id‐ul unei 
înregistrări din colecția „judet”, similar cu o cheie străină din SQL. Funcția 
„populate”  va înlocui valoarea de pe proprietatea  „judet” a obiectelor  
„Localitate”  cu înregistrarea  corespunz ătoare din colecția „judet”.

APLICA ȚIE WEB DE PUBLICITATE  
 
24 
  Înainte apelurilor  de funcție, însă, am pus cuvântul  cheie „await”. Acest 
cuvânt este un cuvânt rezervat în limbajul JavaScript  și are rolul de a semnala 
că ceea ce  urmează după el este o operație asincron ă iar executarea  funcției să 
se reia numai după rezolvarea  apelului
 de funcție. La întâlnirea  unei erori 
serverul va trimite un răspuns cu statusul 400 înapoi la client, împreuna  cu 
eroarea respectiva.  
 
export async function getLocatie (req, res) { 
try { 
if(!req.params.str){throw `Invalid search term! `}; 

const locatii = await Localitate .find({ 
nume : {'$regex':req.params.str, '$options ': 'i'} 
}).populate ('judet'); 

return res.status(Status.OK).json(locatii); 
} catch (err) { 
console.log(err); 
return res.status(Status.BAD_REQUEST ).send(err); 
}}; 
  
  Unele modele au asociate un fișier cu rolul de validare.  Acest fișier 
exportă un obiect de tip JSON cu reguli de validare folosind pachetul  „joi”. 
Acest obiect este folosit ulterior ca parametru  la apelul funcției „validate”  din 
cadrul pachetului  „express ‐validation”.  Funcția se pune pe o ruta de Express 
înainte de referința spre funcția din controller  care va gestiona cererea. În cazul 
în care datele din cerere nu corespund  cu validatorul,  serverul va returna un 
răspuns cu status de eroare, împreun ă cu eroarea din validator.

APLICA ȚIE WEB DE PUBLICITATE  
 
25 
  import validate from 'express-validation '; 
import userValidation from './user.validation '; 

const router = new Router(); 
router.post('/signup',validate (userValidation .signup),UserController .signUp); 
 
  Toate modelele  au asociat un fișier care conține definițiile de rute API 
pentru modelul respectiv.  Fiecare rută este creată folosind un obiect creat prin 
apelarea constructorului  Router din pachetul  Express Acest obiect are diferite 
metode, reprezentând  metodele  prin care se poate face o cerere HTTP. Obiectul  
router este apoi importat  în fișierul principal  de rutare unde sunt declarate  
rutele pentru întreaga parte de API. 
      Fiecare metodă de acest gen primește cel puțin trei parametri:  
 Adresa URL care va fi concatenat ă adresei API și reprezint ă adresa 
rutei 
 Una sau mai multe funcții intermediare,  prin care va trece cererea 
 O referință către funcția care va trata cererea, dacă acesta nu a fost 
respins de către una dintre funcțiile intermediare  
 
Următoarea secvența de cod reprezint ă definiția rutelor pentru modulul  
„subcategorii”.  
 
import {Router} from 'express';
import validate from 'express-validation ';
import { authLocal , authJWT , isAdmin } from '../../services/auth.service ';
import * as UserController from './user.controller ';
import userValidation from './user.validation ';
import errorHandler from '../../config/error ';

APLICA ȚIE WEB DE PUBLICITATE  
 
26 
  
const router = new Router();

router.post('/confirm/email ', errorHandler (UserController .confirmEmail ));
router.post('/confirm/password ', errorHandler (UserController .confirmPassword ));
router.post('/signup', validate (userValidation .signup), errorHandler (UserControl-
ler.signUp));
router.post('/login', authLocal , errorHandler (UserController .login));

router.get('/settings ', authJWT , errorHandler (UserController .getSettings ));
router.get('/checkAdmin ', isAdmin , errorHandler (UserController .checkAuth ));
router.get('/checkAuth ', authJWT , errorHandler (UserController .checkAuth ));
router.patch('/update', authJWT , errorHandler (UserController .updateUser ));
router.patch('/updateEmail ', authJWT , errorHandler (UserController .updateEmail ));
router.patch('/updatePassword ', authJWT , errorHandler (UserController .updatePassword ));

export default router;
 
Pentru ruta de autentificare  avem ca funcție intermediara  o funcție 
numita authLocal,  care reprezint ă strategia  locală de autentificare  cu email și 
parolă. Această strategie  este scrisă folosind pachetul  „passport”  și „passport ‐
local”. În cazul autentific ării cu succes a utilizatorului,  cererea ajunge la funcția 
„login” din cadrul UserController.  În cazul în care utilizatorul  și‐a confirmat  
adresa de e‐mail, această funcție returneaz ă un răspuns cu statusul 200 care 
conține date despre utilizatorul  autentificat,  precum numele de utilizator,  
identificatorul  unic din baza de date, email și cel mai important,  JSON Web 
Token‐ul generat. Acest token va fi stocat pe client și va fi trimis ca valoarea 
pentru câmpul ,,Authorization”  în cadrul fiecărei cereri HTTP. Unele rute 
folosesc o funcție intermediara  care verifică validitatea  tokenului  iar dacă

APLICA ȚIE WEB DE PUBLICITATE  
 
27 
  clientul trimite o cerere cu token invalid pe o rută care necesită ca utilizatorul  să 
fie autentificat  serverul va returna un răspuns cu statusul de 401. 
 
export async function login(req, res, next){
if(!req.user || !req.user.isConfirmed ){
return res.status(HTTPStatus .BAD_REQUEST ).json({message: `Adresa de email nu a fost
confirmata! `});
}
return res.status(HTTPStatus .OK).json(await req.user._toAuthJSON ());
}
 
   Pentru configurarea  serverului  se folosește un fișier JSON care conține 
diverse date, precum: numărul de port, datele de conexiune  la baza de date, 
cheie secretă pentru generarea  token‐ului etc. Acest fișier este citit la pornirea  
serverului  și, în funcție de modul în care a fost pornit serverul,  se va alege o 
configura ție diferită din cadrul obiectul citit.   
      Întregul cod sursă al serverului  este compilat  într‐un singur fișier, 
folosind Webpack.  Iar sintaxa de import, specifică versiunii  ES6 a limbajului  
JavaScript,  este procesată folosind Babel. Acest fișier compilat  este optimizat  din 
mai multe puncte de vedere, cel mai important  dintre ele fiind optimizarea  
importurilor  de pachete. Performan ța serverului  este, în consecin ță, 
îmbunătățită. 
2.2.3. Baza de date

APLICA ȚIE WEB DE PUBLICITATE  
 
28 
     Bază de date este alcătuită din 13 colecții de documente.   Relațiile 
descrise mai jos nu sunt o funcționalitate  a MongoDB,  ci sunt definite folosind 
pachetul  Mongoose.  
 
2.2.3.1. User
 
  În cadrul documentelor  din această colecție se stocheaz ă datele 
utilizatorilor  înregistra ți. Proprietatea  „email” este unică în cadrul 
documentelor  din colecție deoarece  pentru autentificare  pe client se folosește 
combina ția adresa de email si parola.   
   Valoarea  proprietăților „password”  și „passwordChange”  sunt criptate 
ireversibil,  folosind funcția „bcrypt”.   
   În momentul  în care un utilizator  creează un cont nou câmpul 
„isConfirmed”  primește valoarea implicită „fals” iar autentificarea  acestuia nu 
este permisă până când își confirmă adresa de e‐mail. 
   În cazul în care utilizatorul  cere schimbarea  adresei de e‐mail, 
proprietatea  „emailChange”  primește ca valoare noua adresă de e‐mail. 
Utilizatorul  va fi notificat pe client să își confirme  noua adresă de e‐mail atâta 
timp cât proprietatea  „emailChange”  nu are valoarea null. În momentul  în care 
utilizatorul  își confirmă noua adresă de e‐mail valoarea proprietății „email” 
primește valoarea proprietății „emailChange”  iar acesta primește valoarea null. 
Procesul  este similar în cazul în care utilizatorul  își schimbă parolă, proprietatea  
folosită în acest caz fiind „passwordChange”.

APLICA ȚIE WEB DE PUBLICITATE  
 
29 
        Referințele către rolurile pe care le are utilizatorul  sunt stocate în tabloul 
„roles”. În momentul  de față toți utilizatorii  primesc implicit rolul „user” dar 
un utilizator  poate avea și rolul de „admin”.  Aceste roluri sunt folosite în faza 
de autorizare,  atât pe client cât și 
pe server. 
      Dacă utilizatorul  își setează locația sau o poză de profil se salvează o 
referință către aceasta în proprietatea  „localitate”  respectiv  „avatar”.    
      Referințele către anunțurile pe care utilizatorul  le‐a  adăugat la favorite 
sunt salvate în tabloul „favorites.anunturi”.  
 
Structura   Relații 
Câmp  Tip 
_id  String 
username   String 
email  String 
password   String 
isConfirmed   Boolean 
roles  Array 
 [0]  ObjectId  
favorites   Document  
 anunturi   Array 
   [0]  ObjectId  
avatar  ObjectId  
localitate   ObjectId  Colecție 
Părinte Prop. 
PărinteColecție
Copil Proprietate  
Copil 
anunt  _id  user  favorites.anunturi[0]  
imagine  _id  user  avatar 
localitate  _id  user  localitate  
role  _id  user  roles[0] 
user  _id  anunt user 
user  _id  convo user1 
user  _id  convo user2 
user  _id  imagine user 
user  _id  message from 
user  _id  valuta user

APLICA ȚIE WEB DE PUBLICITATE  
 
30 
  telefon  String 
emailChange   String 
passwordChange  String 
createdAt   Date 
updatedAt   Date 
 
2.2.3.2. Anunt
 
     Documentele  din această colecție reprezint ă atributele  unei anumite 
subcategorii.  Un atribut este o caracteristic ă care definește un obiect ce aparține 
de subcategoria  respectiv ă. Un exemplu  de atribut fiind atributul  
„Combustibil”  pentru subcategoria  „Automobile”.  Aceste atribute sunt folosite  
în cadrul filtrării anunțurilor. Un atribut poate depinde de un alt atribut. În acest 
caz se salvează o referință către atributul  părinte în cadrul câmpului  „parinte”.  
În cazul în care un atribut are părinte detaliile afișate pentru acesta în client vor 
depinde de detaliul selectat pentru atributul  părinte. În consecin ță, detaliile 
adăugate pentru acest atribut sunt salvate în cadrul array‐ului „detalii”  a 
detaliului  atributului  părinte. Un exemplu  care descrie acest scenariu sunt 
atributele  „Model”  și „Marca”.  Detaliile pentru atributul  „Marca”  depind de 
detaliul selectat pentru atributul  „Model”.   
 
Structura   Relații 
Câmp  Tip 
_id  String Colecție 
Părinte Prop. 
PărinteColecție 
Copil Proprietate  
Copil 
anunt  _id  convo anunt

APLICA ȚIE WEB DE PUBLICITATE  
 
31 
  nume  String 
descriere   String 
user  ObjectId  
subcategorie   ObjectId  
valuta  ObjectId  
locatie  ObjectId  
atribute  Array 
 [0]  Document  
   _id  String 
   atribut  ObjectId  
   atributDetaliu  ObjectId  
   valoare  String 
imagini  Array 
 [0]  Document  
isNegociabil   Boolean 
isSchimb   Boolean 
isActive  Boolean 
telefon  String 
vizualizari   Numeric  
 anunt  _id  user  favorites.anunturi[0]  
atribut  _id  anunt atribute[0].atribut  
atribut_detaliu _id  anunt atribute[0].atributdetaliu
imagine  _id  anunt imagini[0]  
locatie  _id  anunt locatie 
subcategorie  _id  anunt subcategorie  
user  _id  anunt user 
valuta  _id  anunt valuta 
 
 
2.2.3.3. Atribut

APLICA ȚIE WEB DE PUBLICITATE  
 
32 
     Documentele  din această colecție reprezint ă atributele  unei anumite 
subcategorii.  Un atribut este o caracteristic ă care definește un obiect ce aparține 
de subcategoria  respectiv ă. Un exemplu  de atribut fiind atributul  
„Combustibil”  pentru subcategoria  „Automobile”.  Aceste atribute sunt folosite  
în cadrul filtrării anunțurilor. Un 
atribut poate depinde de un alt atribut. În acest 
caz se salvează o referință către atributul  părinte în cadrul câmpului  „parinte”.  
În cazul în care un atribut are părinte detaliile afișate pentru acesta în client vor 
depinde de detaliul selectat pentru atributul  părinte. În consecin ță, detaliile 
adăugate pentru acest atribut sunt salvate în cadrul array‐ului „detalii”  a 
detaliului  atributului  părinte. Un exemplu  care descrie acest scenariu sunt 
atributele  „Model”  și „Marca”.  Detaliile pentru atributul  „Marca”  depind de 
detaliul selectat pentru atributul  „Model”.  
  
Structura   Relații 
Câmp  Tip 
_id  String 
nume  String 
parinte  ObjectId  
detalii  Array 
 [0]  ObjectId  
sortedAtribute  ObjectId  
forSorting   Boolean 
forValue   Boolean Colecție 
Părinte Prop. 
Părinte Colecție 
Copil Proprietate  
Copil 
atribut  _id  anunt  atribute[0].atribut  
atribut  _id  atribut  atributparinte  
atribut  _id  atribut  sortedAtribute  
atribut  _id  atribut_detaliu  atribut 
atribut  _id  subcategorie  atribute[0]

APLICA ȚIE WEB DE PUBLICITATE  
 
33 
  isRequired   Boolean 
isMin  Boolean 
isMax  Boolean 
createdAt   Date 
updatedAt   Date 
 
2.2.3.4. Atribut_detaliu
 
   Această colecție conține documente  ce reprezint ă detaliile unui anumit 
atribut. Un exemplu  de detaliu este „Diesel” pentru atributul  „Combustibil”.  În 
cazul în care un atribut depinde de un alt atribut, se vor adaugă referințe către  
detaliile atributului  copil  în  array‐ului „detalii”.    
 
Structura   Relații 
Câmp  Tip 
_id  String 
nume  String 
atribut  ObjectId  
detalii  Array 
 [0]  ObjectId  
tags  Array 
 [0]  String 
createdAt   Date Colecție 
Părinte Prop. 
Părin
te Colecție
Copil Proprietate  
Copil 
atribut_detaliu _id Anunt Atribute[0].atributDetaliu  
atribut_detaliu _id Atribut Detalii[0]  
atribut_detaliu _id atribut_d
etaliu detalii

APLICA ȚIE WEB DE PUBLICITATE  
 
34 
  updatedAt   Date 
 

 
 
 
2.2.3.5. Categorie
 
Structura   Relații 
Câmp  Tip 
_id  String 
nume  String 
subcategorii   Array 
 [0]  ObjectId  
orderNr  Numeric  
iconClass   String 
color  String 
slug  String 
createdAt   Date 
updatedAt   Date 
 Colecție 
Părinte Prop. 
PărinteColecție 
Copil Proprietate  
Copil 
categorie   _id  subcategorie  parentCategory  
subcategorie  _id  categorie  subcategorii[0]  
      
 

2.2.3.6. Subcategorie

APLICA ȚIE WEB DE PUBLICITATE  
 
35 
  Structura   Relații 
Câmp  Tip 
_id  String 
nume  String 
ParentCategory  ObjectId  
atribute  Array 
 [0]  ObjectId  
maxImageCount  Numeric  
daysValid   Numeric  
orderNr  Numeric  
iconClass   String 
color  String 
slug  String 
createdAt   Date 
updatedAt   Date 
 Colecție 
Părinte Prop. 
PărinteColecție 
Copil Proprietate  
Copil 
atribut  _id  subcategorie  atribute[0]  
categorie   _id  subcategorie  parentcategory  
subcategorie  _id  anunt  subcategorie  
subcategorie  _id  categorie   subcategorii[0]  
 

2.2.3.7. Convo
 
   Această colecție are rolul de a stoca câte un document  pentru fiecare 
conversa ție dintre doi utilizatori.  În documentul  respectiv  se face referință către 
mesajele,  din colecția „messages”,  care au fost trimise în cadrul conversa ției. De 
asemenea,  se face referință către anunțul prin intermediul  căruia s‐a inițiat 
conversa ția. Acest lucru este folositor în momentul  afișării conversa țiilor în

APLICA ȚIE WEB DE PUBLICITATE  
 
36 
  secțiunea „Conversa ții Recente”,  deoarece  utilizatorul  poate recunoaște o 
conversa ție mult mai ușor pe baza anunțului, decât pe baza numelui de 
utilizator.  
 
Structura   Relații 
Câmp  Tip 
_id  String 
user1  ObjectId  
user2  ObjectId  
anunt  ObjectId  
messages   Array 
 [0]  String 
createdAt   Date 
updatedAt   Date 
 Colecție 
Părinte Prop. 
PărinteColecție
Copil Proprietate  
Copil 
message  _id  convo messages[0]  
anunt  _id  convo anunt 
user  _id  convo user1 
user  _id  convo user2 
 

2.2.3.8. Message
   Documentele  din această colecție reprezint ă mesajele  din cadrul unei 
conversa ții dintre doi utilizatori.  Se stocheaz ă o referință către imaginile  care au 
fost trimise precum și o referință către utilizatorul  care a trimis mesajul. Câmpul 
„seen” se completeaz ă automat cu valoarea „true” atunci când destinatarul  
mesajului  l‐a văzut.

APLICA ȚIE WEB DE PUBLICITATE  
 
37 
  Structura   Relații 
Câmp  Tip 
_id  String 
nume  String 
atribut  ObjectId  
detalii  Array 
 [0]  ObjectId  
tags  Array 
 [0]  String 
createdAt   Date 
updatedAt   Date 
 Colecție 
Părinte Prop. 
PărinteColecție 
Copil Proprietate  
Copil 
imagine  _id  message imagini[0]  
message  _id  convo  messages[0]  
user  _id  message from 
 

 
2.2.3.9. Imagine
 
   Documentele  din această colecție reprezint ă referințe către imaginile  
încărcate de către utilizatori.  În cazul în care se șterge un document  din baza de 
date care conține referințe spre documente  din această colecție, imaginile  
respective  vor fi șterse de pe disc. 
 
Structura   Relații 
Câmp  Tip 
_id  String Colecție 
Părinte Prop. 
PărinteColecție 
Copil Proprietate  
Copil 
imagine  _id  anunt  imagini[0]

APLICA ȚIE WEB DE PUBLICITATE  
 
38 
  nume  String 
user  ObjectId  
createdAt   Date 
updatedAt   Date 
 imagine  _id  messages  imagini[0]  
imagine  _id  user  avatar 
user  _id  imagine  user 
 

2.2.3.10 Judet
 
Structura   Relații 
Câmp  Tip 
_id  String 
nume  String 
 Colecție 
Părinte Prop. 
PărinteColecție 
Copil Proprietate  
Copil 
judet  _id  localitate   judet 
 
 
2.2.3.11 Localitate
 
   Această colecție conține documente  ce reprezint ă fiecare localitate  din 
România  (13,749 de documente).   Fiecare document  conține câte o referință către 
județul de care aparține localitate  și un array special numit „coordonate”.  Acest 
array stocheaz ă longitudinea  și latitudinea  localității, sub formatul  [longitudine,  
latitudine],  și pentru acest câmp se definește un index special, de tip 
„2dsphere”.  Valorile din cadrul acestui câmp sunt utilizate pentru stabilirea  
automată a locației utilizatorului  prin intermediul  sintaxei „$near”.

APLICA ȚIE WEB DE PUBLICITATE  
 
39 
   
 
Structura   Relații 
Câmp  Tip 
_id  String 
nume  String 
judet  ObjectId  
coordonate   Array 
 [0]  Numeric  
createdAt   Date 
updatedAt   Date 
 Colecție 
Părinte Prop. 
PărinteColecție 
Copil Proprietate  
Copil 
judet  _id  localitate judet 
localitate   _id  anunt localitate  
localitate   _id  user  localitate  
 

2.2.3.12 Role
 
   Colecția conține documente  ce reprezint ă rolurile pe care le poate avea 
un utilizator.  Un exemplu  de rol ar fi cel de „Admin”.  Pe baza acestor roluri se 
face autorizarea  utilizatorului  în timp ce acesta navigheaz ă pe client, cât și 
atunci când cere date de la API. 
 
Structura   Relații 
Câmp  Tip 
_id  String Colecție 
Părinte Prop. 
PărinteColecție 
Copil Proprietate  
Copil 
role  _id  user  roles[0]

APLICA ȚIE WEB DE PUBLICITATE  
 
40 
  nume  String 
roleNumber   Numeric  
createdAt   Date 
updatedAt   Date 
 

2.2.3.13 Valuta
  
   În această colecție se stocheaz ă documentele  ce conțin valutele folosite în 
cadrul aplicației. Fiecare valută are un nume (acronim  de 3 litere) și un simbol, 
folosit la afișarea prețului. Aceste documente  sunt actualizate  în fiecare zi , 
deoarece  se aduce noul curs valutar, iar data ultimei actualizări se salvează în 
proprietatea  „ratesFetched”.  Cursul de raportare  este calculat pentru fiecare 
valută din baza de date și se stocheaz ă în tabloului  „rates”.   
 
Structura   Relații 
Câmp  Tip 
_id  String 
nume  String 
simbol  String 
user  ObjectId  
ratesFetched   Date 
rates  Array Colecție 
Părinte Prop. 
PărinteColecție 
Copil Proprietate  
Copil 
user  _id  valuta  user 
valuta  _id  anunt  valuta

APLICA ȚIE WEB DE PUBLICITATE  
 
41 
   [0]  Document  
    _id  ObjectId  
    nume  String 
    valoare  Numeric  
isActive  Boolean 
createdAt   Date 
updatedAt   Date

APLICA ȚIE WEB DE PUBLICITATE  
 
42 
  
Capitolul 3
Aplicația
 
3.1. Autentificare, Autorizare și Înregistrare
  
   Deoarece  aplicația este una „single page” nu există sesiunea  specifică 
PHP iar din considerente  de securitate  nu se folosesc cookie‐uri. În schimb, 
autentificarea  se face folosind JSON Web Token. 
  La prima autentificare  utilizatorul  primește un token generat de către 
server. Acest token este apoi stocat în storage‐ul local al browserului.  La fiecare 
accesare a aplicației se execută acțiunea SetSessionFromStorage.  Funcția 
reductor  care corespunde  acestei acțiuni verifică dacă există în storage‐ul local 
al browserului  obiectul „authData”.   Acest obiect conține date despre 
utilizatorul  autentificat,  precum: numele de utilizator,  email, id‐ul din baza de 
date și token‐ul primit de la server în momentul  autentific ării. Dacă există acest 
obiect, reductorul  va actualiza  starea „auth” din cadrul Store‐ului cu datele din 
obiect. 
  De asemenea,  aplicația va completă automat headerul  Authorization,  
pentru toate cererile trimise, cu valoarea tokenului.   Acest lucru este realizat 
prin intermediul  unui interceptor  HTTP de Angular.  Dacă răspunsul de la 
server este 401, deși tokenul a fost trimis, se execută automat acțiunea Logout

APLICA ȚIE WEB DE PUBLICITATE  
 
43 
  iar funcția reductor  care corespunde  acestei acțiuni va șterge obiectul 
„authData”  din local storage, va actualiza  corespunz ător starea de „auth” a 
Store‐ului și va redirecționa utilizatorul  către pagină de acasă cu un mesaj care 
îl notifica de faptul că va trebui să se
 autentifice  din nou. 
      În cazul creării unui cont nou, dacă acest lucru se realizeaz ă cu succes 
utilizatorul  va fi nevoit să își confirme  adresa de e‐mail înainte să se poată 
autentifica.  
      Unele rute, precum cea de adăugare a unui anunț nou, necesită ca 
utilizatorul  să fie autentificat.  Accesul la aceste rute este protejat printr‐un guard 
Angular care verifică valoarea variabilei  „isAuthenticated”  din cadrul stării de 
„auth”. Această măsura de restricționare a accesului  ține doar de partea de 
client. Fiind parte de client, utilizatorul  are acces la codul sursă și ar putea obține 
acces la rutele restricționate de către guard. În caz că obține acces la o pagină 
restricționata el nu va putea modifică nimic deoarece  pe server se face din nou 
autorizarea  utilizatorului,  pe bază tokenului  acestuia.  În acest caz, dacă nu 
există tokenul sau este invalid serverul va trimite înapoi un răspuns cu statusul 
401 iar clientul va redirecționa utilizatorul,  actualizând  starea „auth” în mod 
corespunz ător. 
      Rutele ce țin de modulul  admin necesită că acesta să fie autentificat  și să 
aibă rolul de administrator.  Această verificare  se face însă diferit față de rutele 
din modulul  client, aplicația trimițând o cerere către server iar serverul 
efectuând  aceasta verificare  de roluri. În funcție de răspunsul de la server 
utilizatorului  i se permite, sau nu, accesul la rute. 
      Rutele definite de modulul  admin sunt protejate  de un Guard. Acest 
Guard trimite o cerere HTTP către server, având ca valoare pentru câmpil

APLICA ȚIE WEB DE PUBLICITATE  
 
44 
  ,,Authorization”  tokenul utilizatorului  autentificat.  În server se stabilește dacă 
utilizatorul  cu tokenul respectiv  are rolul de administrator  și se trimite înapoi 
un răspuns. Dacă utilizatorul  este administrator,  răspunsul va avea statusul 200, 
în caz contrat răspunsul va avea statusul 401. Clientul așteaptă răspunsul
 de la 
server înainte de a realiză rutarea. Dacă răspunsul are statusul 200 se va realiza 
rutarea către componenta  corespunz ătoare din modulul  Admin. În caz contrat, 
routerul Angular va redirecționa utilizatorul  către pagină de 404. Acesta 
abortare,  la fel ca și cea pentru modulul  „client” ține doar de partea de client. 
   Toate rutele care au de a face cu operații CRUD pe resurse gestionate  din 
panoul de admin sunt protejate  cu o funcție intermediara  în server care verifică 
dacă utilizatorul  care încearcă să facă modificarea  este într‐adevăr 
administrator.  Dacă utilizatorul  nu este administrator,  serverul va răspunde cu 
statusul 401 cererii. Când răspunsul ajunge în client iar statusul este 401 
utilizatorul  va fi redirecționat imediat către pagină de 404, fiind nevoit să repete 
procesul.  De asemenea,  cererea respectiva  va fi stocata permanent  în baza de 
date, împreun ă cu datele de conexiune  ale utilizatorului.  
 
 
 
 
 
Figura 3.1.1 Obiectul  „authData”  din local storage pentru un utilizator  autentificat

APLICA ȚIE WEB DE PUBLICITATE  
 
45 
     Autentificarea  cu JSON Web Token este considerat ă una dintre cele mai 
sigure metode, fiind nevoie de manipularea  serverului  pentru a obține 
privilegii.  
 
3.2. Panoul de administrator
     Panoul de administrator  are rolul de a pune la dispoziția utilizatorilor  cu 
privilegiile  corespunz ătoare unelte pentru gestiunea  datelor aplicației.    
3.2.1 Gestionarea resurselor
   În cadrul acestui panou administratorii  pot gestiona resurse precum:   
• Categorii  
• Subcategorii  
◦ Atribute 
◦ Detalii pentru atribute 
• Valute 
     Adăugarea unei subcategorii  se face folosind un „stepper”  de Angular 
Material.  Acesta necesită completarea  unei acțiuni înainte de a permite 
avansarea  la următorul pas. Primul pas
 este alegerea unei categorii  de care va 
ține subcategoria.  În acest pas ne sunt afișate toate categoriile  din aplicație, sub 
aceeași formă în care apăr în pagină de acasă. Pentru afișarea acestora se 
folosește o component ă comună celor două module, component ă 
CategoryCard.

APLICA ȚIE WEB DE PUBLICITATE  
 
46 
    
 
    
 
Figura 3.2.1.1 Primul pas din stepper 
 
   Odată ce administratorul  a ales o categorie,  acestuia i se permite accesul 
la următorul pas, cel de alegere a unei subcategorii.  În cadrul acestui pas 
administratorul  are posibilitatea  de a  alege o subcategorie  deja existentă, în caz 
că dorește să facă modificări. Dacă dorește, însă, să adauge o subcategorie  el este 
nevoit să apese pe butonul „Adaugă”.  Pasul care afișează subcategoriile  
existente  folosește aceeași component ă CategoryCard  ca și pasul care afișează 
categoriile.

APLICA ȚIE WEB DE PUBLICITATE  
 
47 
   
Figura 3.2.1.2 Formularul  de adăugare subcategorie  
 
   În cadrul formularului  de adăugare subcategorie  avem cinci câmpuri.  
Toate aceste câmpuri sunt obligatorii,  introducerea  unei înregistrări fiind 
blocată atunci când unul dintre câmpuri nu este completat.  Primul câmp este 
cel de selectarea  a iconiței. Dacă utilizatorul  dă click pe iconiță deja selectată se 
va deschide  un modal cu toate iconițele disponibile.  Acest modal are un input 
de filtrare. La selectarea  unei iconițe din modal acesta se va închide. Modalul  
este component ă IconPicker.  Al doilea câmp este cel câmpul pentru numele 
subcategoriei.  Al treilea câmp este câmpul pentru selectarea  culorii pe care o va 
avea subcategoria.  Acest câmp primește ca valoare direct un cod hexazecimal  
de culoare dar utilizatorul  poate da click pe cutia ce afișează culoarea  curentă și 
se va deschide  un modal de selectarea  a culorii. Următoarele două câmpuri sunt 
cele care vor stabilii numărul maxim de poze pe care utilizatorul  le va putea 
încărca la adăugarea unui anunț nou pentru această subcategorie  și durata de 
validitate  a anunțului, în zile.

APLICA ȚIE WEB DE PUBLICITATE  
 
48 
    Serverul face verificări în fiecare zi prin intermediul  unui „cron job” și va 
actualiza  automat câmpul „isActive”  cu valoarea „false” pentru toate anunțurile 
din baza de date a căror termen de valabilitate  a expirat. Aceste anunțuri nu vor 
mai apărea în căută
rile utilizatorilor  până când utilizatorul  care a adăugat 
anunțul respectiv  nu îl reactiveaz ă. Odată ce au trecut 15 zile de la dezactivarea  
unui anunț, fără ca acesta să fie reactivat,  serverul va șterse automat toate 
înregistrările din baza de date care țin de acest anunț și imaginile  de pe discul 
serverului.  Această operațiune este, de asemenea,  automată și se realizeaz ă 
folosind un „cron job” care se execută zilnic. 
   Odată ce administratorul  a adăugat cu succes o subcategorie  el va avea 
acces la ultimul pas, cel de adăugare a atributelor  pentru subcategoria  selectată. 
 
 
Figura 3.2.1.3 Pasul de adăugare a atributelor  
 
   Fiecărei subcategorii  noi create i se adaugă automat atributele  „Pret”, 
„Pret de la” și „Pret pana la”. Atributele  care se adaugă la acest pas se vor folosi 
pentru filtrarea anunțurilor de către utilizatori  și unele dintre ele se vor folosi în 
cadrul formularului  de adăugare anunț. Fiecare subcategorie  va avea atribute 
specifice  ei.  Există trei tipuri de atribut:

APLICA ȚIE WEB DE PUBLICITATE  
 
49 
  • forValue,   acest tip de atribut se folosește exclusiv în cadrul 
formularului  de adăugare anunț iar valoarea introdus ă de 
utilizator  pentru acest atribut se va putea sorta. Un exemplu  este 
atributul  „pret” 
•  forSorting,  acest tip de atribut este folosit la sortarea atributelor  de 
tip „forValue”.  
El poate fi de două feluri: 
◦  isMin, dacă detaliul selectat sau valoarea introdus ă manual 
pentru acest atribut reprezint ă valoarea minimă 
◦  isMax, dacă detaliul selectat sau valoarea introdus ă manual 
pentru acest atribut reprezint ă valoarea maximă 
•  Pentru filtrare, acesta este tipul standard  de atribut. El nu permite 
introducerea  manuală de valori ci doar selecția unui detaliu ce 
aparține de el. Acest tip de atribut, însă, poate avea un părinte. 
Dacă are un părinte detaliile lui vor depinde de detaliul selectat în 
atributul  părinte. Un exemplu  ar fi atributele  „Marca”  și „Model”,  
evident detaliile din cadrul atributului  Model depind de Marca 
selectată. 
 
   Pentru fiecare atribut există posibilitatea  de a stabili dacă acesta este 
obligatoriu  sau nu. Această caracteristic ă este luată în considerare  în cadrul 
formularului  de adăugare a unui anunț nou. Odată adăugat,  administratorul  
are posibilitatea  de a adaugă detalii pentru atributul  respectiv,  doar dacă acesta 
nu este de tip forValue.  Dacă atributul  pentru care se adaugă detaliile are un 
atribut părinte atunci administratorul  este nevoit să selecteze  mai întâi un 
detaliu dintre cele ale părintelui. Acestui detaliu i se vor asocia detaliile

APLICA ȚIE WEB DE PUBLICITATE  
 
50 
  introduse  pentru atributul  copil. În cazul în care utilizatorul  dorește să șteargă 
o resursa care ține de acest pas i se afișează un modal de confirmare  care 
detaliază toate resurse care depind de resursa ce urmează să fie ștearsă. 
Ștergerea se va efectuă doar dacă 
administratorul  confirmă. 
      Adăugarea, modificarea  și ștergerea categoriilor  se face în mod similar 
cu cea a subcategoriilor.  Diferențele dintre cele două fiind faptul  ca în cazul 
categoriilor  nu există un „stepper”  și categoriile  nu au atribute. 
   La gestionarea  valutelor  avem un tabel de tip Data Table de la Angular 
Material.  Dacă se selecteaz ă o înregistrare  din tabel se deschide  modalul care 
conține formularul  completat  cu detaliile înregistrării selectate.  De aici avem 
posibilitatea  să facem modificări sau să ștergem înregistrarea.  La ștergere se 
afișează un modal de confirmare.   În cadrul acestei pagini se regăsește și butonul 
de actualizarea  a cursului valutar. Actualizarea  cursului valutar se face zilnic la 
ora 4 dimineață, sau la prima pornire a serverului  pe ziua respectiv ă. Serverul 
aduce cursul valutar de pe un API de specialitate.  Obiectul  care vine ca răspuns 
de la API conține cursul valutar pentru toate valutele din baza de date, raportat 
la prima valută. Funcția care aduce cursul valutar calculeaz ă cursul raportat la 
fiecare valuta din bază de date și le salvează în cadrul array‐ului „rates” pe 
înregistrarea  corespunz ătoare. De asemenea,  actualizeaz ă valoarea proprietății 
„ratesFetched”  cu data la care s‐a realizat actualizarea  cursului.

APLICA ȚIE WEB DE PUBLICITATE  
 
51 
  3.3. Clientul
 
   La accesarea  rutei de bază a aplicației, routerul de Angular va încărca în 
memorie  modulul  Client. În cadrul acestui modul se regăsesc mai multe 
componente,  precum: 
• Home, această component ă conține componentele  care alcătuiesc 
pagina de acasă 
• Search, care conține template ‐ul cu inputul de alegere a locației și 
de introducere  a cuvintelor  cheie pentru căutare. În cadrul acestei 
componente  se află logica pentru typeahead ‐ul de locație. 
• Anunțuri, care conține componentele  de afișare a listei de anunțuri, 
sidebar‐ul de filtrare, pagina de detalii anunț 
• Add‐anunț, pagina de adăugare a unui anunț nou 
• Atributes,  componentele  care se ocupă cu afișarea atributelor  
folosite la filtrarea anunțurilor. În cadrul acestor componente  se 
află și logică care gestioneaz ă selectarea  detaliilor.  
• Paginile ce țin de administrarea  contului:  
◦ anunturi  
◦ favorite 
◦ setari 
  
  Aceste componente   sunt încărcate în interiorul  unui „router‐outlet” aflat 
în componenta  de baza, ClientComponent.  Bara de navigare,  reprezentat ă prin 
elementul  „app‐nav”, este creată o singura data la nivelul întregii aplicații. Ea 
este prezentă în cadrul componentei  AppComponent.

APLICA ȚIE WEB DE PUBLICITATE  
 
52 
  3.3.1. Acas ă
   Prima pagină este cea de acasă. În cadrul acestei componente  se află o 
secțiune de banner. Sub secțiune se află două rânduri de categorii,  câte șase pe 
rând. Aceste două rânduri sunt separate de SearchComponent,  care conține 
două câmpuri.   
 
 
Figura 3.3.1.1 Pagina de Acasă 
 
     Unul dintre câmpuri se folosește pentru selectarea  locației. Acest câmp 
funcționează în stilul „typeahead”.  Atunci când utilizatorul  scrie în el se afișează 
o listă cu sugestii de locații preluate din bază de date, care corespund  cu ceea ce 
a introdus.    
      În interior acestui input se află o iconiță de geo locație. Dacă utilizatorul  
dă click pe această iconiță, clientul va trimite o cerere către un API de geo locație. 
Acesta va stabili locația utilizatorului  pe baza adresei IP și returneaz ă

APLICA ȚIE WEB DE PUBLICITATE  
 
53 
  coordonate  în format  „[longitudine,  latitudine]”.  Aceste coordonate  sunt 
ulterior trimise în serverul aplicației.  Acesta va caută în baza de date locația cea 
mai apropiat ă. Folosirea  sintaxei „$near” este posibilă deoarece  câmpul 
„coordonate”  a colecției „localitate”  a fost definit cu un index de tip „2dsphere”.  
 
 
Figura 3.3.1.2 Typeahead ‐ul de locații 
 
const result = await Localitate .find({
coordonate : {
$near : {
$geometry : {
type : "Point",
coordinates : [params.long, params.lat]
},
$maxDistance : params.acc
}
}
}).populate ('judet');

APLICA ȚIE WEB DE PUBLICITATE  
 
54 
     Dacă utilizatorul  dă click pe una dintre categorii,  ambele rânduri vor fi 
înlocuite  cu subcategoriile  categoriei  selectate.  În partea de dreapta sus a paginii 
apare un buton pe care utilizatorul  îl poate folosi pentru a reveni la categorii.  La 
selectarea  unei subcategorii  se afișează atributele  pentru subcategoria  
respectiv ă. Din această listă sunt excluse atributele  forValue,  fiind prezente  doar 
cele cu detalii și forSorting.  
 
 
Figura 3.3.1.3 Atributele  pentru subcategoria  „Automobile  
3.3.2. Anun ț Nou
 
  Navigarea  către pagina de adăugare a unui anunț nou se poate face 
apăsând butonul Anunț Nou de pe bara de navigare.  În cadrul acestei pagini se 
regăsește un „stepper”  similar cu cel folosit în pagina de gestiune a 
subcategoriilor,  doar ca acesta este dispus pe verticala.  Parcurgerea  acestuia se 
face in mod liniar, ceea ce înseamnă ca utilizatorul  este obligat sa completeze  
anumiți pași înainte sa poată avansa prin el.

APLICA ȚIE WEB DE PUBLICITATE  
 
55 
    Primul pas este cel de selecție a subcategoriei.  În cadrul acestui pas sunt 
afișate toate categoriile  active  sub acelas format ca și pe pagina de acasă. 
Componenta  folosită pentru afișarea lor este CategoryCardComponent.  
  După ce se selecteaz ă o categorie  se avanseaz ă la cel de
 al doilea pas. În 
cadrul acestui pas se selecteaz ă subcategoria.  
 
 
Figura 3.3.2.1 Primul pas pentru adăugarea unui anunț nou

APLICA ȚIE WEB DE PUBLICITATE  
 
56 
   
  
Figura 3.3.2.2 Al treilea pas al stepper‐ului de adăugare anunț 
 
Odată ce au fost selectate  categoria  și subcategoria  utilizatorul  avanseaz ă 
la cel de al treilea pas, cel de completare  a detaliilor  anunțului. În cadrul acestui 
pas se afișează mai multe input‐uri și select‐uri, în cazul atributelor,  încadrate  
într‐o forma. Forma este de tip „reactiveForm”  și are validatori  în componenta  
paginii. Avansarea  la următorul pas este blocata atâta timp cât forma este 
invalida.  
  În partea de sus a formularului  se afla un input pentru titlu, acesta este 
obligatoriu  și are o limita de 70 de caractere.  Sub input se afla un indicator  care  
afișează numărul de caractere  ramase în timp real. De asemenea,  în partea de 
sus, se mai afla inputul de locație. Acest câmp este similar cu cel de pe pagina 
de acasă, singura diferența fiind stilul. El este de tip „typeahead”  și completarea  
acestuia este obligatorie.

APLICA ȚIE WEB DE PUBLICITATE  
 
57 
    Sub cele doua inputuri se afla atributele  subcategoriei  selectate.  Dacă 
atributele  au detalii ele sunt afișate sub forma unui select, în caz contrar este 
sunt inputuri de tip numeric.  Cele definite ca fiind obligatorii  vor preveni 
avansarea  prin „stepper”  pana în momentul  completării acestora.  Dacă un 
atribut are părinte este necesara  selectarea  unui detaliu pentru părinte înainte 
de completarea  lui. 
  Sub atribute se afla inputul pentru preț, un select pentru valuta și patru 
căsuțe de selecție. Căsuța de preț este bifat implicit, ceea ce permite introducerea  
unui preț. Dacă se bifează schimb input‐urile pentru preț  și valuta vor fi 
dezactivate,  la fel și cele pentru „Negociabil”  și „Gratuit”.  Dacă se bifează căsuța 
„Gratuit”,  câmpurile  vor fi din nou blocate iar prețul pentru anunț va fi 0. 
  Pasul trei se termina cu un câmp pentru descriere.  Acesta are un contor 
de caractere  ramase și este limitat la 9000 de caractere.  
  Dacă toate inputurile  din formular  sunt valide utilizatorul  poate avansa 
la cel de al patrulea pas. În cadrul acestui pas, utilizatorul  poate adaugă pozele 
anunțului. Numărul de poze disponibile  este diferit de la o subcategorie  la alta. 
Acesta se stabilește în momentul  definirii subcategoriei.  Adăugarea pozelor nu 
este obligatorie,  utilizatorul  având posibilitatea  de a avansa direct la ultimul 
pas.

APLICA ȚIE WEB DE PUBLICITATE  
 
58 
    
Figura 3.3.2.3 Al patrulea pas al stepper‐ului de adăugare anunț 
 
În cadrul ultimulului  pas, utilizatorul  are posibilitatea  de a introduce  date 
de contact, dacă acesta nu este înregistrat.  Dacă este înregistrat  și are setate date 
de contact implicite  acestea se completeaz ă automat.  De asemenea,  acesta poate 
alege, folosind un „checkbox”,  dacă dorește să fie contactat  prin mesagerie  
pentru anunțul respectiv.  De aici utilizatorul  poate sa publice anunțul sau sa îl 
pre vizualizeze.  Dacă alege sa îl pre vizualizeze  el va fi redirecționat către 
pagina de detalii unde anunțul afișat este cel pe care dorește să‐l adauge. Dacă 
considera  ca este în regulă îl poate adaugă apăsând pe butonul Adăugare. 
3.3.3. Anun țuri
 
   Navigarea  către pagina de anunțuri se face atunci când utilizatorul  apăsa 
pe iconiță de căutare din inputul „Caută”. Înainte de a realiză navigarea,  se 
compune  URL‐ul paginii de anunțuri. URL‐ul paginii va conține ca parametri

APLICA ȚIE WEB DE PUBLICITATE  
 
59 
  de query perechi cheie‐valoare pentru fiecare atribut selectat în timpul filtrării 
inițiale din pagina de acasă. Cheia reprezint ă id‐ul atributului  și valoare 
reprezint ă ori id‐ul detaliului  selectat, în cazul atributelor  cu detalii, ori valoarea 
introdus ă manual de utilizator,  în cazul atributelor  folosite pentru
 sortare. Alți 
parametri   URL posibili sunt:  id‐ul subcategoriei  selectate,  id‐ul valutei în care 
se va face afișarea prețurilor anunțurilor și filtrarea după preț, id‐ul locației 
selectate și textul de query introdus  în inputul de căutare.  De asemenea,  înainte 
de a se realiza navigarea  se aduc anunțurile care corespund  cu filtrele alese de 
utilizator.  
      Filtrarea anunțurilor se realizeaz ă pe server în cadrul funcției 
getAnunturi  a controllerului  de anunțuri. Această funcție tratează o cerere de 
tip POST, care conține toate atributele  selectate de utilizator  în format JSON. 
 
 
Figura 3.3.3.1 Exemplu  de obiect trimis ca body pentru cererea de getAnunturi  
 
În cadrul acestei funcții se construie ște un obiect de query MongoDB  în funcție 
de valorile din obiectul trimis că body. Valoarea  atributelor  din array‐ul de 
atribute, care nu sunt forSorting,  este adăugat în array‐ul care reprezint ă 
valoarea cheii $and din query. Cheia $and îndepline ște un rol similar cu sintaxa  
„WHERE  AND” din limbajul SQL.  După ce se construie ște obiectul de query se

APLICA ȚIE WEB DE PUBLICITATE  
 
60 
  execută query‐ul. Valorile returnate  sunt apoi filtrate încă o dată, de data această 
cu atributele  forSorting.  În cazul în care atributul  sortează prețul, valoarea 
pentru atribut este mai întâi convertit ă în valută în care a fost adăugat anunțul 
filtrat și  apoi se face filtrarea.
 Anunțurile rămase după această etapă de filtrare 
sunt returnate  în client unde sunt afișate în cadrul paginii de anunțuri. 
  
 
Figura 3.3.3.2 Exemplu  de rezultat a filtrării anunțurilor din subcategoria  Automobile  
după marca și preț 
 
   Componen ța AnuntiriComponent  este componen ța de bază în cadrul 
paginii de anunțuri. În stânga paginii se află un o bara care conține inputurile  
din SearchComponent,  două carduri de tip CategoryCardComponent,  un select 
cu valutele disponibile,  cele două atribute de tip AtributeComponent  folosite la 
sortarea anunțurilor după preț, atributele  care țin de categoria  selectată și un 
buton de filtrare, la apăsarea căruia se realizeaz ă filtrarea propriu‐zisă a

APLICA ȚIE WEB DE PUBLICITATE  
 
61 
  anunțurilor. Câmpurile  din SearchComponent  funcționează la fel că  și pe 
pagină de acasă, câmpul pentru locație fiind un „typeahead”  pentru locațiile 
din baza de date. Cardurile  de categorie  și subcategorie  funcționează ca afișaj 
pentru categoria,  respectiv  subcategoria,  selectată. Dacă utilizatorul  ap
ăsa pe 
cardul de categorie  se deschide  un modal de unde poate selecta una dintre 
categoriile  disponibile.  Odată ce utilizatorul  a selectat o categorie  acesta are 
acces la cardul ce reprezint ă subcategoria,  funcționalitatea  ce ține de selectare  
fiind similară. După ce se selecteaz ă o subcategorie  se vor afișa în bara din 
stânga atributele  ce țin de ea. Selectul de valută are ca valoare selectată implicită 
valută „EUR”. Dacă utilizatorul  schimbă valuta selectată se realizeaz ă conversia  
prețurilor anunțurilor afișate în noua valută. Această conversie  se face pe client. 
Atributele  pentru sortarea în funcție de preț sunt prezente  permanent,  
indiferent  dacă s‐a selectat o subcategorie   sau nu. Filtrarea anunțurilor în 
funcție de preț se realizeaz ă în valuta selectată pe client, indiferent  de valuta în 
care s‐a creat anunțul. În cazul în care utilizatorul  a selectat o subcategorie  
acesta are acces la atributele  ce țin de subcategoria  respectiv ă. Aceste atribute 
funcționează la fel ca și în pagină de acasă, fiind aceeași component ă. 
Utilizatorul  poate actualiza  lista de anunțuri folosind butonul „Filtreaz ă”.  
      În partea stânga a componen ței AnunturiComponent  avem anunțurile 
propriu‐zise. Acestea sunt afișate în ordine crescătoare a datei la care au fost 
adăugate. Pentru fiecare anunț sunt afișate: prima imagine,  titlul, locația, dată 
adăugării cu numărul de zile care au trecut de atunci, prețul și iconiță de 
adăugare la favorite. Această secțiune funcționează cu principiul  „infinite 
scroll”. Cererea inițială aduce primele 50 de anunțuri care corespund  cu filtrele

APLICA ȚIE WEB DE PUBLICITATE  
 
62 
  introduse  iar atunci când utilizatorul  ajunge cu bara de scroll aproape de 
capătul paginii se aduc următoarele 50 și așa mai departe. 
      În cazul în care utilizatorul  acceseaz ă pagina de anunțuri direct cu URL, 
acesta nerealizând  navigarea  de pe pagina de acasă, se 
completeaz ă automat 
categoria  selectată și  subcategoria  selectă, dacă este cazul, valorile pentru 
atribute și se aduc anunțurile corespunz ătoare.   
      Utilizatorul  poate naviga spre pagina de detalii prin click pe titlul sau 
poza unui anunț. 
 
3.3.4. Detalii anun ț
 
  
Figura 3.3.4.1 Pagina de detaliu pentru un anunț

APLICA ȚIE WEB DE PUBLICITATE  
 
63 
     URL‐ul pentru această pagină este compus din titlul anunțului și id‐ul 
anunțului ca și parametru  de query. Partea de titlu din URL este ignorată, ea 
având rolul de a face URL‐ul mai lizibil. ID‐ul anunțului este obligatoriu  iar 
dacă se 
încearcă navigarea  folosind un id fals sau fără id utilizatorul  este 
redirecționat spre pagina de 404.       
      Pagina conține titlul anunțului și prețul, în partea de sus. De asemenea  
sunt vizibile două butoane de navigare:  „Anunțul precedent”  și „Anunțul 
Următor”. Aceste butoane sunt vizibile doar dacă utilizatorul  a ajuns pe această 
pagină din pagină de filtrare a anunțurilor. Anunțurile filtrate sunt stocate în 
Store‐ul aplicației, în reductorul  „anunț” și sunt disponibile  în pagină de detalii. 
Utilizatorul  are posibilitatea  de a naviga prin anunțurile deja încărcate, fără să 
fie nevoit să se reîntoarc ă în pagină de filtrare. Dacă utilizatorul  navigheaz ă prin 
anunțuri folosind butonul „anunțul următor” și ajunge la penultimul  anunț 
încărcat, clientul va trimite automat o cerere către server, folosind același obiect 
de query cu care a fost filtrat anunțul curent, pentru a aduce următoarele 100 de 
anunțuri. Deoarece  această navigare  introduce  înregistrări în istoricul 
browserului,  butonul de înapoi al acestuia nu va mai fi de folos utilizatorului  în 
cazul în care dorește să se întoarcă la pagină de filtrare a anunțurilor. De aceea 
în partea de stânga sus, pe bară de navigare,  apare un buton care îl 
redirecționează spre acea pagină. Pagină va fi completat ă cu toate anunțurile 
încărcate iar atributele  vor fi completate  automat,  dacă este cazul. Acest lucru 
este posibil doar dacă utilizatorul  a ajuns pe pagină de detalii prin navigare  din 
pagina de filtrare. În caz contrat, obiectul de query și anunțurile pe care acesta 
l‐a filtrat nu se află în Store‐ul aplicației.

APLICA ȚIE WEB DE PUBLICITATE  
 
64 
        Sub titlul anunțului se află imaginile  care au fost publicate  de către 
autorul anunțului în momentul  adăugării acestuia.  Partea stânga a paginii 
continuă cu o secțiune care conține avatarul utilizatorului  care a publicat 
anunțul și numele acestuia.  Lângă aceste elemente  
se află data publicării 
împreun ă cu numărul de zile de la aceasta. Partea stânga se încheie cu descrierea  
anunțului și numărul de vizualizări.   
      În partea stânga se află prețul și două butoane:  unul de contactarea    
 vânzătorului  unul de adăugare la favorite  anunțului.   La apasarea  butonului  
de contact se deschide  un modal din  care se poate alege dintre doua metode de 
contact:  prin telefon  sau prin mesagerie.  
 
  
  
 
 
 
 
  
 
Figura 3.3.4.2 Modalul  de contact pentru un anunț

APLICA ȚIE WEB DE PUBLICITATE  
 
65 
  
3.3.5. Mesageria Instant ă
 
  Partea de client dispune de un sistem de mesagerie  instantă intre 
utilizatori.  Dacă utilizatorul  alege metoda de contact Mesaj din dialogul ce 
contact pentru un anunț se deschide  o fereastră în partea de dreapta jos a paginii 
prin care acesta poate comunica  cu utilizatorul  care a adăugat anunțul. 
  Mesajele  trimise de către utilizatorul  activ sunt afișate în partea dreapta 
iar cele primite în partea stânga. Dacă celălalt  utilizator  a văzut mesajul acest 
lucru se afișează în fereastra.  
 
Figura 3.3.5.1 Mesaj văzut

APLICA ȚIE WEB DE PUBLICITATE  
 
66 
    Mesageria  funcționează folosind un pachet numit socket.io.  Prin 
intermediul  acestuia clientul comunica  în permanenta  cu serverul,  prin 
protocolul  WebSocket.  Acest socket funcționează prin evenimente.  Atunci când 
un utilizator  se autentifica  pe client se creează o conexiune  cu serverul iar 
serverul răspunde cu conversa țiile recente. 
Aceste conversa ții sunt afișate într‐
un meniu disponibil  pe toate paginile aplicației. Meniul se poate deschide  
folosind iconița specifica  de pe bara de navigație. Dacă utilizatorul  da click pe 
una dintre conversa ții se deschide  fereastra  de chat și se cer mesajele  ce țin de 
conversa ția respectiva  de la server. 
 
Figura 3.3.5.2 Meniul cu conversa ții recente 
 
  În momentul  în care un utilizator  trimite un mesaj, destinatarul   va primii 
mesajul respectiv  prin intermediul  socket‐ului și se va deschide  automat 
fereastra  de chat, în cazul în care aceasta a fost închisă. Dacă utilizatorul  da click

APLICA ȚIE WEB DE PUBLICITATE  
 
67 
  pe fereastra  de chat se emite evenimentul  care tine de faptul ca acesta a văzut 
mesajul iar utilizatorul  care l‐a trimis va fi notificat de către socket ca mesajul a 
fost văzut. Dacă în momentul  primirii mesajului  tabul în care este deschisă 
aplicația nu
 este activ se browser‐ul va reda un sunet de notificare  iar titlul 
tabului va afișa intermitent  faptul ca utilizatorul  a primit un mesaj nou. 
 
3.3.6. Anun țurile Tale
  În cadrul acestei pagini utilizatorul  poate gestiona anunțurile pe care le‐
a adăugat, atât cele active cât și cele inactive. El poate modifica  un anunț activ 
sau îl poate șterge. De asemenea  poate dezactiva  un anunț activ sau poate 
reactiva un anunț inactiv. 
 
 
    Figura 3.3.6.1 Pagina de Anunțurile Tale

APLICA ȚIE WEB DE PUBLICITATE  
 
68 
  3.3.7. Set ări
 
  În aceasta pagina utilizatorul  își poate modifica  setările contului.  Acesta 
își poate schimba numele de utilizator,  locația și nr telefon. Dacă  utilizatorul  își 
adaugă locația și numărul de telefon acestea vor fi completate  automat în 
momentul  adăugării unui anunț nou. 
  De asemenea,  în aceasta pagina utilizatorul  își poate schimba adresa de 
e‐mail sau parola. În ambele cazuri este necesara  confirmarea  schimbării prin 
intermediul  e‐mailului care se trimite automat.

APLICA ȚIE WEB DE PUBLICITATE  
 
69 
  Capitolul 4  
Concluzii
4.1 Dezvolt ări în viitor
  
  Aplicația își atinge obiectivul,  punând la dispoziția utilizatorilor  o 
platform ă modernă și dinamică, prin intermediul  căreia aceștia pot publica cu 
usurință și gratuit anunțuri de mică publicitate.     
Pentru a pregăti aplicația de producție, însă, sunt necesare  anumite 
îmbunătățiri. Este necesară scrierea de teste automate,  aplicația în formatul  
actual fiind testată manual, ceea ce nu este optim. Baza de date ar mai putea fi 
normalizat ă iar volumul  de date tranzacționate între client și server redus. 
Aceasta poate fi dezolvat ă prin adaugarea  de funcționalitate  nouă, 
precum un sistem de reputație pentru utilizatori,  metode mai complexe  de 
filtrare al anunțurilor și instrumente  de administrare.  
  Deoarece  serverul este facut dupa modelul REST API, îmi propun sa creez 
versiuni mobile pentru Android  și iOS, numărul utilizatorilor  care folosesc 
telefonul  mobil pentru navigarea  pe internet fiind în continuă creștere.

APLICA ȚIE WEB DE PUBLICITATE  
 
70 
   
 
 
Bibliografie
      
[1] N.Murray,  A. LERNER , F.Coury și C.Taborda, Ng‐Book: The Complete  Guide to 
Angular , CreateSpace  Independent  Publishing  Platform,  2018
[2] B. SYED, Beginning  Node.js , Apress, 2014 
[3] B. SYED, Typescript  Deep Dive, ARTPOWER  International  PUB, 2017 
[4] J.DICKEY , Write Modern Web apps with the MEAN stack: Mongo, Express, 
AngularJS,  and Node.js , Peachpit  Press, 2014 
[5] A.N AYAK , MongoDB  Cookbook , Packt Publishing  Ltd, 2014

Similar Posts