{"id":26334495,"url":"https://github.com/leolanese/angular19-api-observable-signal","last_synced_at":"2025-06-12T14:09:01.978Z","repository":{"id":280279556,"uuid":"921368789","full_name":"leolanese/Angular19-API-Observable-Signal","owner":"leolanese","description":"Angular (19+) Request API based on Observable, Signal (state management, effect, httpResource, input-pattern, model)","archived":false,"fork":false,"pushed_at":"2025-04-15T16:05:32.000Z","size":1138,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-06-11T23:24:12.196Z","etag":null,"topics":["angular","httpresource","input","input-signal","observable","resful-api","rxjs","signals"],"latest_commit_sha":null,"homepage":"","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/leolanese.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":"2025-01-23T20:28:09.000Z","updated_at":"2025-04-15T16:05:36.000Z","dependencies_parsed_at":"2025-03-02T13:36:11.464Z","dependency_job_id":"c0d7851e-9178-4360-bc4a-cecc7f47121e","html_url":"https://github.com/leolanese/Angular19-API-Observable-Signal","commit_stats":null,"previous_names":["leolanese/angular19-api-observable-signal"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/leolanese/Angular19-API-Observable-Signal","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/leolanese%2FAngular19-API-Observable-Signal","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/leolanese%2FAngular19-API-Observable-Signal/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/leolanese%2FAngular19-API-Observable-Signal/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/leolanese%2FAngular19-API-Observable-Signal/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/leolanese","download_url":"https://codeload.github.com/leolanese/Angular19-API-Observable-Signal/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/leolanese%2FAngular19-API-Observable-Signal/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":259479617,"owners_count":22864363,"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":["angular","httpresource","input","input-signal","observable","resful-api","rxjs","signals"],"created_at":"2025-03-16T00:19:14.129Z","updated_at":"2025-06-12T14:09:01.968Z","avatar_url":"https://github.com/leolanese.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Angular (19.2+) Observable + Signals for handling API requests \n\n🔴\n🟡\n🟢\n🏁\n\n## ⏺ Goals AC's\n\n### ⏺ Test legacy Angular Vs new modern Angular practices\n\nTest API based on Observable, Signal (state management, effect, httpResource, input-pattern, model)\n\n### ⏺ Test simple/direct Api Request using a few API services:\n\nhttps://jsonplaceholder.typicode.com/posts \u003cbr /\u003e\nhttps://swapi.dev/api/vehicles\n\n### ⏺ Test complex/nested API Requests\n\n1. Get all country names and display on the page:\n   https://restcountries.com/v3.1/independent?fields=name\n\n2. Select a country and Show the flag:\n   https://restcountries.com/v3.1/name/Grenada?fields=name,flags\n\n3. Search countries by language:\n   https://restcountries.com/v3.1/lang/spanish?fields=name\n\n---\n\n## 🔸 Demo\n\n![modern-angular](./src/assets/signals-modern-angular.png)\n\n---\n\n## 🔸 Example Solution Arquitecture\n\n```js\nsrc/\n└── app/\n     ├── SoC/\n          └── input/output   // Separation of Concern using Parent and Child, @Input()/@Output()\n          └── input/output   // Separation of Concern using Parent and Child, input signal/@Output()\n     ├── orphan-observable/  // single Component, managing API request using Observables\n     ├── orphan-signal/      // single Component, managing API request using Signals\n     ├── orphan-signal-simple/  // single Component, managing API request using Signal\n     ├── orphan-signal-nested/  // single Component, managing complex API request using Signal\n     ├── orphan-signal-httpresource/ // simple Component, managing API request using Signals with httpResouce asynchronous data fetching\n     ├── orphan-signal--httpresource-reactiveForm/ // Shows how the new signals approach replaces the traditional RxJS pattern\n     ├── orphan-signal-httpresource-signal/ // 100% fully signal-based. Using direct signal binding with [value] and (input). Simple event handler to update the signal\n     ├── orphan-signal-input-pattern/ // full signal-based approach: 1-way binding\n     ├── orphan-signal-model/ // full signal-based approach: 2-way binding\n     |\n     ├── app.component.ts\n     ├── auth.interceptor.ts\n     └── http.interceptor.ts\n```\n\n## 🔸 Technical mentions\n\n🟡 Green solutions are 100% fully reactive signal-based which are Angular recommendations:\n\n🔵 Reactive state management\n\n- `All state is managed through signals in the service`\n\n🔵 Data Management:\n\n- `No local component state variables` that aren't signals\n- `No RxJS Observables or Subjects`\n\n🔵 HTTP Handling:\n\n- `Signal with httpresource`, for automatic data fetching\n\n🔵 Template Binding:\n\n- Replaces `NgModel` is part of the older Forms API, while signals represent Angular's future\n- `All template expressions use signals` (vehicleService.searchTerm(), vehicleService.isLoading(), etc.)\n- Uses `modern Angular control flow` (@if, @else, @for)\n\n🔵 Data Flow:\n\n### `Unidirectional Data Flow`:\n\n```js\n    Signal → View ([property] binding) = [value]=\"searchSignal()\n    View   → Signal (event() handler) = (input)=\"signal.set()\"\n```\n\n- Use `signal-input-pattern`: `[value] + (input) pattern`:\n  `It's simply a combination of 1-way binding (Property [value]=\"searchSignal()\" + event binding (input)=\"signal.set()\")`\n\n- `Direct Signal Control` (when is read = binding, when is updated = event handler)\n\n### `Bidirectional Data Flow` (model() implement 2-way binding simplify two-way binding boilerplate\n\n🔵 Event Handling:\n\n- Input events directly update signals (this.vehicleService.searchTerm.set(value))\n- No intermediate transformations using RxJS operators\n\n🔵 Service Implementation:\n\n- Uses httpResource for HTTP requests (instead HttpClient)\n  This provides:\n- Automatically fetches data when the component initializes.\n- Handles loading, success, and error states without extra code.\n- Provides a .value() method to access the latest data.\n- Supports reloading with .reload().\n- Stays within the signals paradigm and use signals' effect() to automatically handle cleanup (instead OnInit/OnDestroy + No need for manual subscription management)\n\n🟡 Other technical mentions\n\n🔵 `SoC`\nThis example demonstrates the separation of concerns between the:\n`service (responsible for fetching data)`,\n`smart component (responsible for handling business logic and passing data to the dummy component)`, `dummy component (responsible for rendering the UI)`\n\n🔵 `Modern StandAlone Components`:\nI directly bootstrap the component itself, not its module. This is because standalone components have their own injectors and don't rely on a root module for dependency injection. Promotes code maintainability, reusability, and smaller application size.\n\n🔵 Implemented `TSP mechanism`:\nI'm using `Tree Shakeable Providers` in `Services` by using the `providedIn` attribute, this will provide the benefits of both `tree shaking performance` and `dependency injection`,\nmeaning that our services will not be included in the final bundle unless they are being used by other services or components. As a result we reduce the bundle size by removing unused code from the bundle.\n\n🔵 `RxJS`\n\n- `takeUntilDestroyed(this.destroyRef)` to automatically unsubscribe when the component is destroyed, simplifying the cleanup process even further\n- `shareReplay(1)` because multiple components might subscribe to the same observable\n\n🔵 `Dependency Injection Pattern`:\nI'm using Modern `Dependency Injection functions`, instead traditional `constructor-based dependency injection`as result I will have a more Modular, Less Complex\n\n🔵 Implement Caching:\n-- `Cache API Service Calls`\nCaches identical HTTP requests within a single component:\nI'm using `shareReplay()` to improve efficiency, ensuring that all subscribers receive the most recent data without triggering multiple HTTP requests.\n\n🔵 `DestroyRef \u0026 takeUntilDestroyed()`: Angular 16+\nI'm using provides a more declarative and efficient way to handle automatic cleanup tasks when a component or service is destroyed: `takeUntilDestroyed(this.destroyRef)` to automatically unsubscribe when the component is destroyed, simplifying the cleanup process even further\n\n🔵 `Function-based Interceptor` (optional):\nIt also showcases the usage of an interceptor to log HTTP requests and responses. While not necessary for this example, it can be useful for debugging and monitoring purposes (WIP)\n\n---\n\n### :100: \u003ci\u003eThanks!\u003c/i\u003e\n\n#### Now, don't be an stranger. Let's stay in touch ‼\n\n\u003ca href=\"https://github.com/leolanese\" target=\"_blank\" rel=\"noopener noreferrer\"\u003e\n  \u003cimg src=\"https://scastiel.dev/api/image/leolanese?dark\u0026removeLink\" alt=\"leolanese’s GitHub image\" width=\"600\" height=\"314\" /\u003e\n\u003c/a\u003e\n\n##### :radio_button: gitroll: \u003ca href=\"https://gitroll.io/profile/uCOZ9SM8b7ne9h17NuPuKVky9uFh2\" target=\"_blank\"\u003eLeoLanese\u003c/a\u003e\n\n##### :radio_button: Linkedin: \u003ca href=\"https://www.linkedin.com/in/leolanese/\" target=\"_blank\"\u003eLeoLanese\u003c/a\u003e\n\n##### :radio_button: Twitter: \u003ca href=\"https://twitter.com/LeoLanese\" target=\"_blank\"\u003e@LeoLanese\u003c/a\u003e\n\n##### :radio_button: Blog: \u003ca href=\"https://www.dev.to/leolanese\" target=\"_blank\"\u003edev.to/leolanese\u003c/a\u003e\n\n##### :radio_button: Questions / Suggestions / Recommendations: \u003ca href=\"mailto:developer@leolanese.com\"\u003edeveloper@leolanese.com\u003c/a\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fleolanese%2Fangular19-api-observable-signal","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fleolanese%2Fangular19-api-observable-signal","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fleolanese%2Fangular19-api-observable-signal/lists"}