diff --git a/README.md b/README.md
index c812807..4a47b97 100644
--- a/README.md
+++ b/README.md
@@ -8,10 +8,11 @@
## Intro
-This project has been created with two purposes:
+This project has been created with three purposes:
- The first purpose is to assist you in becoming better at Angular and its ecosystem. 💪
- The second purpose is to share best practices and different implementations of the same problem to gain diverse perspectives. 📖
+- The last one is to lower the barrier to open source contribution.
**Sharing knowledge can benefit everyone.**
@@ -25,78 +26,6 @@ If you would like to propose a challenge, this project is open source, so feel f
Check [all 35 challenges](https://angular-challenges.vercel.app/)
-## Challenges (previous version)
-
-> Click the following badges to join your next challenge.
->
->
->
->
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
## Contributors ✨
@@ -122,15 +51,6 @@ Check [all 35 challenges](https://angular-challenges.vercel.app/)
-
-
-
-
-
-
-
-
-
Contributions of any kind are welcome.
## License
diff --git a/apps/performance/memoized/README.md b/apps/performance/memoized/README.md
index f16afc0..c3ae1bc 100644
--- a/apps/performance/memoized/README.md
+++ b/apps/performance/memoized/README.md
@@ -4,9 +4,9 @@
-### Information
+## Information
-### Statement
+## Statement
### Step 1
diff --git a/apps/testing-forms/README.md b/apps/testing-forms/README.md
index 5ebaa60..a996cd0 100644
--- a/apps/testing-forms/README.md
+++ b/apps/testing-forms/README.md
@@ -2,7 +2,7 @@
> Author: Thomas Laforge
-### Statement:
+## Statement:
NOT IMPLEMENTED YET
diff --git a/apps/testing-table/README.md b/apps/testing-table/README.md
index 978debe..f5e232e 100644
--- a/apps/testing-table/README.md
+++ b/apps/testing-table/README.md
@@ -2,7 +2,7 @@
> Author: Thomas Laforge
-### Statement:
+## Statement:
NOT IMPLEMENTED YET
diff --git a/docs/src/content/docs/challenges/angular-performance/12-scroll-cd.md b/docs/src/content/docs/challenges/angular-performance/12-scroll-cd.md
index eaf037c..1782f95 100644
--- a/docs/src/content/docs/challenges/angular-performance/12-scroll-cd.md
+++ b/docs/src/content/docs/challenges/angular-performance/12-scroll-cd.md
@@ -9,7 +9,7 @@ WIP
Challenge #12
-### Information
+## Information
In this challenge, you will need to optimize the change detection cycles run by Angular.
@@ -22,11 +22,11 @@ The following video will explain what is the goal of this challenge.
-### Statement
+## Statement
Your goal for this challenge is to avoid all unnecessary change detection cycles and trigger a CD only when needed.
-#### Constraint:
+## Constraint:
You cannot opt-out of zone.js. If this code is part of a large project and you opt out of zone.js, you will break many things within your application.
diff --git a/docs/src/content/docs/challenges/angular-performance/32-bug-cd.md b/docs/src/content/docs/challenges/angular-performance/32-bug-cd.md
index 014b94f..16d00a3 100644
--- a/docs/src/content/docs/challenges/angular-performance/32-bug-cd.md
+++ b/docs/src/content/docs/challenges/angular-performance/32-bug-cd.md
@@ -11,15 +11,15 @@ WIP
This challenge is inspired by a real-life example that I simplified to create this nice challenge.
-### Information
+## Information
In this small application, we have a navigation menu to route our application to either `barComponent` or `FooComponent`. However our application is not loading and no errors are displayed inside the console.
-### Statement
+## Statement
The goal of the challenge is to debug this application and make it work.
-#### Hints
+## Hints
Hint 1
diff --git a/docs/src/content/docs/challenges/angular-performance/34-default-onpush.md b/docs/src/content/docs/challenges/angular-performance/34-default-onpush.md
index c5d1992..d4ecc07 100644
--- a/docs/src/content/docs/challenges/angular-performance/34-default-onpush.md
+++ b/docs/src/content/docs/challenges/angular-performance/34-default-onpush.md
@@ -9,7 +9,7 @@ WIP
Challenge #34
-### Information
+## Information
In this series of challenges, you will learn how to optimize and enhance the performance of your Angular Application.
@@ -27,11 +27,11 @@ If you click on one of the bars (indicated by the yellow arrow on the picture be

-### Statement
+## Statement
The goal of this challenge is to improve the clustering of change detection within the application.
-### Hints:
+## Hints:
Hint 1
diff --git a/docs/src/content/docs/challenges/angular/10-pipe-utility.md b/docs/src/content/docs/challenges/angular/10-pipe-utility.md
index 9ed65d0..ac19905 100644
--- a/docs/src/content/docs/challenges/angular/10-pipe-utility.md
+++ b/docs/src/content/docs/challenges/angular/10-pipe-utility.md
@@ -13,11 +13,11 @@ The goal of this serie of 3 pipe challenges is to master PIPES in Angular.
Pure pipe are a very useful way to transform data from your template. The difference between calling a function and a pipe is that pure pire are memoized. So they won't be recalculated every change detection cycle if the inputs hasn't changed.
-### Information:
+## Information:
In this third exercice, you want to access utils functions. Currently we cannot access them directly from your template. The goal is to create a specific pipe for this utils file where you will need to pass the name of the function you want to call and the needed arguments.
-### Constraints:
+## Constraints:
- must be strongly typed
diff --git a/docs/src/content/docs/challenges/angular/13-styling.md b/docs/src/content/docs/challenges/angular/13-styling.md
index 7b9a042..dec1f11 100644
--- a/docs/src/content/docs/challenges/angular/13-styling.md
+++ b/docs/src/content/docs/challenges/angular/13-styling.md
@@ -9,6 +9,8 @@ WIP
Challenge #13
+## Information
+
Styling is an important part of a day job of a frontend developer often underestimated. In Angular application, I often see people use `@Input()` to customize the style of their component. But `@Input()` should be used only for the logic and we should use other technique for styling. We can take advantage of css variable and host-context.
In this challenge, you will need to use both to delete all `@Input()` from your code.
@@ -17,7 +19,7 @@ Styling is an important aspect of a frontend developer's day job, but it is ofte
In this challenge, you will need to use both CSS variables and :host-context to remove all `@Input()` from your code.
-### Constraints:
+## Constraints:
- In your final submission, your component should not contain any lines of code. All styling should be handled within the decorator _(or external css files if you prefer)_
diff --git a/docs/src/content/docs/challenges/angular/16-di.md b/docs/src/content/docs/challenges/angular/16-di.md
index e50a0e6..5f275a9 100644
--- a/docs/src/content/docs/challenges/angular/16-di.md
+++ b/docs/src/content/docs/challenges/angular/16-di.md
@@ -9,7 +9,7 @@ WIP
Challenge #16
-### Information
+## Information
To successfully complete this challenge, you will need to have a good understanding of how Dependency Injection works inside Angular.
@@ -17,11 +17,11 @@ The goal is to provide the CurrencyService at the row level, so that each row di
One way to achieve this is by adding a second argument to the pipe, but this is not allowed.
-### Statement
+## Statement
- Your task is to display the correct currency for each row.
-### Constraints:
+## Constraints:
- You cannot modify the pipe.
- You cannot wrap the row inside a component, as this will break the layout.
diff --git a/docs/src/content/docs/challenges/angular/21-achor-scrolling.md b/docs/src/content/docs/challenges/angular/21-achor-scrolling.md
index 40af3cf..0b0aebc 100644
--- a/docs/src/content/docs/challenges/angular/21-achor-scrolling.md
+++ b/docs/src/content/docs/challenges/angular/21-achor-scrolling.md
@@ -9,11 +9,11 @@ WIP
Challenge #21
-### Information
+## Information
You begin with an application that has basic navigation and anchor navigation in the `HomeComponent`. However, using `href` recreates the path each time and refreshes the page.
-### Statement
+## Statement
- Your task is to refactor this application to use the built-in navigation tool to better fit within the Angular Framework. You can explore the router, but it's better to stay within the template and use the `RouterLink` directive.
- To improve the user experience, add smooth scrolling.
diff --git a/docs/src/content/docs/challenges/angular/22-router-input.md b/docs/src/content/docs/challenges/angular/22-router-input.md
index e88ad4f..bbbe25f 100644
--- a/docs/src/content/docs/challenges/angular/22-router-input.md
+++ b/docs/src/content/docs/challenges/angular/22-router-input.md
@@ -9,7 +9,7 @@ WIP
Challenge #22
-### Statement
+## Statement
In this small application, you can pass data though routing to `TestComponent`. v16 of Angular introduiced `RouterInput`. The goal of this exercice is to refactor the code to use the new `RouterInput` strategy.
diff --git a/docs/src/content/docs/challenges/angular/30-interop-rxjs-signal.md b/docs/src/content/docs/challenges/angular/30-interop-rxjs-signal.md
index 856b4e7..70cb041 100644
--- a/docs/src/content/docs/challenges/angular/30-interop-rxjs-signal.md
+++ b/docs/src/content/docs/challenges/angular/30-interop-rxjs-signal.md
@@ -9,7 +9,7 @@ WIP
Challenge #30
-### Information
+## Information
In this challenge, we have a small reactive application using RxJS and NgRx/Component-Store.
diff --git a/docs/src/content/docs/challenges/angular/31-module-to-standalone.md b/docs/src/content/docs/challenges/angular/31-module-to-standalone.md
index 152c6f2..11cc8ee 100644
--- a/docs/src/content/docs/challenges/angular/31-module-to-standalone.md
+++ b/docs/src/content/docs/challenges/angular/31-module-to-standalone.md
@@ -9,7 +9,7 @@ WIP
Challenge #31
-### Information
+## Information
In v14, standalone components were released and made stable in v15. If you haven't played with them, it's never too late. You can try them out in this challenge.
@@ -19,7 +19,7 @@ Finally, standalone components are very simple to understand, but routing/lazy-l
After completing this challenge, standalone components will no longer hold any secrets for you.
-### Statement
+## Statement
The goal of this challenge is to migrate your application from module based components to standalone components.
diff --git a/docs/src/content/docs/challenges/angular/33-decoupling.md b/docs/src/content/docs/challenges/angular/33-decoupling.md
index 6929f30..cb72600 100644
--- a/docs/src/content/docs/challenges/angular/33-decoupling.md
+++ b/docs/src/content/docs/challenges/angular/33-decoupling.md
@@ -12,7 +12,7 @@ WIP
> Big thanks to **Robin Goetz** and his [Spartan Project](https://github.com/goetzrobin/spartan).
> This challenge was proposed by Robin and is strongly inspired by his project.
-### Information
+## Information
The goal of this challenge is to separate the behavior of a component from its style. For the purpose of this challenge, we will be working on a button element. When we click on it, we will toggle a _disabled_ property which will change the style of the element. This is quite useless in real life but the challenge aims to demonstate a useful concept.
@@ -20,7 +20,7 @@ The behavior of the component (referred to as the _brain_ in the Spartan stack)
However the button's helmet needs to access the state of the component to style the button differently based on its state. As mention above, we cannot import the `BtnDisabledDirective` directly into the helmet library as done currently. If you go to [`BtnHelmetDirective`](../../libs/decoupling/helmet/src/lib/btn-style.directive.ts), you will encounter a linting error. **A project tagged with "type:hlm" can only depend on libs tagged with "type:core"**.
-### Statement
+## Statement
The goal of this challenge is to find a way to decouple both Directives.
diff --git a/docs/src/content/docs/challenges/angular/6-permissions.md b/docs/src/content/docs/challenges/angular/6-permissions.md
index 5e6f43c..50878ed 100644
--- a/docs/src/content/docs/challenges/angular/6-permissions.md
+++ b/docs/src/content/docs/challenges/angular/6-permissions.md
@@ -9,13 +9,13 @@ WIP
Challenge #6
-### Information
+## Information
Structural directive is an important concept you will need to master to improve your angular skills and knowledge. This will be the first part of this challenge.
Guard is also very important since you will always need it in every application you build.
-### Statement
+## Statement
In LoginComponent, you will find 6 buttons corresponding at 6 differents users.
@@ -27,11 +27,11 @@ In LoginComponent, you will find 6 buttons corresponding at 6 differents users.
- Client
- Everyone
-### Step 1
+## Step 1
In **InformationComponent**, display the correct piece of information for each roles.
-#### Constraints:
+### Constraints:
- no ngIf directive inside **InformationComponent**
- importing the store inside **InformationComponent** is not allowed.
@@ -50,7 +50,7 @@ You should end up with something like below:
Info Only for superadmin
```
-### Step 2
+## Step 2
In **Routes.ts**, route all user to the correct **DashboardComponent** using **CanMatch** guard.
diff --git a/docs/src/content/docs/challenges/angular/8-pipe-pure.md b/docs/src/content/docs/challenges/angular/8-pipe-pure.md
index c08a516..5ed72d4 100644
--- a/docs/src/content/docs/challenges/angular/8-pipe-pure.md
+++ b/docs/src/content/docs/challenges/angular/8-pipe-pure.md
@@ -13,11 +13,11 @@ The goal of this serie of 3 pipe challenges is to master PIPES in Angular.
Pure pipe are a very useful way to transform data from your template. The difference between calling a function and a pipe is that pure pire are memoized. So they won't be recalculated every change detection cycle if the inputs hasn't changed.
-### Information:
+## Information:
In this first exercice, you add calling a simple function inside your template. The goal is to convert it to a pipe.
-### Constraints:
+## Constraints:
- must be strongly typed
diff --git a/docs/src/content/docs/challenges/angular/9-pipe-wrapFn.md b/docs/src/content/docs/challenges/angular/9-pipe-wrapFn.md
index c76b1aa..3146535 100644
--- a/docs/src/content/docs/challenges/angular/9-pipe-wrapFn.md
+++ b/docs/src/content/docs/challenges/angular/9-pipe-wrapFn.md
@@ -13,12 +13,12 @@ The goal of this serie of 3 pipe challenges is to master PIPES in Angular.
Pure pipe are a very useful way to transform data from your template. The difference between calling a function and a pipe is that pure pire are memoized. So they won't be recalculated every change detection cycle if the inputs hasn't changed.
-### Information:
+## Information:
In this second exercice, you are calling multiple functions inside your template. You can create a specific pipe for each of the functions but this will be too cumbersome.
The goal is to create a `wrapFn` pipe to wrap your callback function though a pipe. Your function MUST remain inside your component. `WrapFn` must be highly reusable.
-### Constraints:
+## Constraints:
- must be strongly typed
diff --git a/docs/src/content/docs/challenges/ngrx/2-effect-selector.md b/docs/src/content/docs/challenges/ngrx/2-effect-selector.md
index de13813..bfab92d 100644
--- a/docs/src/content/docs/challenges/ngrx/2-effect-selector.md
+++ b/docs/src/content/docs/challenges/ngrx/2-effect-selector.md
@@ -21,11 +21,9 @@ In NgRx, **selectors** is a very powerful tool often **misused**. You should use
## Statement
-##### You will have to
+You will have to Refactor this working example of a dashboard of activities.
-1. Refactor this working example of a dashboard of activities.
-
-##### Rules:
+## Contraints:
- Only **one action** should be dispatched from a component
- Status effect is useless. Using **combineLatest** should be a red flag. And Effect are made for side effect, not transforming data. That's a selector role
diff --git a/docs/src/content/docs/challenges/ngrx/7-power-effect.md b/docs/src/content/docs/challenges/ngrx/7-power-effect.md
index 0ddf9e8..3276414 100644
--- a/docs/src/content/docs/challenges/ngrx/7-power-effect.md
+++ b/docs/src/content/docs/challenges/ngrx/7-power-effect.md
@@ -9,7 +9,7 @@ WIP
Challenge #7
-### Information
+## Information
NgRx Effect is a very powerful library develop by the NgRx team. Effects subscribe to a HOT Observable and listen to any event dispatch from any place inside the application.
@@ -49,7 +49,7 @@ Your PR title must start with Answer:7.
❖ Community Answers
▶︎ Author Answer
diff --git a/docs/src/content/docs/challenges/nx/25-generator-lib-ext.md b/docs/src/content/docs/challenges/nx/25-generator-lib-ext.md
index 6d69732..aac1bd3 100644
--- a/docs/src/content/docs/challenges/nx/25-generator-lib-ext.md
+++ b/docs/src/content/docs/challenges/nx/25-generator-lib-ext.md
@@ -4,5 +4,69 @@ description: Challenge 25 is about creating a Nx generator to extend the built-i
---
:::note
-WIP: go [here](https://github.com/tomalaforge/angular-challenges/blob/main/libs/custom-plugin/src/generators/custom-library/README.md) if you want to do this challenge
+WIP
:::
+
+## Information
+
+Welcome to the marvelous world of Nx generators.
+
+Generators are awesome tools that can help you and your team generate code more quickly, especially for pieces of code that you use frequently. While using Nx, you create libraries regularly, but sometimes the default generator doesn't perfectly meet your needs.
+
+## Statement
+
+The goal of this challenge is to create a generator that extends the default library generator of Nx. You will need to override the default `jest.config.ts` and a `eslintrc.json` with a custom one.
+
+You can either use all the default parameters of the Nx library generator or choose to modify some and keep others as defaults. The choice is yours.
+
+## Constraints:
+
+You should only override the jest configuration is the `unitTestRunner` option is set at `JEST`, and you should only update the eslint configuration if the `linter` is set to `eslint`.
+
+---
+
+`jest.config.ts`
+
+```ts
+/* eslint-disable */
+export default {
+ displayName: '< libName >', // 👈 lib name
+ preset: '../../../jest.preset.js', // 👈 be careful with the path
+ setupFilesAfterEnv: ['/src/test-setup.ts'],
+ transform: {
+ '^.+\\.(ts|mjs|js|html)$': [
+ 'jest-preset-angular',
+ {
+ tsconfig: '/tsconfig.spec.json',
+ stringifyContentPathRegex: '\\.(html|svg)$',
+ },
+ ],
+ },
+ transformIgnorePatterns: ['node_modules/(?!(.*\\.mjs$|lodash-es))'],
+};
+```
+
+---
+
+`eslintrc.json`
+
+add this rule `"@typescript-eslint/member-ordering": "off"` inside the rules properties of ts files.
+
+---
+
+:::tip[Reminder]
+Your PR title must start with Answer:25.
+:::
+
+
diff --git a/docs/src/content/docs/challenges/nx/26-generator-comp.md b/docs/src/content/docs/challenges/nx/26-generator-comp.md
index a63ec95..0fe8263 100644
--- a/docs/src/content/docs/challenges/nx/26-generator-comp.md
+++ b/docs/src/content/docs/challenges/nx/26-generator-comp.md
@@ -4,5 +4,157 @@ description: Challenge 26 is about creating a Nx generator to create a custom co
---
:::note
-WIP: go [here](https://github.com/tomalaforge/angular-challenges/blob/main/libs/custom-plugin/src/generators/feature-component/README.md) if you want to do this challenge
+WIP
:::
+
+## Information
+
+Welcome to the marvelous world of Nx generators.
+
+Generators are awesome tools that can help you and your team generate code more quickly, especially for pieces of code that you use frequently. Inside an entreprise project, you often have to create components that look similar. And most of the time, you end up copy/pasting other components. In Nx, you can create this boilerplate in a simple command using generators.
+
+## Statement
+
+The goal of this challenge is to create a generator that will create all the boilerplate of a component for you.
+
+Just below, you will have the end result of your generator for a `UserComponent` associated with a `@ngrx/component-store`.
+
+## Options
+
+- name : name of your component/store/service
+- createService: flag to tell if a http service should be created
+ - yes : create as below
+ - no: don't create the inject/import/effect/function call (anything related to the service call)
+- inlineTemplate: flag to decide if template should be inline or in a separate file
+
+---
+
+`user.component.ts`
+
+```ts
+@Component({
+ selector: 'app-user',
+ standalone: true,
+ imports: [LetDirective],
+ providers: [provideComponentStore(UserStore)],
+ template: ` // do things `,
+ changeDetection: ChangeDetectionStrategy.OnPush,
+})
+export class UserComponent {
+ private userStore = inject(UserStore);
+
+ readonly vm$ = this.userStore.vm$;
+}
+```
+
+---
+
+`user.store.json`
+
+```ts
+import { Injectable, inject } from '@angular/core';
+import { ComponentStore, OnStateInit, OnStoreInit, tapResponse } from '@ngrx/component-store';
+import { mergeMap, pipe, tap } from 'rxjs';
+import { User } from './user.model';
+import { UserService } from './user.service';
+
+export interface UserState {
+ users: User[];
+ loading: boolean;
+ error?: string;
+}
+
+const initialState: UserState = {
+ users: [],
+ loading: false,
+ error: undefined,
+};
+
+@Injectable()
+export class UserStore extends ComponentStore implements OnStateInit, OnStoreInit {
+ private userService = inject(UserService);
+
+ private readonly users$ = this.select((state) => state.users);
+ private readonly loading$ = this.select((state) => state.loading);
+ private readonly error$ = this.select((state) => state.error);
+
+ readonly vm$ = this.select(
+ {
+ users: this.users$,
+ loading: this.loading$,
+ error: this.error$,
+ },
+ { debounce: true }
+ );
+
+ ngrxOnStateInit() {
+ this.setState(initialState);
+ }
+
+ ngrxOnStoreInit() {
+ this.loadUsers();
+ }
+
+ readonly loadUsers = this.effect(
+ pipe(
+ tap(() => this.patchState({ loading: true })),
+ mergeMap(() =>
+ this.userService.loadUsers().pipe(
+ tapResponse(
+ (users) => this.patchState({ users, loading: false }),
+ (err: string) => this.patchState({ error: err, loading: false })
+ )
+ )
+ )
+ )
+ );
+}
+```
+
+---
+
+`user.service.ts`
+
+```ts
+import { BASE_URL } from '@angular-challenges/fake-utils';
+import { HttpClient } from '@angular/common/http';
+import { Injectable, inject } from '@angular/core';
+import { User } from './user.model';
+
+@Injectable({ providedIn: 'root' })
+export class UserService {
+ private http = inject(HttpClient);
+ private BASE_URL = inject(BASE_URL);
+
+ loadUsers = () => this.http.get(`${this.BASE_URL}/users`);
+}
+```
+
+---
+
+`user.model.ts`
+
+```ts
+export interface User {
+ name: string;
+}
+```
+
+---
+
+:::tip[Reminder]
+Your PR title must start with Answer:26.
+:::
+
+
diff --git a/docs/src/content/docs/challenges/nx/27-forbid-enum-rule.md b/docs/src/content/docs/challenges/nx/27-forbid-enum-rule.md
index 4394d49..8891b5e 100644
--- a/docs/src/content/docs/challenges/nx/27-forbid-enum-rule.md
+++ b/docs/src/content/docs/challenges/nx/27-forbid-enum-rule.md
@@ -4,5 +4,38 @@ description: Challenge 27 is about creating a custom Eslint Rule to forbid enums
---
:::note
-WIP: go [here](https://github.com/tomalaforge/angular-challenges) if you want to do this challenge
+WIP
:::
+
+## Information
+
+Eslint is an amazing tool that helps developers avoid simple mistakes and adhere to company style guides.
+
+In this first example, we will create a rule that forbids the use of enums. The rule will suggest using string unions instead of enums whenever you add an enum to your code. It is a straightforward rule for learning how to create rules.
+
+You will also need to write tests to verify the rule's functionality.
+
+To test the rule inside your project, add `"@nrwl/nx/workspace/forbidden-enum": "error"` to the `eslintrc.json` file and attempt to insert an enum into any project to witness the magic. 😇
+
+To assist you with AST (Abstract Syntax Tree) definitions, you can visit the [AST explorer](https://astexplorer.net/) and use JavaScript, @typescript-eslint/parser, and Eslint-v8 as the transformation method. However, please note that you will only get the type information there. The transformation function may not work for TypeScript types since the editor is in JavaScript.
+
+You can also check this [repo](https://github.com/typescript-eslint/typescript-eslint/tree/master/packages/eslint-plugin/src/rules) for eslint rule examples.
+
+---
+
+:::tip[Reminder]
+Your PR title must start with Answer:27.
+:::
+
+
diff --git a/docs/src/content/docs/challenges/rxjs/11-bug-chaining-operator.md b/docs/src/content/docs/challenges/rxjs/11-bug-chaining-operator.md
index f75f57e..cdb6c3d 100644
--- a/docs/src/content/docs/challenges/rxjs/11-bug-chaining-operator.md
+++ b/docs/src/content/docs/challenges/rxjs/11-bug-chaining-operator.md
@@ -13,13 +13,13 @@ Let's dive inside the wonderful word of RxJs.
This challenge is inspired by a real-life example.
-### Presentation of the challenge
+## Presentation of the challenge
-#### User Story
+### User Story
We need a button for each `Topic`. When we click on it, we delete all objects with this `Topic` in our database _(Fake DB in our case)_. Finally we display **All [topic] have been deleted** is everything was deleted successfully or **Error: deletion of some [topic] failed** if some deletions failed
-#### Constraints:
+### Constraints:
We can only pass one object to our DB for deletion at the time. The DB will respond true if the data was successfully deleted and false otherwise.
diff --git a/docs/src/content/docs/challenges/rxjs/14-race-condition.md b/docs/src/content/docs/challenges/rxjs/14-race-condition.md
index e94e003..45944eb 100644
--- a/docs/src/content/docs/challenges/rxjs/14-race-condition.md
+++ b/docs/src/content/docs/challenges/rxjs/14-race-condition.md
@@ -9,20 +9,20 @@ WIP
Challenge #14
-### Information
+## Information
The goal of this application is to display a list of topics in a modal when a button is clicked. The application functions correctly. However, your tech lead has asked you to add tests and they are failing.
-### Statement
+## Statement
Correct your application to pass the test
-### Constraints:
+## Constraints:
- I can see you coming 🤣 => You CANNOT change the test (Test is working fine) 😳
- You CANNOT change the `fakeGetHttpTopic` method. A delay has been added to fake a slow network.
-### Run the test
+## Run the test
HEADLESS : `npx nx component-test rxjs-race-condition`
WATCH MODE : `npx nx component-test rxjs-race-condition --watch`
diff --git a/docs/src/content/docs/challenges/testing/17-router.md b/docs/src/content/docs/challenges/testing/17-router.md
index 1d24fc3..b2d4e10 100644
--- a/docs/src/content/docs/challenges/testing/17-router.md
+++ b/docs/src/content/docs/challenges/testing/17-router.md
@@ -9,7 +9,7 @@ WIP
Challenge #17
-### Information
+## Information
Testing is a crucial step in building scalable, maintainable, and trustworthy applications.
Testing should never be avoided, even in the face of short deadlines or strong pressure from the product team.
@@ -17,7 +17,7 @@ Nowadays, there are numerous awesome tools available that make it easy to test y
In this series of testing exercises, we will learn and master [Testing Library](https://testing-library.com/docs/) and [Cypress Component Testing](https://docs.cypress.io/guides/component-testing/angular/overview) that simplifies DOM manipulation for testing any Angular component.
-### Statement:
+## Statement:
We have a functional application that lists available books for borrowing inside a library. If the book you searched is available, you will be directed to the corresponding book(s), otherwise, you will end up on an error page.
diff --git a/docs/src/content/docs/challenges/testing/18-nested-comp.md b/docs/src/content/docs/challenges/testing/18-nested-comp.md
index 73ef4a9..9c33e4c 100644
--- a/docs/src/content/docs/challenges/testing/18-nested-comp.md
+++ b/docs/src/content/docs/challenges/testing/18-nested-comp.md
@@ -9,7 +9,7 @@ WIP
Challenge #18
-### Statement:
+## Statement:
We have a small application that send a title to a fake backend that you type inside a input.
If the title is correctly typed, you can send the request otherwise you get a nice error and the request is not sent.
diff --git a/docs/src/content/docs/challenges/testing/19-input-output.md b/docs/src/content/docs/challenges/testing/19-input-output.md
index 7e0bc72..8d725a8 100644
--- a/docs/src/content/docs/challenges/testing/19-input-output.md
+++ b/docs/src/content/docs/challenges/testing/19-input-output.md
@@ -9,7 +9,7 @@ WIP
Challenge #19
-### Statement:
+## Statement:
We have a small counter application that increment or decrement a number.
You can play with it by running : `npx nx serve testing-input-output`.
diff --git a/docs/src/content/docs/challenges/testing/20-modal.md b/docs/src/content/docs/challenges/testing/20-modal.md
index 367f854..b5761e0 100644
--- a/docs/src/content/docs/challenges/testing/20-modal.md
+++ b/docs/src/content/docs/challenges/testing/20-modal.md
@@ -9,7 +9,7 @@ WIP
Challenge #20
-### Statement:
+## Statement:
The goal of this challenge is to test dialogs inside your application.
Within this program, you will get an error modal if the user doesn't input a name, while a confirmation modal will appear in all other cases.
diff --git a/docs/src/content/docs/challenges/testing/23-harness.md b/docs/src/content/docs/challenges/testing/23-harness.md
index 8ae3b22..b2ba288 100644
--- a/docs/src/content/docs/challenges/testing/23-harness.md
+++ b/docs/src/content/docs/challenges/testing/23-harness.md
@@ -9,7 +9,7 @@ WIP
Challenge #23
-### Statement:
+## Statement:
The objective of this challenge is to have a better understanding of the CDK test harness API. In this initial challenge, we will only use Angular Material's built-in harnesses.
diff --git a/docs/src/content/docs/challenges/testing/24-harness-creation.md b/docs/src/content/docs/challenges/testing/24-harness-creation.md
index 8122826..f93d4f2 100644
--- a/docs/src/content/docs/challenges/testing/24-harness-creation.md
+++ b/docs/src/content/docs/challenges/testing/24-harness-creation.md
@@ -9,7 +9,7 @@ WIP
Challenge #24
-### Information
+## Information
The goal of this challenge is to create a test harness for `slider.component.ts`. The harness file, `slider.harness.ts`, has already been created.
diff --git a/docs/src/content/docs/challenges/testing/28-checkbox.md b/docs/src/content/docs/challenges/testing/28-checkbox.md
index 0980b49..655ba4d 100644
--- a/docs/src/content/docs/challenges/testing/28-checkbox.md
+++ b/docs/src/content/docs/challenges/testing/28-checkbox.md
@@ -9,7 +9,7 @@ WIP
Challenge #28
-### Information
+## Information
This is the perfect example to get started with `Testing Library`.
diff --git a/docs/src/content/docs/challenges/testing/29-real-application.md b/docs/src/content/docs/challenges/testing/29-real-application.md
index 43cc356..03fef55 100644
--- a/docs/src/content/docs/challenges/testing/29-real-application.md
+++ b/docs/src/content/docs/challenges/testing/29-real-application.md
@@ -9,7 +9,7 @@ WIP
Challenge #29
-### Statement:
+## Statement:
I built this more real life application to create more real life test cases.
In this application, you can search for tickets, you can assign or finish them. You can also create new tickets.
diff --git a/docs/src/content/docs/challenges/typescript/15-overload-fn.md b/docs/src/content/docs/challenges/typescript/15-overload-fn.md
index f0b32e2..f04e921 100644
--- a/docs/src/content/docs/challenges/typescript/15-overload-fn.md
+++ b/docs/src/content/docs/challenges/typescript/15-overload-fn.md
@@ -9,7 +9,7 @@ WIP
Challenge #15
-### Information
+## Information
Angular uses TypeScript, and mastering TypeScript can help you avoid runtime errors by catching them at compile time.
@@ -19,7 +19,7 @@ One solution would be to create a separate function for each vehicle type, but f
To achieve this, we will use overload functions.
-### Statement
+## Statement
- Use function overload
diff --git a/example.README.md b/example.README.md
index e0fbfee..070c246 100644
--- a/example.README.md
+++ b/example.README.md
@@ -7,9 +7,9 @@
-### Information
+## Information
-### Statement
+## Statement
### Step 1
diff --git a/libs/custom-plugin/src/generators/custom-library/README.md b/libs/custom-plugin/src/generators/custom-library/README.md
index 71a12bb..383c4e2 100644
--- a/libs/custom-plugin/src/generators/custom-library/README.md
+++ b/libs/custom-plugin/src/generators/custom-library/README.md
@@ -1,64 +1,7 @@
-Create a generator to extend @nx/angular-lib
+# Extend Lib Generator
> Author: Thomas Laforge
-### Information
+### Documentation and Instruction
-Welcome to the marvelous world of Nx generators.
-
-Generators are awesome tools that can help you and your team generate code more quickly, especially for pieces of code that you use frequently. While using Nx, you create libraries regularly, but sometimes the default generator doesn't perfectly meet your needs.
-
-### Statement
-
-The goal of this challenge is to create a generator that extends the default library generator of Nx. You will need to override the default `jest.config.ts` and a `eslintrc.json` with a custom one.
-
-You can either use all the default parameters of the Nx library generator or choose to modify some and keep others as defaults. The choice is yours.
-
-### Constraints:
-
-You should only override the jest configuration is the `unitTestRunner` option is set at `JEST`, and you should only update the eslint configuration if the `linter` is set to `eslint`.
-
----
-
-`jest.config.ts`
-
-```ts
-/* eslint-disable */
-export default {
- displayName: '< libName >', // 👈 lib name
- preset: '../../../jest.preset.js', // 👈 be careful with the path
- setupFilesAfterEnv: ['/src/test-setup.ts'],
- transform: {
- '^.+\\.(ts|mjs|js|html)$': [
- 'jest-preset-angular',
- {
- tsconfig: '/tsconfig.spec.json',
- stringifyContentPathRegex: '\\.(html|svg)$',
- },
- ],
- },
- transformIgnorePatterns: ['node_modules/(?!(.*\\.mjs$|lodash-es))'],
-};
-```
-
----
-
-`eslintrc.json`
-
-add this rule `"@typescript-eslint/member-ordering": "off"` inside the rules properties of ts files.
-
-### Submitting your work
-
-1. Fork the project
-2. clone it
-3. npm ci
-4. _...work on it_
-5. Commit your work
-6. Submit a PR with a title beginning with **Answer:25** that I will review and other dev can review.
-
-
-
-
-
-
-_You can ask any question on_
+Challenge documentation is [here](https://angular-challenges.vercel.app/challenges/nx/25-generator-lib-ext.md/).
diff --git a/libs/custom-plugin/src/generators/feature-component/README.md b/libs/custom-plugin/src/generators/feature-component/README.md
index 23363ca..b78bda4 100644
--- a/libs/custom-plugin/src/generators/feature-component/README.md
+++ b/libs/custom-plugin/src/generators/feature-component/README.md
@@ -1,152 +1,7 @@
-Create a generator for a custom component
+# Component Generator
> Author: Thomas Laforge
-### Information
+### Documentation and Instruction
-Welcome to the marvelous world of Nx generators.
-
-Generators are awesome tools that can help you and your team generate code more quickly, especially for pieces of code that you use frequently. Inside an entreprise project, you often have to create components that look similar. And most of the time, you end up copy/pasting other components. In Nx, you can create this boilerplate in a simple command using generators.
-
-### Statement
-
-The goal of this challenge is to create a generator that will create all the boilerplate of a component for you.
-
-Just below, you will have the end result of your generator for a `UserComponent` associated with a `@ngrx/component-store`.
-
-#### Options
-
-- name : name of your component/store/service
-- createService: flag to tell if a http service should be created
- - yes : create as below
- - no: don't create the inject/import/effect/function call (anything related to the service call)
-- inlineTemplate: flag to decide if template should be inline or in a separate file
-
----
-
-`user.component.ts`
-
-```ts
-@Component({
- selector: 'app-user',
- standalone: true,
- imports: [LetDirective],
- providers: [provideComponentStore(UserStore)],
- template: ` // do things `,
- changeDetection: ChangeDetectionStrategy.OnPush,
-})
-export class UserComponent {
- private userStore = inject(UserStore);
-
- readonly vm$ = this.userStore.vm$;
-}
-```
-
----
-
-`user.store.json`
-
-```ts
-import { Injectable, inject } from '@angular/core';
-import { ComponentStore, OnStateInit, OnStoreInit, tapResponse } from '@ngrx/component-store';
-import { mergeMap, pipe, tap } from 'rxjs';
-import { User } from './user.model';
-import { UserService } from './user.service';
-
-export interface UserState {
- users: User[];
- loading: boolean;
- error?: string;
-}
-
-const initialState: UserState = {
- users: [],
- loading: false,
- error: undefined,
-};
-
-@Injectable()
-export class UserStore extends ComponentStore implements OnStateInit, OnStoreInit {
- private userService = inject(UserService);
-
- private readonly users$ = this.select((state) => state.users);
- private readonly loading$ = this.select((state) => state.loading);
- private readonly error$ = this.select((state) => state.error);
-
- readonly vm$ = this.select(
- {
- users: this.users$,
- loading: this.loading$,
- error: this.error$,
- },
- { debounce: true }
- );
-
- ngrxOnStateInit() {
- this.setState(initialState);
- }
-
- ngrxOnStoreInit() {
- this.loadUsers();
- }
-
- readonly loadUsers = this.effect(
- pipe(
- tap(() => this.patchState({ loading: true })),
- mergeMap(() =>
- this.userService.loadUsers().pipe(
- tapResponse(
- (users) => this.patchState({ users, loading: false }),
- (err: string) => this.patchState({ error: err, loading: false })
- )
- )
- )
- )
- );
-}
-```
-
----
-
-`user.service.ts`
-
-```ts
-import { BASE_URL } from '@angular-challenges/fake-utils';
-import { HttpClient } from '@angular/common/http';
-import { Injectable, inject } from '@angular/core';
-import { User } from './user.model';
-
-@Injectable({ providedIn: 'root' })
-export class UserService {
- private http = inject(HttpClient);
- private BASE_URL = inject(BASE_URL);
-
- loadUsers = () => this.http.get(`${this.BASE_URL}/users`);
-}
-```
-
----
-
-`user.model.ts`
-
-```ts
-export interface User {
- name: string;
-}
-```
-
-### Submitting your work
-
-1. Fork the project
-2. clone it
-3. npm ci
-4. _...work on it_
-5. Commit your work
-6. Submit a PR with a title beginning with **Answer:26** that I will review and other dev can review.
-
-
-
-
-
-
-_You can ask any question on_
+Challenge documentation is [here](https://angular-challenges.vercel.app/challenges/nx/26-generator-comp.md/).
diff --git a/tools/eslint-rules/rules/forbidden-enum.README.md b/tools/eslint-rules/rules/forbidden-enum.README.md
index 766e292..4ccf234 100644
--- a/tools/eslint-rules/rules/forbidden-enum.README.md
+++ b/tools/eslint-rules/rules/forbidden-enum.README.md
@@ -1,34 +1,13 @@
-Create a eslint rule to forbid enums
+# Custom Eslint Rule
> Author: Thomas Laforge
-### Information
+### Run Application
-Eslint is an amazing tool that helps developers avoid simple mistakes and adhere to company style guides.
+```bash
+npx nx serve eslint-rules
+```
-In this first example, we will create a rule that forbids the use of enums. The rule will suggest using string unions instead of enums whenever you add an enum to your code. It is a straightforward rule for learning how to create rules.
+### Documentation and Instruction
-You will also need to write tests to verify the rule's functionality.
-
-To test the rule inside your project, add `"@nrwl/nx/workspace/forbidden-enum": "error"` to the `eslintrc.json` file and attempt to insert an enum into any project to witness the magic. 😇
-
-To assist you with AST (Abstract Syntax Tree) definitions, you can visit the [AST explorer](https://astexplorer.net/) and use JavaScript, @typescript-eslint/parser, and Eslint-v8 as the transformation method. However, please note that you will only get the type information there. The transformation function may not work for TypeScript types since the editor is in JavaScript.
-
-You can also check this [repo](https://github.com/typescript-eslint/typescript-eslint/tree/master/packages/eslint-plugin/src/rules) for eslint rule examples.
-
-### Submitting your work
-
-1. Fork the project
-2. clone it
-3. npm ci
-4. _...work on it_
-5. `npx nx test eslint-rules`
-6. Commit your work
-7. Submit a PR with a title beginning with **Answer:27** that I will review and other dev can review.
-
-
-
-
-
-
-_You can ask any question on_
+Challenge documentation is [here](https://angular-challenges.vercel.app/challenges/nx/27-forbid-enum-rule.md/).