Skip to content

Commit

Permalink
docs: update README
Browse files Browse the repository at this point in the history
  • Loading branch information
voznik committed Jan 8, 2024
1 parent 03a1a51 commit dff456f
Show file tree
Hide file tree
Showing 3 changed files with 295 additions and 143 deletions.
212 changes: 138 additions & 74 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,23 @@
- [General info](#general-info)
- [Technologies](#technologies)
- [Install](#install)
- [Usage](#usage)
- [Usage (NgModule)](#usage-ngmodule)
- [In your `AppModule`](#in-your-appmodule)
- [In your `FeatureModule`](#in-your-featuremodule)
- [In your `FeatureService`](#in-your-featureservice)
- [Usage (Standalone)](#usage-standalone)
- [In your `main.ts`](#in-your-maints)
- [In your `Component`](#in-your-component)
- [Using `sginals` \& `signalStore` from `@ngrx/signals`](#using-sginals--signalstore-from-ngrxsignals)
- [Features](#features)
- [Diagrams](#diagrams)
- [Status](#status)
- [Inspiration](#inspiration)
- [Notes](#notes)
- [Contact](#contact)

## General info

If you don't want to setup RxDB manually in your next Angular project - just import `NgxRxdbModule`
If you don't want to setup RxDB manually in your next Angular project - just import `NgxRxdbModule` or go with `provideRxDatabase` and `provideRxCollection` if standalone component is your choice.

## Technologies

Expand All @@ -41,28 +44,28 @@ If you don't want to setup RxDB manually in your next Angular project - just imp

`npm install @ngx-odm/rxdb`

## Usage
## Usage (NgModule)

### In your `AppModule`

```typescript
import { NgxRxdbModule } from '@ngx-odm/rxdb';
import { getRxDatabaseCreator } from '@ngx-odm/rxdb/config';

@NgModule({
imports: [
// ... other imports
// ...
NgxRxdbModule.forRoot({
// optional, NgxRxdbConfig extends RxDatabaseCreator, will be merged with default config
name: 'ngx', // <- name (required, 'ngx')
adapter: 'idb', // <- storage-adapter (required, default: 'idb')
password: '123456789', // <- password (optional)
NgxRxdbModule.forRoot(getRxDatabaseCreator({
name: 'demo', // <- name (required, 'ngx')
storage: getRxStorageDexie(), // <- storage (not required, 'dexie')
localDocuments: true,
multiInstance: true, // <- multiInstance (optional, default: true)
queryChangeDetection: false, // <- queryChangeDetection (optional, default: false)
ignoreDuplicate: false,
options: {
// NgxRxdb options (optional)
schemas: [...CollectionConfigs], // array of NgxRxdbCollectionConfig (optional)
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 All @@ -76,17 +79,20 @@ export class AppModule {}
> https://rxdb.info/rx-schema.html
```typescript
import type { RxCollectionCreatorExtended } from '@ngx-odm/rxdb/config';
// create or import your schema
const todoSchema: RxSchema = require('../../../assets/data/todo.schema.json');
// create config
// NgxRxdbCollectionConfig extends RxCollectionCreator
const todoCollectionConfig: NgxRxdbCollectionConfig = {
const todoCollectionConfig: RxCollectionCreatorExtended = {
name: 'todo', // <- name (required)
schema: todoSchema, // <- name (required)
statics: {}, // <- collection methods (optional)
methods: {}, // <- instance-methods methods (optional)
schema: todoSchema, // <- schema (not required, see below)
localDocuments: true,
options: {
initialDocs: [], // docs to be imported into empty collection (optional)
schemaUrl: 'assets/data/todo.schema.json', // load schema from remote url (optional)
replicationStateFactory: collection => {
// provide replication state (optional)
},
},
};

Expand All @@ -95,83 +101,157 @@ const todoCollectionConfig: NgxRxdbCollectionConfig = {
// ... other imports
NgxRxdbModule.forFeature(todoCollectionConfig),
],
// declarations
// providers
})
export class TodosModule {}
export class TodosModule {
constructor(
@Inject(NgxRxdbCollectionService) private collectionService: NgxRxdbCollection<Todo>
) {
this.collectionService.sync(); // INFO: collection is ready
}
}
```

### In your `FeatureService`

```typescript
@Injectable()
export class TodosService {
private collectionService: NgxRxdbCollection<Todo> = inject<NgxRxdbCollection<Todo>>(NgxRxdbCollectionService);
// store & get filter as property of a `local` document
filter$ = this.collectionService
.getLocal('local', 'filterValue')
.pipe(startWith('ALL'), distinctUntilChanged());
// get count of documents in collection as observable (use fast calculation with static colection method)
// get count of documents in collection as observable
count$ = this.collectionService.count();

constructor(private collectionService: NgxRxdbCollectionService<Todo>) {}
// get documents from collection as observable using `RxQuery` mango-queries
selectTodos(): Observable<Todo[]> {
return this.filter$.pipe(
switchMap(filterValue => {
const queryObj = {
selector: {
createdAt: {
$gt: null,
},
completed: filterValue === 'COMPLETED',
},
sort: [{ createdAt: 'desc' } as any],
};
return this.collectionService.docs(queryObj);
})
);
}
// get documents from collection as observable
// optionally using `RxQuery` mango-queries
todos$: Observable<Todo[]> = this.collectionService.docs();

// add new document
add(name: string): void {
const payload: Todo = { guid: uuid(), name, done: false, dateCreated: Date.now() };
this.collectionService.insert(payload);
}

// update prop od existing document
// update property of single document
toggle(guid: string, done: boolean): void {
this.collectionService.update(guid, { done });
this.collectionService.set(guid, { done });
}

// use `bulk` to delete all dcouments by qeury
removeDoneTodos(): void {
const rulesObject = { done: { $eq: true } };
this.collectionService.removeBulkBy(rulesObject);
// update many documents with partial data by query
toggleAllTodos(completed: boolean) {
this.collectionService.updateBulk(
{ selector: { completed: { $eq: !completed } } },
{ completed }
);
}

// remove many dcouments by qeury
removeCompletedTodos(): void {
cthis.collectionService.removeBulk({ selector: { completed: true } });
}
// ...
}
```

## Features
## Usage (Standalone)

By using this module you can
### In your `main.ts`

- Automatically initialize db with settings, optionally provide db dumb to pre-fill with collections & documents
- Automatically initialize RxCollection for each _lazy-loaded Feature module_ with config
- Work straight with `db.collection` or via _NgxRxdbCollectionService_ wrapper with some helper methods
```typescript
import { provideRxDatabase } from '@ngx-odm/rxdb';
import { getRxDatabaseCreator } from '@ngx-odm/rxdb/config';

export const appConfig: ApplicationConfig = {
providers: [
// ... other providers
provideRxDatabase(
getRxDatabaseCreator({
name: 'demo',
localDocuments: true,
multiInstance: true,
ignoreDuplicate: false,
storage: getRxStorageDexie()
})
),
],
};

To-do list:
bootstrapApplication(AppComponent, appConfig).catch(err => console.error(err));
```

- Enable sync
- ...
### In your `Component`

## Diagrams
```typescript
import { provideRxCollection } from '@ngx-odm/rxdb';

@Component({
standalone: true,
// ...
providers: [provideRxCollection(config)]
})
export class StandaloneComponent {
readonly todoCollection = inject(NgxRxdbCollectionService<Todo>);
}
```

### Using `sginals` & `signalStore` from `@ngrx/signals`

```typescript
import { signalStore } from '@ngrx/signals';
import { withEntities } from '@ngrx/signals/entities';
import { withCollectionService } from '@ngx-odm/rxdb/signals';
import { withDevtools } from '@angular-architects/ngrx-toolkit';

export const TodoStore = signalStore(
{ providedIn: 'root' },
withDevtools('todo'),
withEntities<Todo>(),
// INFO: an instance of RxCollection will be provided by this
withCollectionService<Todo, TodosFilter, RxCollectionCreatorExtended>({
filter: 'ALL' as TodosFilter,
collectionConfig: TodosCollectionConfig,
}),
...
);

@Component({
standalone: true,
// ...
providers: [TodoStore],
})
export class StandaloneComponent {
readonly todoStore = inject(TodoStore);

constructor() {
effect(() => {
const { filter, entities } = this.todoStore;
});
}
}
```

## Features

By using this module you can simplify your work with RxDB in Angular application:

- Automatically initialize db with settings
- optionally provide db dumb to pre-fill collections
- optionally provide array of initial documents to pre-fill collection
- optionally provide remote location for schema and fetch it automatically before create collection (e.g. to maintain single source of truth for schema)
- 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)
- Work with signals and entities with `@ngrx/signals` and `@ngrx/entity` (optionally _zoneless_) (see [example](examples/standalone/src/app/todos/todos.store.ts))

<!-- ## Diagrams
![NgxRxdbModule Initialization UML](examples/uml.NgxRxdbModule.png)
NgxRxdbModule Initialization UML
![NgxRxdbService Sequence UML](examples/uml.NgxRxdbService.png)
NgxRxdbModule Initialization UML
NgxRxdbModule Initialization UML -->

## Status

Expand All @@ -184,26 +264,10 @@ Project inspired by
- [rxdb-angular2-example](https://github.com/pubkey/rxdb/blob/master/examples/angular2/README.md#rxdb-angular2-example)
- [Angular NgRx Material Starter](https://tomastrajan.github.io/angular-ngrx-material-starter#/examples/todos)
- _The Angular Library Series_ from [Angular In Depth](https://blog.angularindepth.com/)
- [NgRx Toolkit](https://github.com/angular-architects/ngrx-toolkit/blob/main/libs/ngrx-toolkit/src/lib/with-data-service.ts) - inspired by Manfred Steyer and created singnals tool by analogy of `withDataService` from [Angular Architects](https://angulararchitects.io/)

## Notes

- example on how to trigger a query on signal change. e.g. filter todos using DB query and not in memory
so `fetch` then must be triggered every time

```
toObservable(filter)
.pipe(
switchMap(filterValue => {
if (filterValue === 'COMPLETED') {
query.selector = { completed: { $eq: true } };
} else if (filterValue === 'ACTIVE') {
query.selector = { completed: { $eq: false } };
}
return find(query);
}),
takeUntilDestroyed(this)
).subscribe();
```

## Contact

Expand Down
3 changes: 1 addition & 2 deletions examples/demo/src/app/todos/services/todos.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,8 +109,7 @@ export class TodosService {
}

removeCompletedTodos(): void {
const rulesObject = { selector: { completed: true } };
this.collectionService.removeBulk(rulesObject);
this.collectionService.removeBulk({ selector: { completed: true } });
this.filterTodos('ALL');
}

Expand Down
Loading

0 comments on commit dff456f

Please sign in to comment.