feat(challenge36): add solution on trackby

This commit is contained in:
thomas
2023-10-02 21:42:47 +02:00
parent 0a5bc8514c
commit c062613a8e
30 changed files with 564 additions and 20 deletions

View File

@@ -0,0 +1,58 @@
import { Component, OnInit, inject } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatInputModule } from '@angular/material/input';
import { PersonService } from './list.service';
import { PersonListComponent } from './person-list.component';
@Component({
standalone: true,
imports: [
PersonListComponent,
FormsModule,
MatFormFieldModule,
MatInputModule,
],
providers: [PersonService],
selector: 'app-root',
template: `
<h1 class="font-semibold text-center text-3xl" title="Title">
List of Persons
</h1>
<mat-form-field class="w-3/4">
<input
placeholder="Add one member to the list"
matInput
type="text"
[(ngModel)]="label"
(keydown)="handleKey($event)" />
</mat-form-field>
<app-person-list
class="max-w-2xl w-3/4"
[persons]="persons()"
(delete)="personService.deletePerson($event)"
(update)="personService.updatePerson($event)" />
`,
host: {
class: 'flex items-center flex-col gap-5',
},
})
export class AppComponent implements OnInit {
readonly personService = inject(PersonService);
readonly persons = this.personService.persons;
label = '';
ngOnInit(): void {
this.personService.loadPersons();
}
handleKey(event: any) {
if (event.keyCode === 13) {
this.personService.addPerson(this.label);
this.label = '';
}
}
}

View File

@@ -0,0 +1,6 @@
import { ApplicationConfig } from '@angular/core';
import { provideAnimations } from '@angular/platform-browser/animations';
export const appConfig: ApplicationConfig = {
providers: [provideAnimations()],
};

View File

@@ -0,0 +1,15 @@
import { randEmail, randFirstName } from '@ngneat/falso';
import { Person } from './person.model';
export function generateList() {
const arr: Person[] = [];
for (let i = 0; i < 50; i++) {
arr.push({
email: randEmail(),
name: randFirstName(),
});
}
return arr;
}

View File

@@ -0,0 +1,44 @@
import { Injectable, inject, signal } from '@angular/core';
import { randEmail, randFirstName } from '@ngneat/falso';
import { generateList } from './generateList';
import { Person } from './person.model';
@Injectable()
export class PersonService {
private readonly fakeBackend = inject(FakeBackendService);
readonly persons = signal<Person[]>([]);
loadPersons() {
this.persons.set(generateList());
}
deletePerson(email: string) {
this.persons.set(
this.fakeBackend
.returnNewList(this.persons())
.filter((p) => p.email !== email)
);
}
updatePerson(email: string) {
this.persons.set(
this.fakeBackend
.returnNewList(this.persons())
.map((p) => (p.email === email ? { email, name: randFirstName() } : p))
);
}
addPerson(name: string) {
this.persons.set([
{ email: randEmail(), name },
...this.fakeBackend.returnNewList(this.persons()),
]);
}
}
@Injectable({ providedIn: 'root' })
export class FakeBackendService {
returnNewList = (input: Person[]): Person[] => [
...input.map((i) => ({ ...i })),
];
}

View File

@@ -0,0 +1,37 @@
import { Component, EventEmitter, Input, Output } from '@angular/core';
import { CommonModule } from '@angular/common';
import { Person } from './person.model';
@Component({
selector: 'app-person-list',
standalone: true,
imports: [CommonModule],
template: `
<div
*ngFor="let person of persons"
class="flex justify-between items-center border-b">
<h3>{{ person.name }}</h3>
<div class="flex gap-10 py-1">
<button
class="border rounded-md p-2 bg-blue-500 text-white"
(click)="update.emit(person.email)">
UPDATE
</button>
<button
class="border rounded-md p-2 bg-red-500 text-white"
(click)="delete.emit(person.email)">
DELETE
</button>
</div>
</div>
`,
host: {
class: 'w-full flex flex-col',
},
})
export class PersonListComponent {
@Input() persons: Person[] = [];
@Output() delete = new EventEmitter<string>();
@Output() update = new EventEmitter<string>();
}

View File

@@ -0,0 +1,4 @@
export interface Person {
email: string;
name: string;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>performance-ngfor-optimize</title>
<base href="/" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="icon" type="image/x-icon" href="favicon.ico" />
</head>
<body>
<app-root></app-root>
</body>
</html>

View File

@@ -0,0 +1,7 @@
import { bootstrapApplication } from '@angular/platform-browser';
import { appConfig } from './app/app.config';
import { AppComponent } from './app/app.component';
bootstrapApplication(AppComponent, appConfig).catch((err) =>
console.error(err)
);

View File

@@ -0,0 +1,5 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
/* You can add global styles to this file, and also import other style files */

View File

@@ -0,0 +1,2 @@
import '@testing-library/jest-dom';
import 'jest-preset-angular/setup-jest';