{"id":28098764,"url":"https://github.com/leolanese/angular-forms-performance-fetch-search-filtering","last_synced_at":"2026-05-15T22:03:23.558Z","repository":{"id":265833879,"uuid":"781085172","full_name":"leolanese/Angular-Forms-Performance-Fetch-Search-Filtering","owner":"leolanese","description":"Angular forms performance, search and filtering project ","archived":false,"fork":false,"pushed_at":"2025-05-04T08:31:12.000Z","size":1098,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-05-04T09:27:09.666Z","etag":null,"topics":["angular","angular-components","reactive-forms","rxjs","signal","stand-alone","template-driven-form","typescript"],"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,"zenodo":null}},"created_at":"2024-04-02T18:07:52.000Z","updated_at":"2025-05-04T08:31:15.000Z","dependencies_parsed_at":"2025-01-24T14:25:00.608Z","dependency_job_id":"8483bd67-8dff-4081-a3b9-fb521ecb38a3","html_url":"https://github.com/leolanese/Angular-Forms-Performance-Fetch-Search-Filtering","commit_stats":null,"previous_names":["leolanese/angular-fetch-search-filtering","leolanese/angular-forms-playground-fetch-search-filtering","leolanese/angular-forms-performance-fetch-search-filtering"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/leolanese%2FAngular-Forms-Performance-Fetch-Search-Filtering","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/leolanese%2FAngular-Forms-Performance-Fetch-Search-Filtering/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/leolanese%2FAngular-Forms-Performance-Fetch-Search-Filtering/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/leolanese%2FAngular-Forms-Performance-Fetch-Search-Filtering/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/leolanese","download_url":"https://codeload.github.com/leolanese/Angular-Forms-Performance-Fetch-Search-Filtering/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253999883,"owners_count":21997350,"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","angular-components","reactive-forms","rxjs","signal","stand-alone","template-driven-form","typescript"],"created_at":"2025-05-13T17:58:47.517Z","updated_at":"2025-10-10T00:37:41.517Z","avatar_url":"https://github.com/leolanese.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Angular (20+) Signal Power: Angular Forms, Search \u0026 Filter Performance Lab\n\n- This project serves as a comprehensive demonstration of Angular's flexibility in implementing the same core functionality through different patterns and approaches, each with its own trade-offs in terms of complexity, maintainability, and performance.\n\n\n---\n\n## Demo\n\n- Score\n![demo](./src/assets/forms-playground4.png)\n\n- Winner example 14 - Pure Signal-Based Architecture \n![demo](./src/assets/forms-playground3.png)\n\n---\n\n## Evaluation Criteria\n\n- Modern Angular best practices (Signals, Standalone Components, RxJS, etc.)\n- Performance (reactivity, change detection, memory)\n- Maintainability (readability, modularity, testability)\n- Scalability (how well it would work in a larger app)\n- Follows new modern Angular trends\n\n---\n\n## Resoltion Solution evaluations\n\n### Solutions 1–5: Classic RxJS \u0026 Forms Approaches\n| Solution | Approach | Rating | Notes |\n|----------|----------|--------|-------|\n| 1 | Template-driven forms, RxJS, filter pipe, manual subscription mgmt | 1 | Outdated, verbose, not idiomatic for modern Angular. |\n| 2 | Template ref variable, RxJS, filter pipe | 1 | Slightly better, but still not scalable or modern. |\n| 3 | Reactive Forms, RxJS, filter pipe | 2 | More modern, but relies on pipes and manual RxJS. |\n| 4 | Material input, Reactive Forms, RxJS, filter pipe | 2 | Good for Material projects, but not leveraging Signals. |\n| 5 | Reactive Forms, RxJS, filter pipe | 2 | Clean, but not as modern as Signals-based solutions. |\n\n\n### Solutions 6–9: Hybrid RxJS + Signals\n| Solution | Approach | Rating | Notes |\n|----------|----------|--------|-------|\n| 6 | Reactive Forms, RxJS, toSignal, filter pipe | 2 | Starts using Signals, but still hybrid and not full Signals. |\n| 7 | Like 6, but optimizes for stable values | 2 | Slightly better, but still hybrid. |\n| 8 | Template-driven, Signals, RxJS, filter pipe | 2 | Uses Signals, but not fully idiomatic. |\n| 9 | Template-driven, Signals, RxJS, filter pipe | 2 | Similar to 8, not fully leveraging Signals' power. |\n\n\n### Solutions 10–11: Component-Driven, RxJS, and Modular\n| Solution | Approach | Rating | Notes |\n|----------|----------|--------|-------|\n| 10 | Reactive Forms, RxJS, combineLatest, modular | 2 | Good modularity, but not using Signals. |\n| 11 | Component-driven (filter, sort, pagination), RxJS, modular | 2 | Very modular, but not using Signals. Good for classic Angular. |\n\n\n### Solutions 12–13: Hybrid Signals, Modern trendy Angular\n| Solution | Approach | Rating | Notes |\n|----------|----------|--------|-------|\n| 12 | Component-driven, full Signals (computed, effect, toSignal), modular | 3 | Modern, scalable, maintainable, highly recommended. |\n| 13 | Hybrid Signals (signal, computed, effect), modular, idiomatic | 3 | Mostly Remmended: idiomatic, performant, future-proof, best for new Angular projects. |\n\n\n### Solutions 14: Pure Signals, Modern trendy Angular\n| Solution | Approach | Rating | Notes |\n|----------|----------|--------|-------|\n| 14 | Pure Signal-Based Architecture  | 3 | Remmended: idiomatic, performant, future-proof, trendy, follow Angular best practices and best for new and future Angular projects. |\n\n\n---\n\n## Overall Project Goals\n\n- Demonstrate different approaches to implement the same search functionality in Angular\n- Compare various Angular patterns and best practices\n- Show evolution from simple to complex implementations\n- Warning Spoil: Solution13 is the recommendation!\n\n## Form Handling Approaches\n- Template-driven forms (Solutions 1, 8, 9)\n- Reactive forms (Solutions 3, 4, 10)\n- Manual event handling (Solution 2)\n- Component-driven forms (Solution 11)\n\n## Simple State:\n- Two-way binding (Solution 1)\n- Template reference variables (Solution 2)\n- FormControl direct binding (Solution 3)\n\n## Advanced State:\n- Signals (Solutions 6, 7, 8, 9, 13)\n- RxJS Streams (Solutions 10, 11)\n\n## Pure Signal Solution: Component + Service (httpResource instead httpClient):\n- Fully signal-based (Solution 13)\n- This is using Angular 19.2+ new \"httpResource()\" API which is designed to work with signals and provides automatic state management for HTTP requests.\n- The `httpResource` provides built-in signals:\n+ value for the data\n+ isLoading for loading state\n+ error for error state\n- Main features:\n+ Automatic Reactivity\n+ Default state\n+ Data validation and transformation\n\n## Extra Aditional API notes:\n- Filtering: The end-point is already filtering depending on the user-input, but including the filter pipe in our template (countries$ | async | filter:searchFilter), Angular will apply the FilterPipe's transform method to the countries$ observable's emitted values. This means, that each time the countries$ observable emits a new array of countries, Angular will filter those (can be multiple) countries based on the searchFilter string using the logic defined in the FilterPipe. To test: input `UK` it should return only `Ukraine` then check for the console. You must see: `Filter pipe triggered:  true` once \n\n---\n\n## Conclusion\n\n- `Solutions 1–5` are not recommended for new projects.\n- `Solutions 6–11` are transitional or modular, but not as modern as full Signals-based approaches.\n- `Solution 13` is the most recommended, followed closely by `Solution 12`. Both use Angular Signals throughout, are modular, and align with the latest Angular best practices and trends for 2024 and beyond.\n- `Solution 14` is the the recommended, followed closely by `Solution 14`. This align with 2025 best practices and recommendations.\n\n---\n\n## Individual solutions explanation\n\n1) ✅ Uses:\n- Template-driven forms with [(ngModel)] 2-way binding with ngModelChange\n- Custom pipe filtering with filter pipe\n- RxJS with takeUntilDestroyed pattern\n- Reactive search with debounceTime and distinctUntilChanged\n\n2) ✅ Uses:\n- Template reference variables (#searchBox)\n- Event binding with (input)\n- Custom pipe filtering with filter pipe\n- RxJS with takeUntilDestroyed pattern\n- Reactive search with debounceTime and distinctUntilChanged\n- Manual state management for search text\n\n3) ✅ Uses:\n- Reactive forms with FormControl and FormGroup\n- Direct FormControl binding with [formControl]\n- Custom pipe filtering\n- RxJS with startWith and takeUntilDestroyed\n- valueChanges observableformControl (directly binding the FormControl instance)\n\n4) ✅ Uses:\n- Material UI components\n- Reactive forms with formControlName\n- Custom pipe filtering with slice\n- RxJS subscription management\n- Typed form controls with interfaces formControlName (directly bind to specific - - input element within the template) \n- .get()\n\n5) ✅ Uses:\n- pipe Ng2SearchPipeModule for filtering\n- Reactive forms with formControlName\n- RxJS with takeUntilDestroyed\n- Null safety with optional chaining\n- RxJS error handling with map\n\n6) ✅ Uses:\n- Angular Signals with toSignal\n- Reactive forms with FormGroup\n- Effects for signal monitoring\n- Custom pipe filtering\n- RxJS with startWith and takeUntilDestroyed\n\n7) ✅ Uses:\n- Signals with toSignal (based on stable values \u0026 optimise for efficient rendering) \n- Reactive forms with FormGroup and formControlName\n- Custom pipe filtering\n- RxJS with debounceTime and takeUntilDestroyed\n- Optimized for efficient rendering\n\n8) ✅ Uses:\n- Template-driven forms with [(ngModel)]\n- Signals with signal, computed, and effect\n- ViewChild for form access\n- Custom pipe filtering\n- Manual `onSearch` trigger with button\n\n9) ✅ Uses:\n- Template-driven forms with [(ngModel)]\n- Signals with signal\n- Simplified state management\n- Custom pipe filtering\n- Manual search trigger with button\n- DestroyRef for cleanup\n\n10) ✅ Uses:\n- Reactive forms with validation\n- FormBuilder service\n- RxJS combineLatest for data streams\n- Mock data with static countries\n- Form validation error messages\n- RxJS operators (startWith, distinctUntilChanged)\n(based on https://github.com/leolanese/Angular-rxjs-filtering-list)\n\n11) ✅ Uses:\n- Component-driven architecture\n- Separate components for Filter, Sort, List, and Pagination\n- Advanced RxJS stream management\n- FormBuilder with reactive forms\n- Comprehensive data handling (filter, sort, paginate)\n- SearchService integration\n- Smart and presentational component pattern\n\n12) ✅ Uses:\n- Moving to pure signal\n- Signal-based form value tracking\n- Signal-based state management\n- signal() for writable state, computed() Computed values for filtering, sorting, and pagination, effect() for side effects, toSignal() for converting RxJS\n- Automatic dependency tracking between signals\n- Performance optimization through Signal-based reactivity\n- Reactive forms with FormGroup and FormControl (avoiding continuous re-evaluations caused by traditional getters)\n- Component composition (reusing Solution11's child components)\n\n\u003e This represents a modern Angular implementation using Signals instead of RxJS Observables for state management, while maintaining the component-driven architecture from Solution 11.\n\n13) ✅ Uses:\n### Signal State Management\n✔ Uses a single state signal to manage all application state\n\n### Signal Data Flow\n✔ Manages all state through signals\n✔ Uses computed signals for derived data\n✔ Handles data transformation (filtering, sorting, pagination)\n✔ Communicates with the service layer\n\n## Signal Child Components communication\n✔ SignalFilterComponent: Two-way binding with model() for filter text\n✔ SignalSortComponent: Two-way binding with model() for sort direction\n✔ SignalListComponent: Signal-based input for countries list\n✔ SignalPaginationComponent: Two-way binding with model() for current page\n\n## Signal Service Layer communication\n✔ SignalCountryService uses signals for: Data fetching, Loading states, Error handling\n\n\u003e Solution13 represents a modern, fully signal-based Angular application with: Clean architecture, Efficient state management, Type-safe components, Reactive data flow, Optimised performance, Clear separation of concerns, which is more efficient than traditional change detection and provides better developer experience.\n\n\n## Further explanation `Solution13 as fully signal-based`\n\n### Singal state management\n```js\n// Base signals for state\nfilterText = signal('');\nsortDirection = signal\u003c'asc' | 'desc'\u003e('asc');\ncurrentPage = signal(0);\n```\n\n### Computed Signals for derived state:\n```js\nfilteredCountries = computed(() =\u003e {\n  const countries = this.countryService.getCountries().data();\n  const filter = this.filterText().toLowerCase();\n  // ... filtering logic\n});\n\nsortedCountries = computed(() =\u003e {\n  const countries = this.filteredCountries();\n  // ... sorting logic\n});\n\ntotalCount = computed(() =\u003e this.sortedCountries().length);\ntotalPages = computed(() =\u003e Math.ceil(this.totalCount() / this.itemsPerPage));\n```\n\n### Effects (for side effects)\n\n```js\neffect(() =\u003e {\n  // Reset page when filter or sort changes\n  this.currentPage.set(0);\n}, { allowSignalWrites: true });\n```\n\n### Signal-based Service\n\n```js\nexport class SignalCountryService {\n  private countries = signal\u003cCountry[]\u003e([]);\n  private isLoading = signal(false);\n  private error = signal\u003cstring | null\u003e(null);\n  // ...\n}\n```\n\n### Signal-based Components\n\n```js\n// In SignalFilterComponent\nfilterValue = model.required\u003cstring\u003e();\n\n// In SignalListComponent\ncountries = input.required\u003cCountry[]\u003e();\n```\n\n---\n\nThis project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 17+\n\n```js\nng new Angular-Search-Filtering\n\nnpm i\nnpm i bootstrap\n```\n\n```js\n// package.json\n\"styles\": [\n      \"node_modules/bootstrap/dist/css/bootstrap.min.css\",\n      \"src/styles.scss\"\n]\n```\n\n```js\nng g s services/country\n// later on exploring the Entity Pattern\n\nng g p pipes/filter\n// reusable pipe to filter array based on search term\n```\n\n## API Service\n\nWe are using \"https://restcountries.com\" sometimes these services are not as fast as expected, I'm looking forward to replace (maybe with: https://countries.petethompson.net) it but for now it's good enough.\n\n---\n\n## Development server\n\nRun `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The application will automatically reload if you change any of the source files.\n\n## Code scaffolding\n\nRun `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`.\n\n## Build\n\nRun `ng build` to build the project. The build artifacts will be stored in the `dist/` directory.\n\n## Running unit tests\n\nRun `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).\n\n## Running end-to-end tests\n\nRun `ng e2e` to execute the end-to-end tests via a platform of your choice. To use this command, you need to first add a package that implements end-to-end testing capabilities.\n\n## Further help\n\nTo get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.io/cli) page.\n\n---\n\n### :100: \u003ci\u003eThanks!\u003c/i\u003e\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##### :radio_button: Linkedin: \u003ca href=\"https://www.linkedin.com/in/leolanese/\" target=\"_blank\"\u003eLeoLanese\u003c/a\u003e\n##### :radio_button: Twitter: \u003ca href=\"https://twitter.com/LeoLanese\" target=\"_blank\"\u003e@LeoLanese\u003c/a\u003e\n##### :radio_button: Blog: \u003ca href=\"https://www.dev.to/leolanese\" target=\"_blank\"\u003eBlog/a\u003e\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%2Fangular-forms-performance-fetch-search-filtering","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fleolanese%2Fangular-forms-performance-fetch-search-filtering","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fleolanese%2Fangular-forms-performance-fetch-search-filtering/lists"}