Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: query-params plugin + test coverage #58

Closed
wants to merge 15 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
COUCHDB_USER=admin
COUCHDB_PASSWORD=adminadmin
POSTGRES_NAME=postgres
POSTGRES_USER=postgres
POSTGRES_PASSWORD=postgres
Expand Down
4 changes: 1 addition & 3 deletions .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,7 @@
"es2020": true,
"node": true
},
"extends": [
"plugin:prettier/recommended"
],
"extends": ["plugin:prettier/recommended"],
"parser": "@typescript-eslint/parser",
"parserOptions": {
"project": "tsconfig.json",
Expand Down
40 changes: 25 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,17 +55,19 @@ import { getRxDatabaseCreator } from '@ngx-odm/rxdb/config';
@NgModule({
imports: [
// ... other imports
NgxRxdbModule.forRoot(getRxDatabaseCreator({
name: 'demo', // <- name (required, 'ngx')
storage: getRxStorageDexie(), // <- storage (not required, 'dexie')
localDocuments: true,
multiInstance: true, // <- multiInstance (optional, default: true)
ignoreDuplicate: false,
options: {
storageType: 'dexie|memory', // <- storageType (optional, use if you want defaults provided automatically)
dumpPath: 'assets/dump.json', // path to datbase dump file (optional)
},
})),
NgxRxdbModule.forRoot(
getRxDatabaseCreator({
name: 'demo', // <- name (required, 'ngx')
storage: getRxStorageDexie(), // <- storage (not required, 'dexie')
localDocuments: true,
multiInstance: true, // <- multiInstance (optional, default: true)
ignoreDuplicate: false,
options: {
storageType: 'dexie|memory', // <- storageType (optional, use if you want defaults provided automatically)
dumpPath: 'assets/dump.json', // path to datbase dump file (optional)
},
})
),
],
providers: [],
bootstrap: [AppComponent],
Expand Down Expand Up @@ -116,7 +118,9 @@ export class TodosModule {
```typescript
@Injectable()
export class TodosService {
private collectionService: NgxRxdbCollection<Todo> = inject<NgxRxdbCollection<Todo>>(NgxRxdbCollectionService);
private collectionService: NgxRxdbCollection<Todo> = inject<NgxRxdbCollection<Todo>>(
NgxRxdbCollectionService
);
// store & get filter as property of a `local` document
filter$ = this.collectionService
.getLocal('local', 'filterValue')
Expand Down Expand Up @@ -172,7 +176,7 @@ export const appConfig: ApplicationConfig = {
localDocuments: true,
multiInstance: true,
ignoreDuplicate: false,
storage: getRxStorageDexie()
storage: getRxStorageDexie(),
})
),
],
Expand All @@ -189,7 +193,7 @@ import { provideRxCollection } from '@ngx-odm/rxdb';
@Component({
standalone: true,
// ...
providers: [provideRxCollection(config)]
providers: [provideRxCollection(config)],
})
export class StandaloneComponent {
readonly todoCollection = inject(NgxRxdbCollectionService<Todo>);
Expand Down Expand Up @@ -243,7 +247,14 @@ By using this module you can simplify your work with RxDB in Angular application
- optionally provide syncronization with remote db (CouchDB, Kinto etc.) as DB options
- Automatically initialize RxCollection for each _lazy-loaded Feature module / standalone component_ with config
- Work with documents via _NgxRxdbCollectionService_ with unified methods instead of using _RxCollection_ directly (though you still have access to _RxCollection_ and _RxDatabase_ instance)
- simple methods to work database & documents (with queries)
- simple methods to work with local documents
- simple methods to work with attachments
- simple replication sync initialization
- Work with signals and entities with `@ngrx/signals` and `@ngrx/entity` (optionally _zoneless_) (see [example](examples/standalone/src/app/todos/todos.store.ts))
- Persist collection query ([mango-query-syntax](https://github.com/cloudant/mango)) in URL with new plugin `query-params-plugin` (in demo, set localStorage `_ngx_rxdb_queryparams` )
- provide Observable of current URL (automatically for Angular)
- simple methods to set or patch filter, sort, limit, skip

<!-- ## Diagrams

Expand All @@ -268,7 +279,6 @@ Project inspired by

## Notes


## Contact

Created by [@voznik](https://github.com/voznik) - feel free to contact me!
11 changes: 11 additions & 0 deletions docker-compose.couch.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
version: '3'
services:
db:
image: couchdb:3
environment:
COUCHDB_USER: ${COUCHDB_USER}
COUCHDB_PASSWORD: ${COUCHDB_PASSWORD}
ports:
- '5984:5984'
volumes:
- ./tmp/couchdb:/opt/couchdb/data
4 changes: 2 additions & 2 deletions examples/demo/project.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@
"production": {
"fileReplacements": [
{
"replace": "examples/demo/src/environments/environment.ts",
"with": "examples/demo/src/environments/environment.prod.ts"
"replace": "examples/shared/environment.ts",
"with": "examples/shared/environment.prod.ts"
}
],
"budgets": [
Expand Down
9 changes: 8 additions & 1 deletion examples/demo/src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { RouterModule, Routes } from '@angular/router';
import { NgxRxdbModule } from '@ngx-odm/rxdb';
import { getRxDatabaseCreator } from '@ngx-odm/rxdb/config';
import { RxDBAttachmentsPlugin } from 'rxdb/plugins/attachments';
import { RxDBLeaderElectionPlugin } from 'rxdb/plugins/leader-election';
import { AppComponent } from './app.component';

const routes: Routes = [
Expand All @@ -27,10 +29,15 @@ const routes: Routes = [
NgxRxdbModule.forRoot(
getRxDatabaseCreator({
name: 'demo',
localDocuments: true,
localDocuments: false,
multiInstance: true,
ignoreDuplicate: false,
options: {
plugins: [
// will be loaded by together with core plugins
RxDBAttachmentsPlugin,
RxDBLeaderElectionPlugin,
],
storageType: localStorage['_ngx_rxdb_storage'] ?? 'dexie',
dumpPath: 'assets/data/db.dump.json',
},
Expand Down
40 changes: 38 additions & 2 deletions examples/demo/src/app/todos/todos.component.css
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
/* @import "https://unpkg.com/open-props"; */

.clear-completed:disabled {
color: #999;
cursor: not-allowed;
text-decoration: none;
}

.todo-list li label+.last-modified {
.todo-list li label + .last-modified {
position: absolute;
bottom: 4px;
right: 24px;
Expand All @@ -14,6 +16,40 @@
text-decoration: none !important;
}

.todo-list li:hover label:not(.editing)+.last-modified {
.todo-list li:hover label:not(.editing) + .last-modified {
display: block;
}

dialog {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
z-index: 1000;
width: 80%;
max-width: 500px;
padding: 20px;
border: 1px solid #ccc;
box-shadow:
0 2px 4px 0 rgba(0, 0, 0, 0.2),
0 25px 50px 0 rgba(0, 0, 0, 0.1);
background: white;
color: #111111;
font-family: unset;
}

dialog > form {
padding-top: 1em;
border-top: 1px solid #ccc;
width: 100%;
display: flex;
justify-content: space-between;
}
dialog > form > button {
display: block;
}

dialog::backdrop {
/* make the backdrop a semi-transparent black */
background-color: rgba(0, 0, 0, 0.4);
}
48 changes: 47 additions & 1 deletion examples/demo/src/app/todos/todos.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ <h1>todos</h1>
<li
*ngFor="let todo of $.todos | byStatus: $.filter; trackBy: trackByFn"
[class.completed]="todo.completed"
(contextmenu)="showContextMenu($event, todo)"
>
<div class="view">
<input
Expand Down Expand Up @@ -58,7 +59,7 @@ <h1>todos</h1>
<footer
class="footer"
[hidden]="$.count === 0"
*ngrxLet="($.todos | byStatus: 'ACTIVE')?.length; let remainig"
*ngrxLet="($.todos | byStatus: 'ACTIVE' : true)?.length; let remainig"
>
<ng-container *ngIf="remainig">
<span class="todo-count" [ngPlural]="remainig">
Expand All @@ -68,6 +69,12 @@ <h1>todos</h1>
</span>
</ng-container>
<ul class="filters">
<li>
<a href="javascript:void(0);" (click)="todosService.sortTodos('desc')">&#8675;</a>
</li>
<li>
<a href="javascript:void(0);" (click)="todosService.sortTodos('asc')">&#8673;</a>
</li>
<li>
<a
href="javascript:void(0);"
Expand Down Expand Up @@ -106,3 +113,42 @@ <h1>todos</h1>
</button>
</footer>
</section>

<dialog [open]="isDialogOpen" class="todo-dialog">
<header>Attachments:</header>
<ul *ngIf="selectedTodo?._attachments">
<li *ngFor="let attachment of selectedTodo._attachments | keyvalue">
<a
href="javascript:void(0);"
(click)="todosService.downloadAttachment(selectedTodo.id, attachment.key)"
>
{{ attachment.key }} - {{ attachment.value.type }}
</a>
&nbsp;
<button
type="button"
class="destroy"
(click)="
todosService.removeAttachment(selectedTodo.id, attachment.key);
isDialogOpen = false
"
>
🗙
</button>
</li>
</ul>
<form method="dialog" (ngSubmit)="selectedTodo = undefined; isDialogOpen = false">
<button type="button" (click)="fileInput.click()">Upload attachment</button>
<input
type="file"
accept=".txt"
#fileInput
style="display: none"
(change)="
todosService.uploadAttachment(selectedTodo.id, $any($event.target).files[0]);
isDialogOpen = false
"
/>
<button>Close Dialog</button>
</form>
</dialog>
30 changes: 11 additions & 19 deletions examples/demo/src/app/todos/todos.component.ts
Original file line number Diff line number Diff line change
@@ -1,32 +1,15 @@
import { animate, query, stagger, style, transition, trigger } from '@angular/animations';
import { ChangeDetectionStrategy, Component, inject } from '@angular/core';
import { Title } from '@angular/platform-browser';
import { Todo, todosListAnimation } from '@shared';
import { Observable, tap } from 'rxjs';
import { Todo } from './todos.model';
import { TodosService } from './todos.service';

const listAnimation = trigger('listAnimation', [
transition('* <=> *', [
query(
':enter',
[
style({ opacity: 0 }),
stagger('50ms', animate('60ms ease-in', style({ opacity: 1 }))),
],
{ optional: true }
),
query(':leave', stagger('10ms', animate('50ms ease-out', style({ opacity: 0 }))), {
optional: true,
}),
]),
]);

@Component({
selector: 'demo-todos',
templateUrl: './todos.component.html',
styleUrls: ['./todos.component.css'],
changeDetection: ChangeDetectionStrategy.OnPush,
animations: [listAnimation],
animations: [todosListAnimation],
})
export class TodosComponent {
private title = inject(Title);
Expand All @@ -42,7 +25,16 @@ export class TodosComponent {
);
count$ = this.todosService.count$;

isDialogOpen = false;
selectedTodo: Todo = undefined;

trackByFn = (index: number, item: Todo) => {
return item.last_modified;
};

showContextMenu(event: Event, todo: Todo) {
event.preventDefault();
this.selectedTodo = todo;
this.isDialogOpen = true;
}
}
6 changes: 1 addition & 5 deletions examples/demo/src/app/todos/todos.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,11 @@ import { RouterModule } from '@angular/router';
import { LetDirective, PushPipe } from '@ngrx/component';
import { NgxRxdbModule } from '@ngx-odm/rxdb';
import { NgxRxdbCollection, NgxRxdbCollectionService } from '@ngx-odm/rxdb/collection';
import { TODOS_COLLECTION_CONFIG, Todo } from '@shared';
import { TodosComponent } from './todos.component';
import { TODOS_COLLECTION_CONFIG } from './todos.config';
import { Todo } from './todos.model';
import { TodosPipe } from './todos.pipe';
import { todosReplicationStateFactory } from './todos.replication';
import { TodosService } from './todos.service';

TODOS_COLLECTION_CONFIG.options.replicationStateFactory = todosReplicationStateFactory;

@NgModule({
imports: [
RouterModule.forChild([{ path: '', component: TodosComponent }]),
Expand Down
10 changes: 6 additions & 4 deletions examples/demo/src/app/todos/todos.pipe.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { Pipe, PipeTransform } from '@angular/core';
import { Todo, TodosFilter } from './todos.model';
import { Pipe, PipeTransform, inject } from '@angular/core';
import { RXDB_CONFIG_COLLECTION } from '@ngx-odm/rxdb/config';
import { Todo, TodosFilter } from '@shared';

@Pipe({ name: 'byStatus' })
export class TodosPipe implements PipeTransform {
transform(value: Todo[], status: TodosFilter): Todo[] {
if (!value) {
colConfig = inject(RXDB_CONFIG_COLLECTION);
transform(value: Todo[], status: TodosFilter, force = false): Todo[] {
if (!value || (this.colConfig.options.useQueryParams && !force)) {
return value;
}
if (status === 'ALL') {
Expand Down
Loading
Loading