diff --git a/README.md b/README.md
index 2503690..34e1d4d 100644
--- a/README.md
+++ b/README.md
@@ -45,6 +45,7 @@ If you would like to propose a challenge, this project is open source, so feel f
+
@@ -92,7 +93,7 @@ If you would like to propose a challenge, this project is open source, so feel f
diff --git a/apps/crud/src/app/app.component.ts b/apps/crud/src/app/app.component.ts
index 3a6584e..400d403 100644
--- a/apps/crud/src/app/app.component.ts
+++ b/apps/crud/src/app/app.component.ts
@@ -24,7 +24,6 @@ export class AppComponent implements OnInit {
this.http
.get('https://jsonplaceholder.typicode.com/todos')
.subscribe((todos) => {
- console.log('return', todos);
this.todos = todos;
});
}
diff --git a/apps/rxjs-to-signal/.eslintrc.json b/apps/interop-rxjs-signal/.eslintrc.json
similarity index 100%
rename from apps/rxjs-to-signal/.eslintrc.json
rename to apps/interop-rxjs-signal/.eslintrc.json
diff --git a/apps/rxjs-to-signal/README.md b/apps/interop-rxjs-signal/README.md
similarity index 52%
rename from apps/rxjs-to-signal/README.md
rename to apps/interop-rxjs-signal/README.md
index 37ff9a2..d6a6a51 100644
--- a/apps/rxjs-to-signal/README.md
+++ b/apps/interop-rxjs-signal/README.md
@@ -1,32 +1,26 @@
-migrate application to signals
+interoperability Rxjs and Signal
> Author: Thomas Laforge
-
-
### Information
-### Statement
+In this challenge, we have a small reactive application using RxJS and NgRx/Component-Store.
-### Step 1
-
-### Step 2
-
-### Constraints:
+The goal of this challenge is to use the new **Signal API** introduced in Angular v16. However, we should not convert everything. Certain portions of the code are better suited for RxJS rather than Signal. It is up to you to determine the threshold and observe how **Signal and RxJS coexist**, as well as how the interoperability is achieved in Angular.
### Submitting your work
1. Fork the project
2. clone it
3. npm ci
-4. `npx nx serve rxjs-to-signal`
+4. `npx nx serve interop-rxjs-signal`
5. _...work on it_
6. Commit your work
7. Submit a PR with a title beginning with **Answer:30** that I will review and other dev can review.
-
-
+
+
-
+
_You can ask any question on_
diff --git a/apps/rxjs-to-signal/jest.config.ts b/apps/interop-rxjs-signal/jest.config.ts
similarity index 85%
rename from apps/rxjs-to-signal/jest.config.ts
rename to apps/interop-rxjs-signal/jest.config.ts
index e0adc03..cfc3de3 100644
--- a/apps/rxjs-to-signal/jest.config.ts
+++ b/apps/interop-rxjs-signal/jest.config.ts
@@ -1,9 +1,9 @@
/* eslint-disable */
export default {
- displayName: 'rxjs-to-signal',
+ displayName: 'interop-rxjs-signal',
preset: '../../jest.preset.js',
setupFilesAfterEnv: ['/src/test-setup.ts'],
- coverageDirectory: '../../coverage/apps/rxjs-to-signal',
+ coverageDirectory: '../../coverage/apps/interop-rxjs-signal',
transform: {
'^.+\\.(ts|mjs|js|html)$': [
'jest-preset-angular',
diff --git a/apps/rxjs-to-signal/project.json b/apps/interop-rxjs-signal/project.json
similarity index 71%
rename from apps/rxjs-to-signal/project.json
rename to apps/interop-rxjs-signal/project.json
index 741d5ed..f101002 100644
--- a/apps/rxjs-to-signal/project.json
+++ b/apps/interop-rxjs-signal/project.json
@@ -1,26 +1,26 @@
{
- "name": "rxjs-to-signal",
+ "name": "interop-rxjs-signal",
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"projectType": "application",
"prefix": "app",
- "sourceRoot": "apps/rxjs-to-signal/src",
+ "sourceRoot": "apps/interop-rxjs-signal/src",
"tags": [],
"targets": {
"build": {
"executor": "@angular-devkit/build-angular:browser",
"outputs": ["{options.outputPath}"],
"options": {
- "outputPath": "dist/apps/rxjs-to-signal",
- "index": "apps/rxjs-to-signal/src/index.html",
- "main": "apps/rxjs-to-signal/src/main.ts",
+ "outputPath": "dist/apps/interop-rxjs-signal",
+ "index": "apps/interop-rxjs-signal/src/index.html",
+ "main": "apps/interop-rxjs-signal/src/main.ts",
"polyfills": ["zone.js"],
- "tsConfig": "apps/rxjs-to-signal/tsconfig.app.json",
+ "tsConfig": "apps/interop-rxjs-signal/tsconfig.app.json",
"assets": [
- "apps/rxjs-to-signal/src/favicon.ico",
- "apps/rxjs-to-signal/src/assets"
+ "apps/interop-rxjs-signal/src/favicon.ico",
+ "apps/interop-rxjs-signal/src/assets"
],
"styles": [
- "apps/rxjs-to-signal/src/styles.scss",
+ "apps/interop-rxjs-signal/src/styles.scss",
"./node_modules/@angular/material/prebuilt-themes/indigo-pink.css"
],
"scripts": []
@@ -56,10 +56,10 @@
"executor": "@angular-devkit/build-angular:dev-server",
"configurations": {
"production": {
- "browserTarget": "rxjs-to-signal:build:production"
+ "browserTarget": "interop-rxjs-signal:build:production"
},
"development": {
- "browserTarget": "rxjs-to-signal:build:development"
+ "browserTarget": "interop-rxjs-signal:build:development"
}
},
"defaultConfiguration": "development"
@@ -67,7 +67,7 @@
"extract-i18n": {
"executor": "@angular-devkit/build-angular:extract-i18n",
"options": {
- "browserTarget": "rxjs-to-signal:build"
+ "browserTarget": "interop-rxjs-signal:build"
}
},
"lint": {
@@ -75,8 +75,8 @@
"outputs": ["{options.outputFile}"],
"options": {
"lintFilePatterns": [
- "apps/rxjs-to-signal/**/*.ts",
- "apps/rxjs-to-signal/**/*.html"
+ "apps/interop-rxjs-signal/**/*.ts",
+ "apps/interop-rxjs-signal/**/*.html"
]
}
},
@@ -84,7 +84,7 @@
"executor": "@nx/jest:jest",
"outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
"options": {
- "jestConfig": "apps/rxjs-to-signal/jest.config.ts",
+ "jestConfig": "apps/interop-rxjs-signal/jest.config.ts",
"passWithNoTests": true
},
"configurations": {
diff --git a/apps/rxjs-to-signal/src/app/app.component.ts b/apps/interop-rxjs-signal/src/app/app.component.ts
similarity index 100%
rename from apps/rxjs-to-signal/src/app/app.component.ts
rename to apps/interop-rxjs-signal/src/app/app.component.ts
diff --git a/apps/rxjs-to-signal/src/app/app.config.ts b/apps/interop-rxjs-signal/src/app/app.config.ts
similarity index 100%
rename from apps/rxjs-to-signal/src/app/app.config.ts
rename to apps/interop-rxjs-signal/src/app/app.config.ts
diff --git a/apps/interop-rxjs-signal/src/app/detail/detail.component.ts b/apps/interop-rxjs-signal/src/app/detail/detail.component.ts
new file mode 100644
index 0000000..c1493b2
--- /dev/null
+++ b/apps/interop-rxjs-signal/src/app/detail/detail.component.ts
@@ -0,0 +1,33 @@
+import { DatePipe } from '@angular/common';
+import { Component, Input as RouterInput } from '@angular/core';
+import { RouterLink } from '@angular/router';
+import { Photo } from '../photo.model';
+
+@Component({
+ selector: 'app-photos',
+ standalone: true,
+ imports: [DatePipe, RouterLink],
+ template: `
+
+ Title: {{ photo.title }}
+ Owner: {{ photo.ownername }}
+ Date: {{ photo.datetaken | date }}
+ Tags: {{ photo.tags }}
+
+
+ Back
+
+ `,
+ host: {
+ class: 'p-5 block',
+ },
+})
+export default class DetailComponent {
+ @RouterInput({
+ required: true,
+ transform: (value: string) => JSON.parse(decodeURIComponent(value)),
+ })
+ photo!: Photo;
+}
diff --git a/apps/rxjs-to-signal/src/app/list/photos.component.ts b/apps/interop-rxjs-signal/src/app/list/photos.component.ts
similarity index 87%
rename from apps/rxjs-to-signal/src/app/list/photos.component.ts
rename to apps/interop-rxjs-signal/src/app/list/photos.component.ts
index 0d8b948..b8705e3 100644
--- a/apps/rxjs-to-signal/src/app/list/photos.component.ts
+++ b/apps/interop-rxjs-signal/src/app/list/photos.component.ts
@@ -7,7 +7,7 @@ import { MatProgressBarModule } from '@angular/material/progress-bar';
import { RouterLinkWithHref } from '@angular/router';
import { LetDirective } from '@ngrx/component';
import { provideComponentStore } from '@ngrx/component-store';
-import { debounceTime, distinctUntilChanged } from 'rxjs';
+import { debounceTime, distinctUntilChanged, skipWhile, tap } from 'rxjs';
import { Photo } from '../photo.model';
import { PhotoStore } from './photos.store';
@@ -38,10 +38,6 @@ import { PhotoStore } from './photos.store';
-
Page :{{ vm.page }} / {{ vm.pages }}
+
0; else noPhoto">
@@ -87,13 +87,25 @@ import { PhotoStore } from './photos.store';
})
export default class PhotosComponent implements OnInit {
store = inject(PhotoStore);
- readonly vm$ = this.store.vm$;
+ readonly vm$ = this.store.vm$.pipe(
+ tap(({ search }) => {
+ if (!this.formInit) {
+ this.search.setValue(search);
+ this.formInit = true;
+ }
+ })
+ );
+ private formInit = false;
search = new FormControl();
ngOnInit(): void {
this.store.search(
- this.search.valueChanges.pipe(debounceTime(300), distinctUntilChanged())
+ this.search.valueChanges.pipe(
+ skipWhile(() => !this.formInit),
+ debounceTime(300),
+ distinctUntilChanged()
+ )
);
}
diff --git a/apps/rxjs-to-signal/src/app/list/photos.store.ts b/apps/interop-rxjs-signal/src/app/list/photos.store.ts
similarity index 83%
rename from apps/rxjs-to-signal/src/app/list/photos.store.ts
rename to apps/interop-rxjs-signal/src/app/list/photos.store.ts
index 2d720cc..8f039c4 100644
--- a/apps/rxjs-to-signal/src/app/list/photos.store.ts
+++ b/apps/interop-rxjs-signal/src/app/list/photos.store.ts
@@ -10,6 +10,8 @@ import { filter, mergeMap, tap } from 'rxjs/operators';
import { Photo } from '../photo.model';
import { PhotoService } from '../photos.service';
+const PHOTO_STATE_KEY = 'photo_search';
+
export interface PhotoState {
photos: Photo[];
search: string;
@@ -51,6 +53,7 @@ export class PhotoStore
readonly vm$ = this.select(
{
photos: this.photos$,
+ search: this.search$,
page: this.page$,
pages: this.pages$,
endOfPage: this.endOfPage$,
@@ -61,7 +64,17 @@ export class PhotoStore
);
ngrxOnStoreInit() {
- this.setState(initialState);
+ const savedJSONState = localStorage.getItem(PHOTO_STATE_KEY);
+ if (savedJSONState === null) {
+ this.setState(initialState);
+ } else {
+ const savedState = JSON.parse(savedJSONState);
+ this.setState({
+ ...initialState,
+ search: savedState.search,
+ page: savedState.page,
+ });
+ }
}
ngrxOnStateInit() {
@@ -108,6 +121,10 @@ export class PhotoStore
photos: photo,
pages,
});
+ localStorage.setItem(
+ PHOTO_STATE_KEY,
+ JSON.stringify({ search, page })
+ );
},
(error: unknown) => this.patchState({ error, loading: false })
)
diff --git a/apps/rxjs-to-signal/src/app/photo.model.ts b/apps/interop-rxjs-signal/src/app/photo.model.ts
similarity index 100%
rename from apps/rxjs-to-signal/src/app/photo.model.ts
rename to apps/interop-rxjs-signal/src/app/photo.model.ts
diff --git a/apps/rxjs-to-signal/src/app/photos.service.ts b/apps/interop-rxjs-signal/src/app/photos.service.ts
similarity index 100%
rename from apps/rxjs-to-signal/src/app/photos.service.ts
rename to apps/interop-rxjs-signal/src/app/photos.service.ts
diff --git a/apps/rxjs-to-signal/src/assets/.gitkeep b/apps/interop-rxjs-signal/src/assets/.gitkeep
similarity index 100%
rename from apps/rxjs-to-signal/src/assets/.gitkeep
rename to apps/interop-rxjs-signal/src/assets/.gitkeep
diff --git a/apps/rxjs-to-signal/src/favicon.ico b/apps/interop-rxjs-signal/src/favicon.ico
similarity index 100%
rename from apps/rxjs-to-signal/src/favicon.ico
rename to apps/interop-rxjs-signal/src/favicon.ico
diff --git a/apps/rxjs-to-signal/src/index.html b/apps/interop-rxjs-signal/src/index.html
similarity index 100%
rename from apps/rxjs-to-signal/src/index.html
rename to apps/interop-rxjs-signal/src/index.html
diff --git a/apps/rxjs-to-signal/src/main.ts b/apps/interop-rxjs-signal/src/main.ts
similarity index 100%
rename from apps/rxjs-to-signal/src/main.ts
rename to apps/interop-rxjs-signal/src/main.ts
diff --git a/apps/rxjs-to-signal/src/styles.scss b/apps/interop-rxjs-signal/src/styles.scss
similarity index 100%
rename from apps/rxjs-to-signal/src/styles.scss
rename to apps/interop-rxjs-signal/src/styles.scss
diff --git a/apps/rxjs-to-signal/src/test-setup.ts b/apps/interop-rxjs-signal/src/test-setup.ts
similarity index 100%
rename from apps/rxjs-to-signal/src/test-setup.ts
rename to apps/interop-rxjs-signal/src/test-setup.ts
diff --git a/apps/rxjs-to-signal/tailwind.config.js b/apps/interop-rxjs-signal/tailwind.config.js
similarity index 100%
rename from apps/rxjs-to-signal/tailwind.config.js
rename to apps/interop-rxjs-signal/tailwind.config.js
diff --git a/apps/rxjs-to-signal/tsconfig.app.json b/apps/interop-rxjs-signal/tsconfig.app.json
similarity index 100%
rename from apps/rxjs-to-signal/tsconfig.app.json
rename to apps/interop-rxjs-signal/tsconfig.app.json
diff --git a/apps/rxjs-to-signal/tsconfig.editor.json b/apps/interop-rxjs-signal/tsconfig.editor.json
similarity index 100%
rename from apps/rxjs-to-signal/tsconfig.editor.json
rename to apps/interop-rxjs-signal/tsconfig.editor.json
diff --git a/apps/rxjs-to-signal/tsconfig.json b/apps/interop-rxjs-signal/tsconfig.json
similarity index 100%
rename from apps/rxjs-to-signal/tsconfig.json
rename to apps/interop-rxjs-signal/tsconfig.json
diff --git a/apps/rxjs-to-signal/tsconfig.spec.json b/apps/interop-rxjs-signal/tsconfig.spec.json
similarity index 100%
rename from apps/rxjs-to-signal/tsconfig.spec.json
rename to apps/interop-rxjs-signal/tsconfig.spec.json
diff --git a/apps/rxjs-to-signal/src/app/detail/detail.component.ts b/apps/rxjs-to-signal/src/app/detail/detail.component.ts
deleted file mode 100644
index 63cf622..0000000
--- a/apps/rxjs-to-signal/src/app/detail/detail.component.ts
+++ /dev/null
@@ -1,48 +0,0 @@
-import { JsonPipe, NgFor, NgIf } from '@angular/common';
-import { Component, Input } from '@angular/core';
-import { ReactiveFormsModule } from '@angular/forms';
-import { MatFormFieldModule } from '@angular/material/form-field';
-import { MatInputModule } from '@angular/material/input';
-import { MatProgressBarModule } from '@angular/material/progress-bar';
-import { LetDirective } from '@ngrx/component';
-import { Photo } from '../photo.model';
-
-@Component({
- selector: 'app-photos',
- standalone: true,
- imports: [
- ReactiveFormsModule,
- MatFormFieldModule,
- MatProgressBarModule,
- NgIf,
- NgFor,
- MatInputModule,
- LetDirective,
- JsonPipe,
- ],
- template: `
-
- {{ photo | json }}
- `,
- // providers: [provideComponentStore(PhotoStore)],
- host: {
- class: 'p-5 block',
- },
-})
-export default class DetailComponent {
- @Input({ required: true }) photo!: Photo;
- // store = inject(PhotoStore);
- // readonly vm$ = this.store.vm$;
-
- // search = new FormControl();
-
- // ngOnInit(): void {
- // this.store.search(
- // this.search.valueChanges.pipe(debounceTime(300), distinctUntilChanged())
- // );
- // }
-
- // trackById(index: number, photo: Photo) {
- // return photo.id;
- // }
-}