Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14,699 changes: 14,699 additions & 0 deletions code-snapshots/14-routing/17-fixes/package-lock.json

Large diffs are not rendered by default.

7 changes: 4 additions & 3 deletions code-snapshots/14-routing/17-fixes/src/app/app.component.html
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
<app-header />
<app-header />

<main>
<app-users />
<app-users /> <!-- lista e users -->

<div>
<router-outlet />
<router-outlet /> <!-- komponentet te app.routes.ts -->
</div>
</main>

Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ import { UsersComponent } from './users/users.component';
imports: [HeaderComponent, UsersComponent, RouterOutlet],
})
export class AppComponent {}

17 changes: 13 additions & 4 deletions code-snapshots/14-routing/17-fixes/src/app/app.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,23 @@ import {

import { routes } from './app.routes';

// withComponentInputBinding() -> perdoret per te regjistruar te dhena ne url ose routes,
// brenda data dhe resolve, te cilat i thersim me input te komponenti lokal qe i kemi
// bashkagjitur keto features.

// withRouterConfig({ paramsInheritanceStrategy: 'always' }) -> perdoret per ti dhen
// askses femis se nje routes te mari me input vlerat e prindit, pra: data, resolve
// userId qe ndodhet ne url, njesoj sic e mernim dikur nga activatedRoute me paramMap.

export const appConfig: ApplicationConfig = {
providers: [
provideRouter(
routes,
withComponentInputBinding(),
routes, // Konfigurimi i rrugëve të cilat janë importuar
withComponentInputBinding(),
withRouterConfig({
paramsInheritanceStrategy: 'always',
paramsInheritanceStrategy: 'always',
})
),
],
};
};

49 changes: 31 additions & 18 deletions code-snapshots/14-routing/17-fixes/src/app/app.routes.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { CanMatchFn, RedirectCommand, Router, Routes } from '@angular/router';
import { inject } from '@angular/core';

import { routes as userRoutes } from './users/users.routes';
// 'as' eshte nje metode, i cili modifikon emertimin e merit, nga routes, e therasim userRoutes
import { routes as userRoutes } from './users/users.routes';
import { NoTaskComponent } from './tasks/no-task/no-task.component';
import {
UserTasksComponent,
Expand All @@ -10,38 +11,50 @@ import {
} from './users/user-tasks/user-tasks.component';
import { NotFoundComponent } from './not-found/not-found.component';

// Funksioni dummyCanMatch është një funksion për përputhje të rrugëve (route matching) që
// përdoret për të vendosur nëse një përdorues duhet të ketë akses në një rrugë specifike.
// Ky funksion mund të krahasohet me një gardian që vendos nëse një rrugë duhet të
// aktivizohet ose nëse përdoruesi duhet të ridrejtohet diku tjetër

const dummyCanMatch: CanMatchFn = (route, segments) => {
const router = inject(Router);
const shouldGetAccess = Math.random();
if (shouldGetAccess < 1) {
return true;
const shouldGetAccess = Math.random(); // // Gjeneron një numër të rastësishëm midis 0 dhe 1
// console.log('Gardian shouldGetAccess ', shouldGetAccess);
if (shouldGetAccess < 1) { // // Nëse numri është më i vogël se 1, lejon aksesin
return true; // // Kjo rrugë është e vlefshme dhe përdoruesi mund të ketë qasje
}
// // Përndryshe, ridrejtohet te '/unauthorized'
return new RedirectCommand(router.parseUrl('/unauthorized'));
};

export const routes: Routes = [
{
path: '', // <your-domain>/
{
path: '',
component: NoTaskComponent,
// redirectTo: '/users/u1',
// redirectTo: '/users/u1',
// pathMatch: 'full'
title: 'No task selected',
},
{
path: 'users/:userId', // <your-domain>/users/<uid>
component: UserTasksComponent,
children: userRoutes,
canMatch: [dummyCanMatch],
data: {
message: 'Hello!',
{
path: 'users/:userId',
component: UserTasksComponent,
children: userRoutes, // femijest e prinderve te nje routes, quhen 'Nested Routes'.
canMatch: [dummyCanMatch],
// 1. si data dhe resolve jan dy feature te ciles i bashkagjit te dhena brenda url.
// 2. keto te dhena i marim nga: this.activatedRoute.data.subscribe(data => console.log('data routes', data));
// 3. ose i marim me input: userName = input.required<string>(); dhe message = input.required<string>();
// 4. per te funksjonuar keto features of Routes, shtojme ne app.config.ts: withComponentInputBinding()
data: { // data mer vlera statike
message: 'Hello!',
},
resolve: {
userName: resolveUserName,
resolve: { // resolve mer data dinamike, te ndryshushme
userName: resolveUserName,
},
title: resolveTitle,
title: resolveTitle, // ** kjo e shfaq emrin e user brenda ikones se faqes **
},
{
path: '**',
component: NotFoundComponent,
},
];
];

Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,18 @@ <h2>Add Task</h2>
rows="5"
name="summary"
[(ngModel)]="enteredSummary"
></textarea>
></textarea>
</p>

<p>
<p>
<label for="due-date">Due Date</label>
<input
type="date"
id="due-date"
name="due-date"
[(ngModel)]="enteredDate"
/>
</p>
</p>

<p class="actions">
<a routerLink="../">Cancel</a>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Component, inject, input, signal } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { CanDeactivateFn, Router, RouterLink } from '@angular/router';
import { ActivatedRoute, CanDeactivateFn, Router, RouterLink } from '@angular/router';

import { TasksService } from '../tasks.service';

Expand All @@ -12,21 +12,37 @@ import { TasksService } from '../tasks.service';
styleUrl: './new-task.component.css',
})
export class NewTaskComponent {
userId = input.required<string>();
// 1. marim me input userId nga url: http://localhost:4200/users/u2/tasks/new, pra u2
// njesoj si kur ta mernim nga activatedRoute me paramMap duke perdorur features
// of Routes: withRouterConfig({ paramsInheritanceStrategy: 'always' }).

// 2. per vec userId, marim message dhe userName.

// 3. vem re se nuk mund te marim vlerat userTasks te komponentit TasksComponent edhe pse
// eshte femija i komponentit prind te perbashket
userId = input.required<string>();

enteredTitle = signal('');
enteredSummary = signal('');
enteredDate = signal('');
enteredDate = signal('');
submitted = false;

private tasksService = inject(TasksService);
private router = inject(Router);

// private activatedRoute = inject(ActivatedRoute);

// ngOnInit(): void {
// this.activatedRoute.data.subscribe(data => console.log('data routes', data));
// }

onSubmit() {
this.tasksService.addTask(
{
title: this.enteredTitle(),
title: this.enteredTitle(),
summary: this.enteredSummary(),
date: this.enteredDate(),
},
},
this.userId()
);
this.submitted = true;
Expand All @@ -37,12 +53,20 @@ export class NewTaskComponent {
}
}

// Krijojme nje funksjon Guard i cili e parandalon user, nese ka vlera brenda form dhe ai do te lundroj ne nje faqe tjeter, i shfaqet
// nje dialog me mesazhin: ' Do you really want to leave? You will lose the entered data. '.
export const canLeaveEditPage: CanDeactivateFn<NewTaskComponent> = (component) => {

// Nëse submitted është true, përdoruesi ka dërguar formën, prandaj largimi është i sigurt dhe funksioni kthen true për të lejuar largimin.
if (component.submitted) {
return true;
}

// nese nje nga input eshte flase na shaqet dialog me mesazhin me posht, duke na dhen akses te kalojme ne nje faqe tjeter.
if (component.enteredTitle() || component.enteredDate() || component.enteredSummary()) {
return window.confirm('Do you really want to leave? You will lose the entered data.')
}

// Nëse përdoruesi pranon mesazhin, true lejon largimin; përndryshe, false e bllokon atë dhe e lë përdoruesin në të njëjtën faqe.
return true;
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,14 @@ import { TasksService } from '../tasks.service';
})
export class TaskComponent {
task = input.required<Task>();

private tasksService = inject(TasksService);

// Router është një shërbim që përdoret për të naviguar në rrugë (routes) të ndryshme të aplikacionit.
private router = inject(Router);

// ActivatedRoute është një shërbim që jep informacion mbi rrugën aktuale të aktivizuar, duke përfshirë
// parametra, query params, dhe segmente rruge.
private activatedRoute = inject(ActivatedRoute);

onComplete() {
Expand All @@ -28,3 +34,24 @@ export class TaskComponent {
});
}
}

// relativeTo: this.activatedRoute:
// Kjo përcakton që rruga ku do të navigohet duhet të jetë relative ndaj rrugës aktuale.
// activatedRoute përmban të dhëna mbi rrugën aktuale të aktivizuar, dhe duke e përdorur
// këtë opsion, mund të vazhdosh navigimin nga një rrugë relative.

// onSameUrlNavigation: 'reload':
// Ky opsion përcakton se çfarë ndodh nëse navigon në të njëjtën URL që je aktualisht.
// 'reload' thotë që edhe nëse je në të njëjtën rrugë, komponenti duhet të rifreskohet
// plotësisht. Pa këtë opsion, Angular nuk do ta rifreskonte komponentin nëse je duke
// naviguar në të njëjtën rrugë.

// queryParamsHandling: 'preserve':
// Ky opsion ruan parametrat ekzistues të query nga URL-ja kur bën navigimin.
// 'preserve' siguron që parametrat aktualë të query të ruhen në URL edhe pasi të ketë
// ndodhur navigimi. Pa këtë, parametrat mund të humben kur ndryshon rruga.

// Permbledhja:
// Ky kod po bën një navigim programatik brenda rrugës aktuale relative ndaj rrugës së
// aktivizuar (ActivatedRoute), duke ruajtur parametrat e query që ndodhen aktualisht
// në URL dhe duke siguruar që komponenti të rifreskohet nëse po navigon në të njëjtën rrugë.
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@

<!-- sinjali input: order = input<'asc' | 'desc' | undefined>(); vler fillestare ka undefined, duke
qe undefined, funksjoni if - else nuk e lexon kushtin === me 'asc', duke mos e lexuar ai shfaq
'asc' and 'Ascending', kur e shtypim, input mer vleren 'asc' dhe funksjoni behet 'desc' ose 'Descending' -->
<p>
<a routerLink="./" [queryParams]="{order: order() === 'asc' ? 'desc' : 'asc'}">
Sort {{ order() === 'asc' ? 'Descending' : 'Ascending' }}
Sort {{ order() === 'asc' ? 'Descending' : 'Ascending' }} - {{order()}}
</a>
</p>


<ul>
@for (task of userTasks(); track task.id) {
<li>
Expand All @@ -13,3 +18,6 @@
<p>There are no tasks yet. Start adding some!</p>
}
</ul>



74 changes: 64 additions & 10 deletions code-snapshots/14-routing/17-fixes/src/app/tasks/tasks.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {
inject,
input,
} from '@angular/core';
import { ResolveFn, RouterLink } from '@angular/router';
import { ActivatedRoute, ResolveFn, RouterLink } from '@angular/router';

import { TaskComponent } from './task/task.component';
import { TasksService } from './tasks.service';
Expand All @@ -17,28 +17,82 @@ import { Task } from './task/task.model';
imports: [TaskComponent, RouterLink],
})
export class TasksComponent {
userTasks = input.required<Task[]>();
userId = input.required<string>();

// marim array me objecte me input nga users.routes.ts resolve: { userTasks: resolveUserTasks } nga TasksComponent.
userTasks = input.required<Task[]>();

// 1. kemi nje input sinjal i cili permban 3 vlera, fillestare undefind, asc dhe desc

// 2. [queryParams]="{order: order() === 'asc' ? 'desc' : 'asc'}", kur e shtypim linkun
// gjenerojme nje objekt: { order: 'asc' } ose { order: 'desc' }, i cili bashkagjitet ne url:
// http://localhost:4200/users/u2/tasks?order=asc or http://localhost:4200/users/u2/tasks?order=desc

// 3. ku nga const order = activatedRouteSnapshot.queryParams['order']; marim string asc ose desc.
order = input<'asc' | 'desc' | undefined>();

// private activatedRoute = inject(ActivatedRoute);
// userId = input.required<string>();
// userName = input.required<string>();
// message = input.required<string>();

ngOnInit(): void {
// this.activatedRoute.data.subscribe(data => console.log('data routes', data));
// this.activatedRoute.queryParams.subscribe(data => console.log('data params', data));
}
}

export const resolveUserTasks: ResolveFn<Task[]> = (
// 1. angular 18, na mundeson nje menyr te re, si ti bashkagjisim disa data brenda platformes routes.

// 2. ne rastin ton gjejme objektet qe jan krijuar nga nje user by id, dhe i bashkagjisim ne:
// user.routes.ts resolve: { userTasks: resolveUserTasks }, duke e theritur me input ne kete komponent:
// userTasks = input.required<Task[]>();.

// 3. pra user.routes.ts eshte femija i prinderit { path: 'users/:userId', component: UserTasksComponent }
// dhe cfare datash kemi regjistruar prenda prinderit dhe femis si: data: { message: 'Hello!' },
// resolve: { userName: resolveUserName }, title: resolveTitle, resolve: { userTasks: resolveUserTasks },
// i theras te dhenat e tyre me inputs: userId = input.required<string>(); userName = input.required<string>();
// message = input.required<string>(); per ti trasferuar ne template ku ne i shofim ato lehtesisht me subscribe
// nga: this.activatedRoute.data.subscribe(data => console.log('data routes', data));,

// 4. nese nuk shtojme ne route kete sinstaks withComponentInputBinding(), e gjith llogjika e mesiperme nuk punon.

export const resolveUserTasks: ResolveFn<Task[]> = (
activatedRouteSnapshot,
routerState
routerState
) => {
const order = activatedRouteSnapshot.queryParams['order'];

// 1. Nëse order është 'asc', rendit lista e tasks sipas id-së në mënyrë ngjitëse (nga më e
// vogla te më e madhja).

// 2. Nëse order nuk është 'asc' (ose nuk ka fare vlerë), rendit lista e tasks sipas id-së
// në mënyrë zbritëse (nga më e madhja te më e vogla).

const order = activatedRouteSnapshot.queryParams['order'];
// console.log("orderrrrrrr", order)

const tasksService = inject(TasksService);
const tasks = tasksService
.allTasks()
.filter(

// paramMap.get('userId') mer id nga url per te gjetur task e user
const tasks = tasksService.allTasks().filter(
(task) => task.userId === activatedRouteSnapshot.paramMap.get('userId')
);

// Kjo kontrollon nëse ekziston një vlerë për order dhe nëse ajo është 'asc', që do të thotë
// renditje ngjitëse (nga më e vogla te më e madhja).
if (order && order === 'asc') {
// Ky bllok përdor metodën sort() të JavaScript-it për të renditur elementët në listën tasks
// sipas vlerës së id-së së secilit element (a dhe b janë dy elemente të listës).
// Nëse a.id > b.id, rendit a pas b, kështu që kthen 1.
// Nëse a.id është më i vogël ose i barabartë me b.id, rendit a para b, kështu që kthen -1.
tasks.sort((a, b) => (a.id > b.id ? 1 : -1));
} else {
tasks.sort((a, b) => (a.id > b.id ? -1 : 1));
}

return tasks.length ? tasks : [];
// si funksjonon sort():
// Krahason vlerën e a.id me vlerën e b.id.
// Nëse a.id është më i madh se b.id, atëherë kthen 1, që do të thotë se a vendoset pas b në renditjen përfundimtare.
// Nëse a.id është më i vogël ose i barabartë me b.id, atëherë kthen -1, që do të thotë se a vendoset para b.

return tasks.length ? tasks : []; // return nese length > 0 -> array task nga lart posht ose nga posht lart
};
Loading