{"id":19893025,"url":"https://github.com/iliaidakiev/query-param-store","last_synced_at":"2025-09-08T05:41:07.581Z","repository":{"id":38663230,"uuid":"168526336","full_name":"IliaIdakiev/query-param-store","owner":"IliaIdakiev","description":"Angular Reactive Query Parameters Store","archived":false,"fork":false,"pushed_at":"2023-02-03T05:00:46.000Z","size":1680,"stargazers_count":15,"open_issues_count":7,"forks_count":2,"subscribers_count":2,"default_branch":"master","last_synced_at":"2024-10-05T04:21:46.900Z","etag":null,"topics":["angular","query-params","query-params-store","state-management","store"],"latest_commit_sha":null,"homepage":"https://stackblitz.com/github/IliaIdakiev/query-param-store","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/IliaIdakiev.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}},"created_at":"2019-01-31T13:05:18.000Z","updated_at":"2022-11-02T12:15:34.000Z","dependencies_parsed_at":"2023-02-18T03:31:30.272Z","dependency_job_id":null,"html_url":"https://github.com/IliaIdakiev/query-param-store","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/IliaIdakiev%2Fquery-param-store","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/IliaIdakiev%2Fquery-param-store/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/IliaIdakiev%2Fquery-param-store/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/IliaIdakiev%2Fquery-param-store/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/IliaIdakiev","download_url":"https://codeload.github.com/IliaIdakiev/query-param-store/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":224327116,"owners_count":17293005,"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","query-params","query-params-store","state-management","store"],"created_at":"2024-11-12T18:26:57.909Z","updated_at":"2024-11-12T18:26:58.698Z","avatar_url":"https://github.com/IliaIdakiev.png","language":"TypeScript","readme":"[![Build Status](https://travis-ci.org/IliaIdakiev/query-param-store.svg?branch=master)](https://travis-ci.org/IliaIdakiev/query-param-store)\n\n# Angular Query Params Store - RxJS Query Params State Management Container for Angular\n\nDeveloping web applications requires persistent state and what better way to store it than using the query parameters for that. With this npm module (`yarn add query-params-store` || `npm install query-params-store`) you can easily configure what query perameters your application route will have, what are the default values (values used when the query parameter doesn't exist in the URL),\nrestrict what query parameters can be used on the current route and more.\n\nReal world example: [Angular Air Demo App](https://github.com/IliaIdakiev/angular-air-qps)\n\n\n# Video Presentations\n\n[![IMAGE ALT TEXT HERE](https://img.youtube.com/vi/gTJVfRq7uog/0.jpg)](https://www.youtube.com/watch?v=gTJVfRq7uog)\n[![IMAGE ALT TEXT HERE](https://img.youtube.com/vi/jCzZbl9b7w4/0.jpg)](https://www.youtube.com/watch?v=jCzZbl9b7w4)\n\n---\n\n## Configuration\napp.module.ts\n```typescript\n@NgModule({\n  declarations: [...],\n  imports: [\n    BrowserModule,\n    AppRoutingModule,\n    QueryParamsStoreModule // \u003c-- Import the Query Params Module to AppModule\n    // Or you can use QueryParamsStoreModule.withConfig({ debug: true }) to have some extra console logging while developing\n    // Check out the section on the bottom in order to find out about QueryParamsStoreModule.withConfig({ useCompression: true })\n  ],\n  bootstrap: [AppComponent]\n})\nexport class AppModule { }\n```\n\napp-routing.module.ts\n```typescript\nimport { IQueryParamsStoreRoute, IQueryParamsStoreRoutes } from 'query-params-store';\n\nconst listRoute: IQueryParamsStoreRoute = {\n  path: 'list',\n  canActivate: [ListActivate],\n  resolve: [ListResolver],\n  component: ListComponent,\n  data: {\n    storeConfig: {\n      stateConfig: {\n        page: 0, // the query parameter - page will be of type number with default value of 0 (type number)\n        pageSize: {\n          value: '30' // the query parameter - pageSize will be of type string with default value of '30' (type string)\n          allowedValues: ['30', '20', '10'] // (optional) the full set of allowed values for the current query param\n          // if the provided value doesn't match any of the listed once it will be removed and the default one\n          // will be provided\n        },\n        filter: {\n          value: null, // the default value of the query parameter filter will be null\n          typeConvertor: String, // if a value inside the URL is provided it will be automatically parsed as String\n          // If parsing is not successful the query parameter will be removed.\n          // (possible values for typeConvertor - String | Number | Boolean)\n          multi: false // it will be a single value (not an array)\n        },\n        sort: {\n          multi: true, // the provided value (either from the URL or the default one) will be threated as a string and it\n          // will be split by the given separator bellow\n          separator: ';' // the seperator that we split by\n          count: 2 //use in order to limit the number of items (this is useful when you have default value set to null)\n          value: 'name:asc;email:asc;age:desc', // the default value of the query parameter sort will be\n          // ''name:asc;email:asc;age:desc' but since we have 'multi: true' it will be split by the given separator and\n          // at the end we will recevice an array - ['name:asc', 'email:asc', 'age:desc'];\n          typeConvertor: String, // the convertor will be used on each value from the split array\n          // (possible values for typeConvertor - String | Number | Boolean)\n        },\n        // we call this option (Binary Boolean - it converts a given number query parameter to a boolean array and it's very\n        // useful when dealing with a lot of toggles/popups or whatever and you want ot use the store to take care of the state)\n        // we also provide a utility function for easier navigation when using the Binary Boolean option - binaryToNumber so \n        // navigation will look something like: \n        // this.router.navigate([], { queryParams: { openToggles: binaryToNumber(updatedOpenTogglesBooleanArray) ... } ... }) \n        openToggles: {\n          typeConvertor: Boolean, // Convert the values to booleans\n          multi: true, // We will be getting a boolean array with true or false values for each open/closed toggle section\n          value: 0, // The initial value will be converted to binary. Since we have 0 it will just be 0 which will be\n          // represented as [false]. If we had 10 we would have 1010 which will be represented as [false, true, false, true]\n          // which is the reversed of [true, false, true, false] - 1010 (binaries are read from right to left)\n          length: 6, // limit the length of the binary array\n          // in the case of 6 if we have ?openToggles=0 we will have [false, false, false, false, false, false]\n          // if we have ?openToggles=10 we will have [false, true, false, true, false, false]\n          removeInvalid: true // remove the invalid numbers that overflow the lenght\n          // if we have length: 6 and query ?openToggles=800 since 800 is 1100100000 and its length is 10 then it will be removed\n        }\n      },\n      removeUnknown: true, // remove all query params that don't match the ones provided in stateConfig config property\n      // (default value - false) (this triggers a router.navigate with all unknown query params set to undefined)\n      noQueryParams: false, // remove all query params for current route (default value - false)\n      inherit: true, // inherit all query parameters from parent routes (default value - true)\n      caseSensitive: true // match query parameters with case sensitive logic (default value - true)\n    },\n  }\n};\n\nconst routes: IQueryParamsStoreRoutes = [\n  {\n    path: '',\n    pathMatch: 'full',\n    redirectTo: 'list',\n  },\n  listRoute\n];\n\nexport const AppRoutingModule = RouterModule.forRoot(routes);\n```\n\n## Usage\n\nThe QueryParamsStore can be used anywhere inside you application. One example is using it inside your resolver:\n\nlist.resolver.ts\n```typescript\nimport { Injectable } from '@angular/core';\nimport { Resolve } from '@angular/router';\nimport { Observable } from 'rxjs';\nimport { switchMap, first } from 'rxjs/operators';\nimport { QueryParamsStore } from 'query-params-store';\nimport { UserService } from './user.service';\n\n@Injectable({ providedIn: 'root' })\nexport class ListResolver implements Resolve\u003cObservable\u003cany[]\u003e\u003e {\n\n  constructor(\n    private queryParamsStore: QueryParamsStore,\n    private userService: UserService\n  ) { }\n\n  resolve() {\n    return this.queryParamsStore.store.pipe(\n      switchMap((queryParams) =\u003e {\n        const { page, pageSize, filter, sort } = queryParams;\n        return this.userService.load(page, pageSize, filter, sort);\n      }),\n      first(),\n      catchError(error =\u003e {\n        // handle error...\n      })\n    );\n  }\n}\n```\n\nprotect your routes from unwanted query parameters inside canActivate\n\n```typescript\n@Injectable({ providedIn: 'root' })\nexport class ListActivate implements CanActivate {\n\n  constructor(\n    private queryParamsStore: QueryParamsStore\u003cany\u003e\n  ) { }\n\n  canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable\u003cboolean\u003e {\n    // if we have this route query params store configuration for the route\n    // data: {\n    //   storeConfig: {\n    //     stateConfig: {\n    //       role: {\n    //         value: null,\n    //         multi: false,\n    //         typeConvertor: String\n    //       }\n    //     }\n    //   }\n    // },\n    // this guard will allow the route to be accessed only if the role query param is 'ADMIN' or the defaultValue from\n    // the configuration object (null). If we don't have a defaultValue we should add undefined instead of null\n    return this.queryParamsStore.canActivate({ role: { match: [null, 'ADMIN'] } }, route);\n  }\n}\n\n```\n\nprotect exiting routes with unwanted query parameters inside canDeactivate\n\n```typescript\n@Injectable({ providedIn: 'root' })\nexport class DialogDeactivate implements CanDeactivate\u003cObservable\u003cboolean\u003e\u003e {\n\n  constructor(\n    private queryParamsStore: QueryParamsStore\n  ) { }\n\n\n  canDeactivate(component: any, currentRoute: ActivatedRouteSnapshot, currentState: RouterStateSnapshot, nextState?: RouterStateSnapshot) {\n    // if we have this route query params store configuration for the route\n    // data: {\n    //     storeConfig: {\n    //       stateConfig: {\n    //         completed: {\n    //           value: null,\n    //           multi: false,\n    //           typeConvertor: Boolean\n    //         }\n    //       }\n    //     }\n    //   },\n    // this guard will allow the route to be exited only if the completed query param is true or the defaultValue from\n    // the configuration object (null).\n    return this.queryParamsStore.canDeactivate({ completed: { match: [null, true] } }, currentRoute, currentState)\n  }\n}\n```\n\n* There is an additional **queryParamsStore.match(allowedValues: IAllowedValuesConfig | Observable\u003cIAllowedValuesConfig\u003e)** method that can be used.\n\nUsing query params store inside our components:\n\nlist.component.ts\n```typescript\nimport { Component } from '@angular/core';\nimport { ActivatedRoute } from '@angular/router';\nimport { QueryParamsStore } from 'query-params-store';\nimport { Observable } from 'rxjs';\n\n@Component({\n  selector: 'list-component',\n  templateUrl: './list.component.html',\n  styleUrls: ['./list.component.css']\n})\nexport class ListComponent {\n  filter$: Observable\u003cstring\u003e;\n  userList: any[];\n\n  constructor(private queryParamsStore: QueryParamsStore, activatedRoute: ActivatedRoute) {\n    this.filter$ = queryParamsStore.select\u003cstring\u003e('filter');\n    // this.filter$ = queryParamsStore.select\u003cstring\u003e(queryParams =\u003e queryParams.filter);\n    this.userList = this.activatedRoute.snapshot.data[0];\n  }\n}\n\n```\n\nlist.component.html\n```html\n\u003cdiv class=\"filter-input\"\u003e\n  \u003cinput type=\"text\" [value]=\"filter$ | async\" placeholder=\"Search...\" #searchInput\u003e\n  \u003cbutton (click)=\"search(searchInput.value)\"\u003eSearch\u003c/button\u003e\n\u003c/div\u003e\n\u003cul\u003e\n  \u003cli *ngFor=\"let user of userList\"\u003e{{user.email}}\u003c/li\u003e\n\u003c/ul\u003e\n```\n\n## Now (from v.3.5) you also have the option to use lz-string (LZ-based compression algorithm) to minimize the size of your query parameters.\n\napp.module.ts\n```typescript\n@NgModule({\n  declarations: [...],\n  imports: [\n    BrowserModule,\n    AppRoutingModule,\n    QueryParamsStoreModule.withConfig({ \n      useCompression: true,\n      compressionKey: 'i' // you can use this in order to provide a custom query parameter key that will be for the compressed query params\n      // you can also use '#' in order to use the fragment (anchor) part of the url.\n      // (in this case the url will look like: mydomain.com/path/to/page#COMPRESSED_QPS_STATE)\n    })\n  ],\n  bootstrap: [AppComponent]\n})\nexport class AppModule { }\n```\n\n\nThis repository contains an example app showing how you can use the *query params store*. You can also view it on [**Stackblitz**](https://stackblitz.com/github/IliaIdakiev/query-param-store)!\n\n[Check our website](https://hillgrand.com/);","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Filiaidakiev%2Fquery-param-store","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Filiaidakiev%2Fquery-param-store","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Filiaidakiev%2Fquery-param-store/lists"}