Skip to content

Commit

Permalink
fix: many grammar tweaks
Browse files Browse the repository at this point in the history
Fix some typos.
Follow some non-obvious English punctuation rules.
remove duplicate md files for 50.
Add some links for Angular Testing Library
Remove inconsistent colons after "Constraints" headers
Fix a broken link on 9.
Chrome's tab is "Elements", not "source"
Add missing challenges to the performance page
Similar changes
  • Loading branch information
LMFinney committed May 22, 2024
1 parent 61fd418 commit d8ecfac
Show file tree
Hide file tree
Showing 40 changed files with 112 additions and 116 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,13 @@ sidebar:

## Information

This is the third of three `@Pipe()` challenges, the goal of this series is to master **pipes** in Angular.
This is the third of three `@Pipe()` challenges. The goal of this series is to master **pipes** in Angular.

Pipes are a very powerful way to transform data in your template. The difference between calling a function and a pipe is that pure pipes are memoized. So they won't be recalculated every change detection cycle if their inputs haven't changed.
Pipes are a very powerful way to transform data in your template. The difference between calling a function and a pipe is that pure pipes are memoized. So, they won't be recalculated every change detection cycle if their inputs haven't changed.

Pipes are designed to be efficient and optimized for performance. They use change detection mechanisms to only recalculate the value if the input changes, to minimize unnecessary calculations and improving rendering performance.
Pipes are designed to be efficient and optimized for performance. They use change detection mechanisms to only recalculate the value if the input changes, to minimize unnecessary calculations and improve rendering performance.

By default a pipe is pure, you should be aware that setting `pure` to false is prone to be inefficient, because it increases the amount of rerenders.
By default, a pipe is pure. You should be aware that setting `pure` to false is prone to be inefficient, because it increases the amount of rerenders.

:::note
A **pure** pipe is only called when the value changes.\
Expand All @@ -31,7 +31,7 @@ There are some useful predefined pipes like the DatePipe, UpperCasePipe and Curr

## Statement

In this exercise, you want to access utils functions. Currently you 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.
In this exercise, you want to access utils functions. Currently, you 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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ sidebar:

## Information

Styling is an important aspect of a frontend developer's day job, but it is often underestimated. In Angular applications, I frequently see people using `@Input()` to customize the style of their components. However, `@Input()` should only be used for logic. Other techniques, such as **CSS variables** and **host-context** should be used for styling.
Styling is an important aspect of a frontend developer's day job, but it is often underestimated. In Angular applications, I frequently see people using `@Input()` to customize the style of their components. However, `@Input()` should only be used for logic. Other techniques, such as **CSS variables** and **host-context**, should be used for styling.

In this challenge, you will need to use both CSS variables and `:host-context` to remove all `@Input()` from your code.

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
title: 🟢 @RouterInput()
description: Challenge 22 is about using the @Input decorator to retreive router params.
description: Challenge 22 is about using the @Input decorator to retrieve router params.
author: thomas-laforge
contributors:
- tomalaforge
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,15 @@ This exercise can feel obsolete with the new control flow and the empty block in

## Information

Directive is a very powerful tool only offered by the Angular framework. You can apply the DRY principle by having shared logic inside a directive and applying it to any component you want.
Directives are a very powerful tool only offered by the Angular framework. You can apply the DRY principle by having shared logic inside a directive and applying it to any component you want.

But the real power is that you can enhance an already existing directive which moreover doesn't **belong** to you.
But the real power is that you can enhance an already-existing directive, which moreover doesn't **belong** to you.

## Statement

In this exercise, we have a want to display a list of persons. If the list is empty, you must display _" the list is empty !! "_.

Currently we have:
Currently, we have:

```typescript
<ng-container *ngIf="persons.length > 0; else emptyList">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ This challenge is inspired by a real-life example that I simplified to create th

## 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.
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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ sidebar:

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 demonstrate a useful concept.

The behavior of the component (referred to as the _brain_ in the Spartan stack) is located in the brain library. The styling part (referred to as the _helmet_) is located inside the helmet library. Both libraries cannot depend on each other because we want to be able to publish them separately. To help us address the issue, we are using the Nx enforce eslint rule. You can find more details [here](https://nx.dev/core-features/enforce-module-boundaries).
The behavior of the component (referred to as the _brain_ in the Spartan stack) is located in the brain library. The styling part (referred to as the _helmet_) is located inside the helmet library. Both libraries cannot depend on each other because we want to be able to publish them separately. To help us address the issue, we are using the Nx `enforce-module-boundaries` eslint rule. You can find more details [here](https://nx.dev/core-features/enforce-module-boundaries).

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`**.
However, the button's helmet needs to access the state of the component to style the button differently based on its state. As mentioned 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

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
title: 🟠 InjectionToken
description: Challenge 39 is about learning the power of dependancy injection
description: Challenge 39 is about learning the power of dependency injection
author: thomas-laforge
contributors:
- tomalaforge
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ sidebar:

You can improve template type checking for custom directives by adding template guard properties to your directive definition. Angular offers the static function [`ngTemplateContextGuard`](https://angular.dev/guide/directives/structural-directives#improving-template-type-checking-for-custom-directives) to strongly type structural directives.

However the context of **NgTemplateOutlet** type is **Object**. But with the help of the above guard, we can improve that behavior.
However, the context of **NgTemplateOutlet** type is **Object**. But with the help of the above guard, we can improve that behavior.

## Statement

Expand All @@ -28,17 +28,17 @@ This exercise has two levels of complexity.

### Level 1: Known Interface

Currently we have the following piece of code.
Currently, we have the following piece of code.

![Unkown Person](../../../../assets/4/unknown-person.png 'Unkown Person')
![Unknown Person](../../../../assets/4/unknown-person.png 'Unknown Person')

As we can see, `name` is of type `any`. We want to infer the correct type using the custom directive `PersonDirective`.

### Level 2: Generic Interface

Currently we have the following piece of code.
Currently, we have the following piece of code.

![Unkown Student](../../../../assets/4/unknown-student.png 'Unkown Student')
![Unknown Student](../../../../assets/4/unknown-student.png 'Unknown Student')

As we can see, `student` is of type `any`. We want to infer the correct type using the custom directive `ListDirective`.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ sidebar:

## Information

This is the second of two animation challenges, the goal of this series is to master animations in Angular.
This is the second of two animation challenges. The goal of this series is to master animations in Angular.

The View Transition API is a brand new API that provides a set of features that allow developers to control and manipulate the transitions and animations between views within an application.
The View Transition API is a brand-new API that provides a set of features that allow developers to control and manipulate the transitions and animations between views within an application.
It plays a pivotal role in enhancing the user experience (UX), bringing applications to life with engaging and captivating transitions to guide users through different pages or sections of the app.

The goal of this challenge is to learn about and manipulate all types of transitions proposed by the API.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ npm i --save-dev @types/react @types/react-dom

<details>
<summary>Hint 2 - Initialization</summary>
Create a react root with `createRoot(...)`
Create a React root with `createRoot(...)`
</details>

<details>
Expand All @@ -76,5 +76,5 @@ npm i --save-dev @types/react @types/react-dom

<details>
<summary>Hint 4 - Design</summary>
Do not forget to allow the react file in Tailwind.
Do not forget to allow the React file in Tailwind.
</details>
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ sidebar:

## Information

This is the first of two animation challenges, the goal of this series is to master animations in Angular.
This is the first of two animation challenges. The goal of this series is to master animations in Angular.

Well-designed animations can make your application more fun and straightforward to use, but they aren't just cosmetic. Animations can improve your application and user experience in a number of ways:

Expand All @@ -22,7 +22,7 @@ Well-designed animations can make your application more fun and straightforward

I would recommend you read the [official documentation](https://angular.dev/guide/animations). You will learn everything that is necessary to successfully complete the challenge.

Otherwise look at this [working example](https://svenson95.github.io/ng-xmp-animations/) and [git repo](https://github.com/svenson95/ng-xmp-animations) to get inspired.
Otherwise, look at this [working example](https://svenson95.github.io/ng-xmp-animations/) and [git repo](https://github.com/svenson95/ng-xmp-animations) to get inspired.

## Statement

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ Communicating and having a global/local state in sync with your backend is the h

In this exercise, you have a small CRUD application, which get a list of TODOS, update and delete some todos.

Currently we have a working example but filled with lots of bad practices.
Currently, we have a working example but filled with lots of bad practices.

### Step 1: refactor with best practices

Expand Down
21 changes: 0 additions & 21 deletions docs/src/content/docs/challenges/angular/50-bug-effect-signal.md

This file was deleted.

8 changes: 4 additions & 4 deletions docs/src/content/docs/challenges/angular/8-pure-pipe.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,13 @@ sidebar:

## Information

This is the first of three `@Pipe()` challenges, the goal of this series is to master **pipes** in Angular.
This is the first of three `@Pipe()` challenges. The goal of this series is to master **pipes** in Angular.

Pipes are a very powerful way to transform data in your template. The difference between calling a function and a pipe is that pure pipes are memoized. So they won't be recalculated every change detection cycle if their inputs haven't changed.
Pipes are a very powerful way to transform data in your template. The difference between calling a function and a pipe is that pure pipes are memoized. So, they won't be recalculated every change detection cycle if their inputs haven't changed.

Pipes are designed to be efficient and optimized for performance. They use change detection mechanisms to only recalculate the value if the input changes, to minimize unnecessary calculations and improving rendering performance.
Pipes are designed to be efficient and optimized for performance. They use change detection mechanisms to only recalculate the value if the input changes, to minimize unnecessary calculations and improve rendering performance.

By default a pipe is pure, you should be aware that setting `pure` to false is prone to be inefficient, because it increases the amount of rerenders.
By default, a pipe is pure. You should be aware that setting `pure` to false is prone to be inefficient, because it increases the amount of rerenders.

:::note
A **pure** pipe is only called when the value changes.\
Expand Down
14 changes: 7 additions & 7 deletions docs/src/content/docs/challenges/angular/9-wrap-function-pipe.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,26 +16,26 @@ sidebar:

## Information

This is the second of three `@Pipe()` challenges, the goal of this series is to master **pipes** in Angular.
This is the second of three `@Pipe()` challenges. The goal of this series is to master **pipes** in Angular.

Pipes are a very powerful way to transform data in your template. The difference between calling a function and a pipe is that pure pipes are memoized. So they won't be recalculated every change detection cycle if their inputs haven't changed.
Pipes are a very powerful way to transform data in your template. The difference between calling a function and a pipe is that pure pipes are memoized. So, they won't be recalculated every change detection cycle if their inputs haven't changed.

Pipes are designed to be efficient and optimized for performance. They use change detection mechanisms to only recalculate the value if the input changes, to minimize unnecessary calculations and improving rendering performance.
Pipes are designed to be efficient and optimized for performance. They use change detection mechanisms to only recalculate the value if the input changes, to minimize unnecessary calculations and improve rendering performance.

By default a pipe is pure, you should be aware that setting `pure` to false is prone to be inefficient, because it increases the amount of rerenders.
By default, a pipe is pure. You should be aware that setting `pure` to false is prone to be inefficient, because it increases the amount of rerenders.

:::note
A **pure** pipe is only called when the value changes.\
A **impure** pipe is called every change detection cycle.
:::

There are some useful predefined pipes like the DatePipe, UpperCasePipe and CurrencyPipe. To learn more about pipes in Angular, check the API documentation [here]https://angular.dev/guide/pipes).
There are some useful predefined pipes like the DatePipe, UpperCasePipe and CurrencyPipe. To learn more about pipes in Angular, check the API documentation [here](https://angular.dev/guide/pipes).

## Statement

In this exercise, 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.
In this exercise, 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 through a pipe. Your function MUST remain inside your component. **`WrapFn` must be highly reusable.**

## Constraints:
## Constraints

- Must be strongly typed
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ Here's the feature expressed as a user story with a functional expectation:
## Acceptance Criteria

1. If one of the form fields is not empty and the user tries to navigate to a different route or page, or wants to reload the page, show an alert dialog to _avoid losing form data_.

2. The content of `dialog.component.ts` must be used for your alert.
3. The appearance and behavior of the alert dialog box must comply with W3C conventions, see [alert dialog pattern](https://www.w3.org/WAI/ARIA/apg/patterns/alertdialog/).
4. Maximize the use of the new concepts and syntax in the latest version of Angular.
Expand Down
18 changes: 9 additions & 9 deletions docs/src/content/docs/challenges/ngrx/2-effect-selector.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,22 +14,22 @@ sidebar:
order: 113
---

For this exercise, you will have a dashboard of activities displaying the name, the main teacher and a list of subtitutes.
For this exercise, you will have a dashboard of activities displaying the name, the main teacher and a list of possible substitutes.

## Information

In NgRx, **selectors** is a very powerful tool often **misused**. You should use them as soon as you need to transform an already existing data in the store.
In NgRx, **selectors** is a very powerful tool that is often **misused**. You should use them as soon as you need to transform an already existing data in the store.

- You shouldn't store **derived state**. This is error prone because when your data changes, you will have to change it at multiple places => you should have only one place of truth with that data, and every transformation should be done in a **selector**.
- You shouldn't store **derived state**. This is error-prone because when your data changes, you will have to change it at multiple places => you should have only one place of truth with that data, and every transformation should be done in a **selector**.

- Inside a component, you shouldn't transform a selector (using map operator), or you shouldn't have to call a selector from a function in your view. The useful data for a component should be done in a **selector**.
- Inside a component, you shouldn't transform a selector (using the map operator), and you shouldn't have to call a selector from a function in your view. The useful logic for preparing data for a component should be done in a **selector**.

## Statement

You will have to Refactor this working example of a dashboard of activities.
You will have to refactor this working example of a dashboard of activities.

## Contraints:
## Constraints

- 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
- Status state might not be useful, it's only a **derived state** of existing state.
- Only **one action** should be dispatched from a component (or none, if you can solve the problem with Effect lifecycle hooks).
- Status effect is useless. Using **combineLatest** should be a red flag. Effects are made for side effects, not for transforming data. That's a selector's role.
- Status state might not be useful; it's only a **derived state** of existing state.
Loading

0 comments on commit d8ecfac

Please sign in to comment.