{"id":23408121,"url":"https://github.com/fmalcher/book-monkey6-draft","last_synced_at":"2025-10-29T21:31:21.650Z","repository":{"id":267470848,"uuid":"900991209","full_name":"fmalcher/book-monkey6-draft","owner":"fmalcher","description":null,"archived":false,"fork":false,"pushed_at":"2025-01-29T21:38:02.000Z","size":152,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-01-29T22:29:12.128Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/fmalcher.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-12-09T20:58:39.000Z","updated_at":"2025-01-29T21:38:05.000Z","dependencies_parsed_at":"2024-12-10T15:19:18.874Z","dependency_job_id":"f9b61db4-85aa-43da-8853-a1863eba6a39","html_url":"https://github.com/fmalcher/book-monkey6-draft","commit_stats":null,"previous_names":["fmalcher/book-monkey6-draft"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fmalcher%2Fbook-monkey6-draft","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fmalcher%2Fbook-monkey6-draft/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fmalcher%2Fbook-monkey6-draft/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fmalcher%2Fbook-monkey6-draft/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/fmalcher","download_url":"https://codeload.github.com/fmalcher/book-monkey6-draft/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":238896982,"owners_count":19548901,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-12-22T14:38:19.988Z","updated_at":"2025-10-29T21:31:21.289Z","avatar_url":"https://github.com/fmalcher.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# BookMonkey 6 Draft\n\n- Mehr Fokus auf Praxisprojekt, die Buchstruktur orientiert sich an den Umsetzungsthemen, nicht zwingend an Fachthemen\n- Ein Kapitel muss nicht immer ein ganzes Thema abdecken, sondern Themen**teile** sind auch okay. Es gibt Querschnittsthemen, die gelernte Aspekte nochmal aufgreifen.\n- Es gibt BM-Schritte, die eine vorherige Lösung refactoren. Deshalb sollte jeder BM-Schritt einen eigenen Mini-Monkey bekommen.\n- Forms nur noch Create\n\n## Entwurf für Struktur Praxisteil\n\n## Projekt anlegen\n\n- `ng new book-monkey --style=scss --no-ssr`\n- Styles einbinden\n- `AppComponent` leeren (auch Property `title`)\n- Template für AppComponent: `\u003cmain\u003e\u003c/main\u003e`\n\n\n### [THEORIE] Komponenten\n\n- Template-Syntax: Interpolation, for, if, let\n- Signals Basics: Idee, `signal()`, Getter, `.set()`/`.update()`\n\n### [BM] Buchliste\n\n- Interface `Book` anlegen: `ng g i shared/book`\n  - `createdAt` (Erstellungsdatum des Datensatzes)\n  - optionales Feld `subtitle` (für `@if` im Template)\n- Komponente anlegen: `ng g c books-portal/book-list`\n- einbinden in AppComponent\n- Signal `books` für die Buchliste, statische Bücher\n- `@for` im Template (Variable `b`)\n\n### [THEORIE] Property Bindings, Inputs\n\n### [BM] Property Bindings Item-Komponente\n\n- Komponente anlegen: `ng g c books-portal/book-item`\n- Listen-Template aus BookList in Item-Komponente auslagern\n  - `book = input.required\u003cBook\u003e()`\n  - `@let` verwenden, damit wir nicht überall bei `b` die Klammern hinzufügen müssen: `@let b = book();`\n- Komponente einbinden in BookList, `@for` bleibt im Container, Property Binding für einzelne Bücher\n\n### [THEORIE] Event Bindings, Outputs\n\n### [BM] Event Binding Favoritenliste\n\n- lokale Favoritenliste\n- Item:\n  - Output `like` und Methode `likeBook()`\n  - Button im Template\n- BookList:\n  - sammelt Favoritenliste und zeigt sie an\n  - Signal `likedBooks` wie `books`\n  - Methode `addLikedBook()` aktualisiert Liste (Immutability!)\n  - Event Binding im Template\n- Liste leeren\n  - Methode `clearLikedBooks()`\n  - Button im Template\n\n### [THEORIE] Services, Dependency Injection\n\n### [BM] Service mit statischen Büchern\n\n- `ng g s shared/book-store`\n- statische Buchliste aus BookList verschieben in Service\n- in BookList: `this.books.set(this.#service.getBookList())`\n- nicht `getAll()`, weil wir die Methode später auch für Filter verwenden, dann sind es nicht mehr *alle* Bücher\n\n### [THEORIE] Signals Advanced\n\n- computed\n- linkedSignal\n- effect\n\n### [BM] Lokaler Filter mit computed\n\n- rein lokale Suche in der Buchliste\n- Signal für Suchbegriff: `searchTerm = signal('')`. Property über `books` anlegen, brauchen wir später\n- Eingabefeld mit nativen Bindings `(input)` und `[value]`\n  - ggf. migrieren wir das später auf einen Forms-Ansatz\n- `computed` rechnet aus der Buchliste und dem Suchbegriff eine gefilterte Liste `filteredBooks` aus\n- Template: `books()` ändern zu `filteredBooks()`\n\n### [THEORIE] Routing\n\n- Routing wie immer\n- auch `Router.navigate()` erläutern\n\n### [BM] Routing mit lokaler Buchliste\n\n- HomeComponent anlegen:\n  - `ng g c home`\n  - Template mit Begrüßungstext\n- `ng g c books-portal/book-details`\n- Routen definieren in `books-portal.routes.ts`\n- alles zusammenführen in `app.routes.ts`\n- RouterOutlet platzieren\n- Import für BookListComponent entfernen\n- Weiterleitung von Root zu `home`\n- Links setzen\n  - Item zu Details\n  - Details zu List\n  - Navigationsleiste in AppComponent (Home, Books)\n- Detailseite bauen\n  - Service `getOneBook()`\n  - Parameter mit ActivatedRoute synchron abfragen\n  - `BookStoreService.getOneBook()` sucht synchron in lokaler Liste, dann `this.book.set()`\n\n### [THEORIE] Component Input Binding\n\n- den Teil ggf. auch in Theorie Routing integrieren, später schauen\n\n### [BM] Component Input Binding\n\n- `withComponentInputBinding()` in `app.config.ts` aktivieren\n- Detailseite:\n  - Konstruktor und `#route` komplett weg\n  - Input `isbn`\n  - `computed` wandelt ISBN in Buch um (weil der Service das synchron liefert)\n- Argumentation: Detailkomponente hat keine Abhängigkeit zu ActivatedRoute mehr\n\n### [THEORIE] HTTP\n\n- fetch in der Theorie erläutern\n- HttpClient erläutern\n  - Vorteile nennen: in Angular integriert, mit DI mockbar beim Testing, Interceptors, … (?)\n  - aber auch klar sagen, dass es auch okay ist, wenn man die Angular-Lösung nicht nutzt und lieber fetch nimmt\n- hier noch keine Resource\n\n### [BM] Daten laden mit HTTP\n\n- HttpClient, aber auf RxJS nicht näher eingehen\n- `app.config.ts`: `provideHttpClient(withFetch())`\n- Service:\n  - `inject(HttpClient)`\n  - `getBookList()` umbauen\n- **Buchliste:** `getBookList().subscribe()` und `books.set()`\n- Service `getOneBook()` umbauen\n- **Detailseite:**\n  - `book` wird wieder mit `signal()` initialisiert\n  - Effect reagiert auf geänderte ISBN, `getOneBook(this.isbn()).subscribe()` und `book.set()`\n- **Buch löschen:**\n  - Servicemethode anlegen\n  - Methode und Button auf Detailseite\n  - danach `Router.navigateByUrl()` zur Buchliste\n\n### [THEORIE] Resource\n\n\n### [BM] Resource\n\n- **Buchliste:**\n  - Service `getBookList()` umbauen auf `httpResource`\n  - Komponente: Konstruktor weg\n  - `books` wird Resource\n  - in `filteredBooks` auf `books.value()` umstellen\n  - Reload-Button\n  - Ladeindikator mit `isLoading()`\n- **Detailseite:**\n  - Service `getOneBook()` umbauen: HttpResource mit Request, ganzes Signal übergeben\n  - Komponente: `effect` weg\n  - `book` wird Resource\n  - Template: `book.value()`\n  - Link zu anderer Detailseite (statische ISBN), damit deutlich wird, dass der erneute Request funktioniert. Diskutieren, dass die ISBN natürlich später aus der Datenbank o.Ä. kommen sollte\n\n\n### [THEORIE] Pipes\n\n- Tabelle mit allen Pipes zeigen\n- nur ein paar wichtige Pipes erläutern: DatePipe, DecimalPipe, CurrencyPipe, JsonPipe\n- Exkurs: Sprache einstellen (`LOCALE_ID`)\n\n### [BM] Pipes\n\n- Detailseite: DatePipe nutzen für `createdAt`\n- ggf. eigene ISBN-Pipe\n\n### [THEORIE] Forms\n\nwhatever it will be 🤷\n\n### [BM] Forms Buch anlegen\n\n- Vorbereitung\n  - `ng g c books-admin/book-create`\n  - `ng g c books-admin/book-form`\n    - wir machen zwar kein Bearbeiten mehr, aber das kann eine gute Zusatzaufgabe für die Leser sein. Deswegen Komponententrennung berücksichtigen und auch diskutieren\n  - `books-admin.routes.ts`: Route und Weiterleitung\n  - einbinden in `app.routes.ts`\n  - Eintrag in Navigation\n  - Servicemethode `createBook()` bauen\n  - BookCreate: Template mit Überschrift und Kindkomponente, damit man erstmal was sieht\n- Formular bauen in BookForm\n  - `ReactiveFormsModule` importieren\n  - alles ähnlich wie aktuell im Buch\n  - Template `@let c = bookForm.controls` und `[formControl]=\"c.isbn\"` für Typsicherheit\n  - mit dynamischen Autorenfeldern\n  - `createdAt` beim Submit lokal hinzufügen\n- verdrahten in BookCreate:\n  - erst TypeScript: inject, Methode, navigate\n  - Template mit Event Binding\n\n### [BM] Suche in der Buchliste verbessern\n\n- die beiden Aspekte ggf. in 2 Kapitel trennen, je nach Komplexität\n- 1.) Suche mit HTTP in Buchliste\n  - Service `getBookList()` umbauen: bekommt `searchTerm: Signal\u003cstring\u003e` übergeben, wird als Request in Resource genutzt. Searchterm in HTTP-URL übergeben\n  - BookList\n    - `getBookList(this.searchterm)`\n    - lokales `filteredBooks` kommt weg, im Template direkt auf `books.value()` gehen\n- 2.) Suchbegriff als Query-Parameter\n  - `this.search`: Input mit Component Input Binding für Parameter aus URL\n  - `searchTerm` wird ein LinkedSignal, denn wir wollen es a) direkt setzen (aus dem Formular) und b) auf Basis eines anderen Signals berechnen lassen (`this.search`)\n  - Effect mit `Router.navigate([], { queryParams: { search: this.searchTerm() || null } })` (null für unset)\n  - TODO: Query Parameter nicht `search` nennen, damit er sich vom Param der API unterscheidet. Könnte man sonst verwechseln.\n\n### [THEORIE] Lazy Loading\n\n- Lazy Loading allgemein (\"zur Laufzeit nachladen\")\n- Theorie `loadChildren` und `@defer`\n\n### [BM] Lazy Loading\n\n- Features `books-admin` und `books-portal` lazy laden\n  - `app.routes.ts`: Imports und Spread weg\n  - Basisrouten `books` und `admin` anlegen\n  - in Feature-Routen: Präfix entfernen\n  - bei Weiterleitung in `books-admin`: `pathMatch: 'full'` einfügen, weil Weiterleitung vom leeren Pfad\n- (ggf. Default Export für Routes-Array nutzen)\n- (ggf. HomeComponent mit `loadComponent` laden, später mal überlegen)\n\n\n### [THEORIE] RxJS\n\n- wie immer, ggf. kürzer\n- AsyncPipe\n- RxJS \u003c\u003e Signals (toSignal, toObservable)\n\n### [BM] Suche mit RxJS\n\n- Service: neue Methode `searchBooks(searchTerm: string): Observable\u003cBook[]\u003e`\n- HomeComponent:\n  - übliches RxJS-Beispiel, mit `FormControl.valueChanges`, wenn es das dann noch gibt\n  - erstmal manuell subscriben, damit man was sieht\n  - `toSignal()` mit `{ initialValue: [] }` verwenden\n\n\n### Diskussionspunkte\n\n- 0: Import für RouterOutlet am Anfang entfernen oder stehen lassen?\n- 12: Query-Parameter nicht `search` nennen, weil der in der API auch so heißt und verwechselt werden könnte\n- 6: Detailroute `books/:isbn` oder `books/details/:isbn`?\n- Warnungen wegen Image-Größen\n\n\n### Was noch rein könnte, aber nicht muss\n\n- \\[class\\]/\\[style\\]\n- eigener Validator: müssen aber abwarten, was mit Forms passiert\n- NgOptimizedImage\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffmalcher%2Fbook-monkey6-draft","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffmalcher%2Fbook-monkey6-draft","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffmalcher%2Fbook-monkey6-draft/lists"}