Skip to content

Update docs #14

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Aug 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions docs/1.architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ We'll dig into these in more detail in later documents.

> [!NOTE]
>
> Think of **smart components** as more involved components that have access and awareness of the broader application state and logic, via stores (and other services). They don't function just as black boxes and are not usually reusable across different parts of the app.
> Think of **smart components** as more involved components that have access and awareness of the broader application state and logic, via stores and other services. They don't function just as black boxes and are not usually reusable across different parts of the app.
>
> Think of **presentational components** as simple and naive components that only know their inputs and outputs, making no assumptions of the overall application state and structure. They should be easy to test (as a black box) and easy to reuse.

Expand All @@ -67,7 +67,7 @@ We'll dig into these in more detail in later documents.
>
> As a reminder, all the Angular components (including ones generated through the Angular CLI) have been configured to use the [`OnPush` change detection strategy](https://angular.dev/best-practices/skipping-subtrees#using-onpush) by default.
>
> This is a more performant approach that [works well with Angular's signals](https://angular.dev/guide/signals#reading-signals-in-onpush-components), and since we use NgRx SignalStore you are unlikely to hit the cases where change detection is not triggered when it should be.
> This is a more performant approach that [works well with Angular's signals](https://angular.dev/guide/signals#reading-signals-in-onpush-components), and since we use NgRx SignalStore and Angular's signals to manage most application state you are unlikely to hit the cases where change detection is not triggered when it should be.
>
> With the caveat that forms _sometimes_ don't behave well with OnPush change detection, so in rare cases you'd need to use the `ChangeDetectorRef` to manually mark a component for change detection.
>
Expand Down Expand Up @@ -96,7 +96,7 @@ sequenceDiagram
(Ignore the time-ordering of this sequence diagram, it's just a way to visualize the data flow — it's not a strict sequence of events.)

- Use Angular services to wrap ALL access to databases and external services.
- Use state management "stores" to encapsulate as much of the app's state and behavior as possible, leaving components to focus on UI needs and responding to state changes.
- Use state management "stores" to encapsulate as much of the app's state and behavior as possible, leaving components to focus on UI needs, triggering store behaviors and responding to state changes.
- Use smart components to interact with stores to bind state and trigger application logic.
- Use presentational components (within the template of smart components) to abstract out UI presentation and logic in a way that does not need to know about the overall application state and structure, communicating all actions/events back to the parent smart component.

Expand Down
4 changes: 2 additions & 2 deletions docs/2.routes-and-shell.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

The base template comes with a `website` feature folder (within the `app`) where the static pages live. We could've added more pages and components here to build our logbook app, but it's better to separate it out into a dedicated feature folder ([`app/src/app/logbook/`](../app/src/app/logbook/)) and lazily load it only when the user navigates to a particular URL — `/logbook` in this case — as registered in the top-level app routes file:

<https://github.com/FullStacksDev/angular-and-firebase-simple-example-app/blob/f283f8f73e5d318c08d85e226a85a56f9df03d8e/app/src/app/app.routes.ts#L8-L11>
<https://github.com/FullStacksDev/angular-and-firebase-simple-example-app/blob/36ebc48fc9ef3f21f761ed6f60b83d2c30985fc1/app/src/app/app.routes.ts#L8-L11>

Here, the use of an `import` for the `loadChildren` property tells Angular to separate out the code for the logbook feature into its own bundle and only load it when the user navigates to `/logbook`.

Expand All @@ -16,7 +16,7 @@ Here, the use of an `import` for the `loadChildren` property tells Angular to se

Let's now look at the routes for the logbook feature itself:

<https://github.com/FullStacksDev/angular-and-firebase-simple-example-app/blob/f283f8f73e5d318c08d85e226a85a56f9df03d8e/app/src/app/logbook/logbook.routes.ts#L9-L25>
<https://github.com/FullStacksDev/angular-and-firebase-simple-example-app/blob/36ebc48fc9ef3f21f761ed6f60b83d2c30985fc1/app/src/app/logbook/logbook.routes.ts#L9-L25>

- We define a parent route that will load the `LogbookShellComponent`, with child routes defined within.
- This shell component has a `<router-outlet>` in its template where a matching child route will have it's component placed in to.
Expand Down
14 changes: 7 additions & 7 deletions docs/3.data-model-and-access.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ erDiagram

Given the design decision to store all entries in a single collection, we first set up the security rules to ensure proper access control:

<https://github.com/FullStacksDev/angular-and-firebase-simple-example-app/blob/f283f8f73e5d318c08d85e226a85a56f9df03d8e/firebase/firestore.rules#L4-L19>
<https://github.com/FullStacksDev/angular-and-firebase-simple-example-app/blob/36ebc48fc9ef3f21f761ed6f60b83d2c30985fc1/firebase/firestore.rules#L4-L19>

- This is a special "domain specific language" (DSL) that Firestore uses to define access control rules in the [`firebase/firestore.rules`](../firebase/firestore.rules) file ([docs](https://firebase.google.com/docs/firestore/security/get-started)).
- `isAuthed()` and `matchesAuthedUser(userId)` are helper functions we've defined to allow easy reuse in multiple rules.
Expand Down Expand Up @@ -104,7 +104,7 @@ Both Angular and the Firebase JavaScript SDK have first class support for TypeSc

When loading entries from Firestore we want to assume they take a particular _shape_ — the `EntryDoc` type — which the TypeScript (and VS Code) tooling can then use to look for type errors and provide code completion.

<https://github.com/FullStacksDev/angular-and-firebase-simple-example-app/blob/f283f8f73e5d318c08d85e226a85a56f9df03d8e/app/src/app/shared/models.ts#L15-L23>
<https://github.com/FullStacksDev/angular-and-firebase-simple-example-app/blob/36ebc48fc9ef3f21f761ed6f60b83d2c30985fc1/app/src/app/shared/models.ts#L15-L23>

- This uses the `Readonly` TypeScript utility type to mark the whole object as readonly.
- We build on the `WithId` type from the [`firebase/common/models.ts`](../firebase/common/models.ts) file, which adds the `id` field to the object.
Expand All @@ -122,7 +122,7 @@ When loading entries from Firestore we want to assume they take a particular _sh

In the same file we also define a special `NewOrUpdatedEntryInput` type for the data we send to Firestore when creating or updating an entry:

<https://github.com/FullStacksDev/angular-and-firebase-simple-example-app/blob/f283f8f73e5d318c08d85e226a85a56f9df03d8e/app/src/app/shared/models.ts#L25>
<https://github.com/FullStacksDev/angular-and-firebase-simple-example-app/blob/36ebc48fc9ef3f21f761ed6f60b83d2c30985fc1/app/src/app/shared/models.ts#L25>

Here we're picking a subset of the fields from the `EntryDoc` type — we only want to allow the user to set or update these fields.

Expand Down Expand Up @@ -150,11 +150,11 @@ Let's walk through this service:

First, we inject the Firestore client using the helper provided from the base template:

<https://github.com/FullStacksDev/angular-and-firebase-simple-example-app/blob/f283f8f73e5d318c08d85e226a85a56f9df03d8e/app/src/app/logbook/data/db/entries.service.ts#L29>
<https://github.com/FullStacksDev/angular-and-firebase-simple-example-app/blob/36ebc48fc9ef3f21f761ed6f60b83d2c30985fc1/app/src/app/logbook/data/db/entries.service.ts#L29>

Next, we define a special converter object that the Firestore JavaScript library understands. We also define a reference to the collection (which we use in the actual data access methods):

<https://github.com/FullStacksDev/angular-and-firebase-simple-example-app/blob/f283f8f73e5d318c08d85e226a85a56f9df03d8e/app/src/app/logbook/data/db/entries.service.ts#L31-L51>
<https://github.com/FullStacksDev/angular-and-firebase-simple-example-app/blob/36ebc48fc9ef3f21f761ed6f60b83d2c30985fc1/app/src/app/logbook/data/db/entries.service.ts#L31-L51>

[Firestore converters](https://firebase.google.com/docs/firestore/query-data/get-data#custom_objects) are a first class way to convert Firestore document data into strongly typed objects, and back. A converter is an object that conforms to the `FirestoreDataConverter` type, which requires two methods:

Expand Down Expand Up @@ -299,13 +299,13 @@ Because Realtime Database doesn't natively support arrays we have to model the c

We then have some very basic security rules to allow anyone to read but no one to write (from the client-side):

<https://github.com/FullStacksDev/angular-and-firebase-simple-example-app/blob/f283f8f73e5d318c08d85e226a85a56f9df03d8e/firebase/database.rules.json#L1-L8>
<https://github.com/FullStacksDev/angular-and-firebase-simple-example-app/blob/36ebc48fc9ef3f21f761ed6f60b83d2c30985fc1/firebase/database.rules.json#L1-L8>

We add some tests in [`firebase/test/rtdb/rtdb-rules.spec.ts`](../firebase/test/rtdb/rtdb-rules.spec.ts) to ensure that these work as expected.

Finally, we have a service that wraps access to this config object (and thus the underlying categories): [`ConfigService`](../app/src/app/logbook/data/db/config.service.ts). The key line in this service is where we flatten the `categories` object into an array of strings:

<https://github.com/FullStacksDev/angular-and-firebase-simple-example-app/blob/f283f8f73e5d318c08d85e226a85a56f9df03d8e/app/src/app/logbook/data/db/config.service.ts#L29-L32>
<https://github.com/FullStacksDev/angular-and-firebase-simple-example-app/blob/36ebc48fc9ef3f21f761ed6f60b83d2c30985fc1/app/src/app/logbook/data/db/config.service.ts#L29-L32>

Next we look at how we build on top of these data access services to drive state management and app logic using stores.

Expand Down
Loading