Skip to content

Commit

Permalink
feat: request using @tanstack/angular-query
Browse files Browse the repository at this point in the history
  • Loading branch information
edbzn committed Jan 2, 2024
1 parent 860f23a commit c8ac2f1
Show file tree
Hide file tree
Showing 6 changed files with 68 additions and 44 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@

Todo MVC demo using [RxAngular](https://rx-angular.github.io/rx-angular/#/).

- Fully Zoneless for lightning-fast performance
- Push-based Architecture
- Standalone Components
- Fully Zoneless for lightning-fast performance
- Angular v17 with Control-Flow Syntax
- Consumes an API via HTTP (to showcase a real-world app)
- Consumes an API via HTTP (uses `@tanstack/angular-query` under the hood)

![demo](./app.png)
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"@rx-angular/cdk": "17.0.0",
"@rx-angular/state": "17.0.0",
"@rx-angular/template": "17.0.0",
"@tanstack/angular-query-experimental": "^5.17.0",
"body-parser": "^1.20.1",
"cors": "^2.8.5",
"rxjs": "~7.4.0",
Expand All @@ -45,4 +46,4 @@
"ts-node": "~8.3.0",
"typescript": "~5.2.2"
}
}
}
28 changes: 11 additions & 17 deletions projects/todo-mvc/src/app/todo-list.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,7 @@ import { inject, Injectable } from '@angular/core';
import { rxState } from '@rx-angular/state';
import { eventValue, rxActions } from '@rx-angular/state/actions';
import { merge, MonoTypeOperatorFunction } from 'rxjs';
import {
exhaustMap,
filter,
map,
tap,
withLatestFrom,
} from 'rxjs/operators';
import { exhaustMap, filter, map, tap, withLatestFrom } from 'rxjs/operators';
import { Todo, TodoFilter } from './todo.model';
import { TodoResource } from './todo.resource';

Expand Down Expand Up @@ -56,30 +50,29 @@ export class TodoService {
readonly #state = rxState<TodoState>(({ set, connect, select }) => {
set({ filter: 'all' });

const getAll$ = this.#todoResource
.getAll()
.pipe(map((todos) => ({ todos })));
connect('todos', this.#todoResource.allTodos.data);

const setFilter$ = this.actions.setFilter$.pipe(
map((filter) => ({ filter }))
);
const create$ = this.actions.create$.pipe(
filter(({ text }) => text.trim().length > 0),
tap(({ callback }) => callback()),
exhaustMap((todo) => this.#todoResource.create(todo)),
exhaustMap((todo) => this.#todoResource.addOne.mutateAsync(todo)),
map((todos) => ({ todos }))
);
const remove$ = this.actions.remove$.pipe(
exhaustMap((todo) => this.#todoResource.removeOne(todo)),
exhaustMap((todo) => this.#todoResource.removeOne.mutateAsync(todo)),
map((todos) => ({ todos }))
);
const update$ = this.actions.update$.pipe(
exhaustMap((todo) => this.#todoResource.updateOne(todo)),
exhaustMap((todo) => this.#todoResource.updateOne.mutateAsync(todo)),
map((todos) => ({ todos }))
);
const toggleAll$ = this.actions.toggleAll$.pipe(
withLatestFrom(select('todos')),
exhaustMap(([, todos]) =>
this.#todoResource.updateMany(
this.#todoResource.updateMany.mutateAsync(
todos.map((todo) => ({
...todo,
done: todos.every(({ done }) => !done),
Expand All @@ -90,7 +83,9 @@ export class TodoService {
);
const clearCompleted$ = this.actions.clearCompleted$.pipe(
withLatestFrom(select('todos').pipe(activeTodos)),
exhaustMap(([, todos]) => this.#todoResource.updateMany(todos)),
exhaustMap(([, todos]) =>
this.#todoResource.updateMany.mutateAsync(todos)
),
map((todos) => ({ todos }))
);
const drop$ = this.actions.drop$.pipe(
Expand All @@ -102,13 +97,12 @@ export class TodoService {
updatedTodos.splice(currentIndex, 0, todo);
return updatedTodos;
}),
exhaustMap((todos) => this.#todoResource.updateMany(todos)),
exhaustMap((todos) => this.#todoResource.updateMany.mutateAsync(todos)),
map((todos) => ({ todos }))
);

connect(
merge(
getAll$,
setFilter$,
create$,
remove$,
Expand Down
51 changes: 33 additions & 18 deletions projects/todo-mvc/src/app/todo.resource.ts
Original file line number Diff line number Diff line change
@@ -1,30 +1,45 @@
import { HttpClient } from "@angular/common/http";
import { inject, Injectable } from "@angular/core";
import { Todo } from "./todo.model";
import { HttpClient } from '@angular/common/http';
import { inject, Injectable } from '@angular/core';
import {
injectMutation,
injectQuery,
} from '@tanstack/angular-query-experimental';
import { lastValueFrom } from 'rxjs';
import { Todo } from './todo.model';

@Injectable({ providedIn: 'root' })
export class TodoResource {
readonly #http = inject(HttpClient);

private static endpoint = new URL('http://localhost:3000/todo').toString();

getAll() {
return this.#http.get<Todo[]>(TodoResource.endpoint);
}
allTodos = injectQuery(() => ({
queryKey: ['todos'],
initialData: [],
queryFn: () => lastValueFrom(this.#http.get<Todo[]>(TodoResource.endpoint)),
}));

create(todo: Pick<Todo, 'text'>) {
return this.#http.post<Todo[]>(TodoResource.endpoint, todo);
}
addOne = injectMutation(() => ({
mutationFn: (todo: Pick<Todo, 'text'>) =>
lastValueFrom(this.#http.post<Todo[]>(TodoResource.endpoint, todo)),
}));

removeOne(todo: Pick<Todo, 'id'>) {
return this.#http.delete<Todo[]>(`${TodoResource.endpoint}/${todo.id}`);
}
removeOne = injectMutation(() => ({
mutationFn: (todo: Pick<Todo, 'id'>) =>
lastValueFrom(
this.#http.delete<Todo[]>(`${TodoResource.endpoint}/${todo.id}`)
),
}));

updateOne(todo: Todo) {
return this.#http.put<Todo[]>(`${TodoResource.endpoint}/${todo.id}`, todo);
}
updateOne = injectMutation(() => ({
mutationFn: (todo: Todo) =>
lastValueFrom(
this.#http.put<Todo[]>(`${TodoResource.endpoint}/${todo.id}`, todo)
),
}));

updateMany(todos: Todo[]) {
return this.#http.put<Todo[]>(TodoResource.endpoint, todos);
}
updateMany = injectMutation(() => ({
mutationFn: (todos: Todo[]) =>
lastValueFrom(this.#http.put<Todo[]>(`${TodoResource.endpoint}`, todos)),
}));
}
11 changes: 6 additions & 5 deletions projects/todo-mvc/src/main.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
import { HttpClientModule } from '@angular/common/http';
import {
NgZone,
importProvidersFrom,
ɵNoopNgZone
} from '@angular/core';
import { NgZone, importProvidersFrom, ɵNoopNgZone } from '@angular/core';
import { bootstrapApplication } from '@angular/platform-browser';
import {
QueryClient,
provideAngularQuery,
} from '@tanstack/angular-query-experimental';
import { AppComponent } from './app/app.component';

bootstrapApplication(AppComponent, {
providers: [
{ provide: NgZone, useClass: ɵNoopNgZone },
importProvidersFrom(HttpClientModule),
provideAngularQuery(new QueryClient()),
],
}).catch((err) => console.error(err));
15 changes: 14 additions & 1 deletion yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2418,6 +2418,19 @@
resolved "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz"
integrity sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==

"@tanstack/angular-query-experimental@^5.17.0":
version "5.17.0"
resolved "https://registry.yarnpkg.com/@tanstack/angular-query-experimental/-/angular-query-experimental-5.17.0.tgz#b8e118f655358200b89f273f30e9c2f54131d47a"
integrity sha512-vcPqrJGhoMqWKVTtF7BPW6ZkRe+Lra06wkbbO/dU9+dY6Dwrt33DUw1V3437L7y5SEXDfzqY7AMCW9pHsckIsg==
dependencies:
"@tanstack/query-core" "5.17.0"
tslib "^2.6.2"

"@tanstack/[email protected]":
version "5.17.0"
resolved "https://registry.yarnpkg.com/@tanstack/query-core/-/query-core-5.17.0.tgz#3fd41e8557de9904c76e92b3820f62407223c990"
integrity sha512-LoBaPtbMY26kRS+ohII4thTsWkJJsXKGitOLikTo2aqPA4yy7cfFJITs8DRnuERT7tLF5xfG9Lnm33Vp/38Vmw==

"@tootallnate/once@2":
version "2.0.0"
resolved "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz"
Expand Down Expand Up @@ -8494,7 +8507,7 @@ tsconfig-paths@^4.1.2:
minimist "^1.2.6"
strip-bom "^3.0.0"

[email protected], tslib@^2.4.0:
[email protected], tslib@^2.4.0, tslib@^2.6.2:
version "2.6.2"
resolved "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz"
integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==
Expand Down

0 comments on commit c8ac2f1

Please sign in to comment.