diff --git a/README.md b/README.md index 136070d07..a4ddb5836 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ If you would like to propose a challenge, this project is open source, so feel f ## Challenges -Check [all 37 challenges](https://angular-challenges.vercel.app/) +Check [all 38 challenges](https://angular-challenges.vercel.app/) ## Contributors ✨ diff --git a/apps/rxjs-catch-error/.eslintrc.json b/apps/rxjs-catch-error/.eslintrc.json new file mode 100644 index 000000000..b428c220b --- /dev/null +++ b/apps/rxjs-catch-error/.eslintrc.json @@ -0,0 +1,36 @@ +{ + "extends": ["../../.eslintrc.json"], + "ignorePatterns": ["!**/*"], + "overrides": [ + { + "files": ["*.ts"], + "rules": { + "@angular-eslint/directive-selector": [ + "error", + { + "type": "attribute", + "prefix": "app", + "style": "camelCase" + } + ], + "@angular-eslint/component-selector": [ + "error", + { + "type": "element", + "prefix": "app", + "style": "kebab-case" + } + ] + }, + "extends": [ + "plugin:@nx/angular", + "plugin:@angular-eslint/template/process-inline-templates" + ] + }, + { + "files": ["*.html"], + "extends": ["plugin:@nx/angular-template"], + "rules": {} + } + ] +} diff --git a/apps/rxjs-catch-error/README.md b/apps/rxjs-catch-error/README.md new file mode 100644 index 000000000..d1808fd03 --- /dev/null +++ b/apps/rxjs-catch-error/README.md @@ -0,0 +1,13 @@ +# catchError + +> Author: Devesh Chaudhari + +### Run Application + +```bash +npx nx serve rxjs-catch-error +``` + +### Documentation and Instruction + +Challenge documentation is [here](https://angular-challenges.vercel.app/challenges/rxjs/38-catch-error/). diff --git a/apps/rxjs-catch-error/jest.config.ts b/apps/rxjs-catch-error/jest.config.ts new file mode 100644 index 000000000..0f2fbb2cd --- /dev/null +++ b/apps/rxjs-catch-error/jest.config.ts @@ -0,0 +1,22 @@ +/* eslint-disable */ +export default { + displayName: 'rxjs-catch-error', + preset: '../../jest.preset.js', + setupFilesAfterEnv: ['/src/test-setup.ts'], + coverageDirectory: '../../coverage/apps/rxjs-catch-error', + transform: { + '^.+\\.(ts|mjs|js|html)$': [ + 'jest-preset-angular', + { + tsconfig: '/tsconfig.spec.json', + stringifyContentPathRegex: '\\.(html|svg)$', + }, + ], + }, + transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'], + snapshotSerializers: [ + 'jest-preset-angular/build/serializers/no-ng-attributes', + 'jest-preset-angular/build/serializers/ng-snapshot', + 'jest-preset-angular/build/serializers/html-comment', + ], +}; diff --git a/apps/rxjs-catch-error/project.json b/apps/rxjs-catch-error/project.json new file mode 100644 index 000000000..7c191a9fc --- /dev/null +++ b/apps/rxjs-catch-error/project.json @@ -0,0 +1,95 @@ +{ + "name": "rxjs-catch-error", + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "projectType": "application", + "prefix": "app", + "sourceRoot": "apps/rxjs-catch-error/src", + "tags": [], + "targets": { + "build": { + "executor": "@angular-devkit/build-angular:browser", + "outputs": ["{options.outputPath}"], + "options": { + "outputPath": "dist/apps/rxjs-catch-error", + "index": "apps/rxjs-catch-error/src/index.html", + "main": "apps/rxjs-catch-error/src/main.ts", + "polyfills": ["zone.js"], + "tsConfig": "apps/rxjs-catch-error/tsconfig.app.json", + "assets": [ + "apps/rxjs-catch-error/src/favicon.ico", + "apps/rxjs-catch-error/src/assets" + ], + "styles": ["apps/rxjs-catch-error/src/styles.scss"], + "scripts": [] + }, + "configurations": { + "production": { + "budgets": [ + { + "type": "initial", + "maximumWarning": "500kb", + "maximumError": "1mb" + }, + { + "type": "anyComponentStyle", + "maximumWarning": "2kb", + "maximumError": "4kb" + } + ], + "outputHashing": "all" + }, + "development": { + "buildOptimizer": false, + "optimization": false, + "vendorChunk": true, + "extractLicenses": false, + "sourceMap": true, + "namedChunks": true + } + }, + "defaultConfiguration": "production" + }, + "serve": { + "executor": "@angular-devkit/build-angular:dev-server", + "configurations": { + "production": { + "browserTarget": "rxjs-catch-error:build:production" + }, + "development": { + "browserTarget": "rxjs-catch-error:build:development" + } + }, + "defaultConfiguration": "development" + }, + "extract-i18n": { + "executor": "@angular-devkit/build-angular:extract-i18n", + "options": { + "browserTarget": "rxjs-catch-error:build" + } + }, + "lint": { + "executor": "@nx/linter:eslint", + "outputs": ["{options.outputFile}"], + "options": { + "lintFilePatterns": [ + "apps/rxjs-catch-error/**/*.ts", + "apps/rxjs-catch-error/**/*.html" + ] + } + }, + "test": { + "executor": "@nx/jest:jest", + "outputs": ["{workspaceRoot}/coverage/{projectRoot}"], + "options": { + "jestConfig": "apps/rxjs-catch-error/jest.config.ts", + "passWithNoTests": true + }, + "configurations": { + "ci": { + "ci": true, + "codeCoverage": true + } + } + } + } +} diff --git a/apps/rxjs-catch-error/src/app/app.component.spec.ts b/apps/rxjs-catch-error/src/app/app.component.spec.ts new file mode 100644 index 000000000..3905d62a4 --- /dev/null +++ b/apps/rxjs-catch-error/src/app/app.component.spec.ts @@ -0,0 +1,8 @@ +import { render } from '@testing-library/angular'; +import { AppComponent } from './app.component'; + +describe('AppComponent', () => { + test('...', async () => { + await render(AppComponent); + }); +}); diff --git a/apps/rxjs-catch-error/src/app/app.component.ts b/apps/rxjs-catch-error/src/app/app.component.ts new file mode 100644 index 000000000..17117e4b3 --- /dev/null +++ b/apps/rxjs-catch-error/src/app/app.component.ts @@ -0,0 +1,79 @@ +import { Component, ElementRef, ViewChild } from '@angular/core'; +import { HttpClient } from '@angular/common/http'; +import { concatMap, fromEvent, map } from 'rxjs'; +import { CommonModule } from '@angular/common'; + +@Component({ + standalone: true, + imports: [CommonModule], + selector: 'app-root', + template: ` +
+ Posible values: posts, comments, albums, photos, todos, users +
+
+ + +
+
+ {{ response | json }} +
+ `, + styles: [ + ` + body { + font-family: Arial, sans-serif; + display: flex; + align-items: center; + justify-content: center; + height: 100vh; + margin: 0; + } + .form-container { + text-align: center; + } + input { + padding: 8px; + margin-right: 8px; + border: 1px solid #ccc; + border-radius: 4px; + } + button { + padding: 8px 16px; + background-color: #007bff; + color: #fff; + border: none; + border-radius: 4px; + cursor: pointer; + } + `, + ], +}) +export class AppComponent { + @ViewChild('buttonRef', { static: true }) buttonRef!: ElementRef; + @ViewChild('inputRef', { static: true }) inputRef!: ElementRef; + response: unknown; + constructor(private http: HttpClient) {} + + ngOnInit() { + const buttonClick$ = fromEvent(this.buttonRef.nativeElement, 'click'); + buttonClick$ + .pipe( + map(() => this.inputRef.nativeElement.value), + concatMap((value) => + this.http.get(`https://jsonplaceholder.typicode.com/${value}/1`) + ) + ) + .subscribe({ + next: (value) => { + console.log(value); + this.response = value; + }, + error: (error) => { + console.log(error); + this.response = error; + }, + complete: () => console.log('done'), + }); + } +} diff --git a/apps/rxjs-catch-error/src/app/app.config.ts b/apps/rxjs-catch-error/src/app/app.config.ts new file mode 100644 index 000000000..610704b37 --- /dev/null +++ b/apps/rxjs-catch-error/src/app/app.config.ts @@ -0,0 +1,6 @@ +import { ApplicationConfig, importProvidersFrom } from '@angular/core'; +import { HttpClientModule } from '@angular/common/http'; + +export const appConfig: ApplicationConfig = { + providers: [importProvidersFrom(HttpClientModule)], +}; diff --git a/apps/rxjs-catch-error/src/assets/.gitkeep b/apps/rxjs-catch-error/src/assets/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/apps/rxjs-catch-error/src/favicon.ico b/apps/rxjs-catch-error/src/favicon.ico new file mode 100644 index 000000000..317ebcb23 Binary files /dev/null and b/apps/rxjs-catch-error/src/favicon.ico differ diff --git a/apps/rxjs-catch-error/src/index.html b/apps/rxjs-catch-error/src/index.html new file mode 100644 index 000000000..07c9d6692 --- /dev/null +++ b/apps/rxjs-catch-error/src/index.html @@ -0,0 +1,13 @@ + + + + + rxjs-catch-error + + + + + + + + diff --git a/apps/rxjs-catch-error/src/main.ts b/apps/rxjs-catch-error/src/main.ts new file mode 100644 index 000000000..514c89a08 --- /dev/null +++ b/apps/rxjs-catch-error/src/main.ts @@ -0,0 +1,7 @@ +import { bootstrapApplication } from '@angular/platform-browser'; +import { appConfig } from './app/app.config'; +import { AppComponent } from './app/app.component'; + +bootstrapApplication(AppComponent, appConfig).catch((err) => + console.error(err) +); diff --git a/apps/rxjs-catch-error/src/styles.scss b/apps/rxjs-catch-error/src/styles.scss new file mode 100644 index 000000000..77e408aa8 --- /dev/null +++ b/apps/rxjs-catch-error/src/styles.scss @@ -0,0 +1,5 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +/* You can add global styles to this file, and also import other style files */ diff --git a/apps/rxjs-catch-error/src/test-setup.ts b/apps/rxjs-catch-error/src/test-setup.ts new file mode 100644 index 000000000..15de72a3c --- /dev/null +++ b/apps/rxjs-catch-error/src/test-setup.ts @@ -0,0 +1,2 @@ +import '@testing-library/jest-dom'; +import 'jest-preset-angular/setup-jest'; diff --git a/apps/rxjs-catch-error/tailwind.config.js b/apps/rxjs-catch-error/tailwind.config.js new file mode 100644 index 000000000..38183db2c --- /dev/null +++ b/apps/rxjs-catch-error/tailwind.config.js @@ -0,0 +1,14 @@ +const { createGlobPatternsForDependencies } = require('@nx/angular/tailwind'); +const { join } = require('path'); + +/** @type {import('tailwindcss').Config} */ +module.exports = { + content: [ + join(__dirname, 'src/**/!(*.stories|*.spec).{ts,html}'), + ...createGlobPatternsForDependencies(__dirname), + ], + theme: { + extend: {}, + }, + plugins: [], +}; diff --git a/apps/rxjs-catch-error/tsconfig.app.json b/apps/rxjs-catch-error/tsconfig.app.json new file mode 100644 index 000000000..fff4a41d4 --- /dev/null +++ b/apps/rxjs-catch-error/tsconfig.app.json @@ -0,0 +1,10 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../dist/out-tsc", + "types": [] + }, + "files": ["src/main.ts"], + "include": ["src/**/*.d.ts"], + "exclude": ["jest.config.ts", "src/**/*.test.ts", "src/**/*.spec.ts"] +} diff --git a/apps/rxjs-catch-error/tsconfig.editor.json b/apps/rxjs-catch-error/tsconfig.editor.json new file mode 100644 index 000000000..8ae117d96 --- /dev/null +++ b/apps/rxjs-catch-error/tsconfig.editor.json @@ -0,0 +1,7 @@ +{ + "extends": "./tsconfig.json", + "include": ["src/**/*.ts"], + "compilerOptions": { + "types": ["jest", "node"] + } +} diff --git a/apps/rxjs-catch-error/tsconfig.json b/apps/rxjs-catch-error/tsconfig.json new file mode 100644 index 000000000..e01cf19bd --- /dev/null +++ b/apps/rxjs-catch-error/tsconfig.json @@ -0,0 +1,32 @@ +{ + "compilerOptions": { + "target": "es2022", + "useDefineForClassFields": false, + "forceConsistentCasingInFileNames": true, + "strict": true, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true + }, + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.app.json" + }, + { + "path": "./tsconfig.spec.json" + }, + { + "path": "./tsconfig.editor.json" + } + ], + "extends": "../../tsconfig.base.json", + "angularCompilerOptions": { + "enableI18nLegacyMessageIdFormat": false, + "strictInjectionParameters": true, + "strictInputAccessModifiers": true, + "strictTemplates": true + } +} diff --git a/apps/rxjs-catch-error/tsconfig.spec.json b/apps/rxjs-catch-error/tsconfig.spec.json new file mode 100644 index 000000000..1a4817a7d --- /dev/null +++ b/apps/rxjs-catch-error/tsconfig.spec.json @@ -0,0 +1,15 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../dist/out-tsc", + "module": "commonjs", + "types": ["jest", "node", "@testing-library/jest-dom"] + }, + "files": ["src/test-setup.ts"], + "include": [ + "jest.config.ts", + "src/**/*.test.ts", + "src/**/*.spec.ts", + "src/**/*.d.ts" + ] +} diff --git a/challenge-number.json b/challenge-number.json index 14d6f15fe..55abd212c 100644 --- a/challenge-number.json +++ b/challenge-number.json @@ -1,6 +1,6 @@ { - "total": 37, - "🟢": 13, + "total": 38, + "🟢": 14, "🟠": 117, "🔴": 207 } diff --git a/docs/src/content/docs/challenges/angular-performance/37-ngfor-biglist.md b/docs/src/content/docs/challenges/angular-performance/37-ngfor-biglist.md index 84e28f309..6cc1f48aa 100644 --- a/docs/src/content/docs/challenges/angular-performance/37-ngfor-biglist.md +++ b/docs/src/content/docs/challenges/angular-performance/37-ngfor-biglist.md @@ -3,7 +3,6 @@ title: 🟠 Optimize Big List description: Challenge 37 is about learning how virtualization optimize big list rendering sidebar: order: 117 - badge: New ---
Challenge #37
diff --git a/docs/src/content/docs/challenges/rxjs/38-rxjs-catch-error.md b/docs/src/content/docs/challenges/rxjs/38-rxjs-catch-error.md new file mode 100644 index 000000000..c1d7f587c --- /dev/null +++ b/docs/src/content/docs/challenges/rxjs/38-rxjs-catch-error.md @@ -0,0 +1,43 @@ +--- +title: 🟢 catchError +description: Challenge 38 is about learning error handling with catchError rxjs operator +sidebar: + order: 14 + badge: New +--- + +
Challenge #38
+ +## Information + +In this application, we will learn the correct placement of a catchError operator. If wrongly placed, the overall subscription will get completed. And we will not be able to send more requests. Aim is to preserve overall subscription, by taking care of error notification from inner observables. +Possible correct values for which we should get a success response are posts, comments, albums, photos, todos, users + +## Statement + +Handle the error without completion of the subscription. + +## Constraints + +## Users should be able to console log value/error each time clicks on the fetch button. + +:::note +Start the project by running: `npx nx serve rxjs-catch-error`. +::: + +:::tip[Reminder] +Your PR title must start with Answer:38. +::: + + diff --git a/docs/src/content/docs/index.mdx b/docs/src/content/docs/index.mdx index 80e2ce9a2..40c476e84 100644 --- a/docs/src/content/docs/index.mdx +++ b/docs/src/content/docs/index.mdx @@ -23,8 +23,8 @@ hero: import { Card, CardGrid } from '@astrojs/starlight/components'; - - This repository gathers 37 Challenges related to Angular, Nx, RxJS, Ngrx and Typescript. + + This repository gathers 38 Challenges related to Angular, Nx, RxJS, Ngrx and Typescript. These challenges resolve around real-life issues or specific features to elevate your skills.