> Canonical URL: https://codingcat.dev/course/angularmaterial/lesson/angular-material-forms-from-firestore

# Angular Material Forms from Firestore | Angular Material | CodingCat.dev Course

[🧶 Angular Material](/course/angularmaterial)Section 1.6 · Lesson 6 of 8[![](/logos/firebase/firebase-symbol.png)![](/logos/firebase/firebase-symbol.png)Firebase](/topic/firebase)[![](/logos/tags/angular-symbol-light.svg)![](/logos/tags/angular-symbol-dark.svg)Angular](/topic/angular)[![](/logos/tags/html-symbol-light.svg)![](/logos/tags/html-symbol-dark.svg)HTML](/topic/html)[![](/logos/tags/github-symbol-light.svg)![](/logos/tags/github-symbol-dark.svg)GitHub](/topic/github)[← Prev Lesson](/course/angularmaterial/lesson/angular-material-dynamic-navigation-using-firestore)[Next Lesson →](/course/angularmaterial/lesson/angular-material-reactive-forms-update-firestore)

# Angular Material Forms from Firestore

![](https://cdn.sanity.io/media-libraries/mlttp0BZbCpV/images/e5e13cc945db42c6e018a6fda8b3239668f17514-1920x1080.png?w=1920&h=1080&q=82&fit=max&auto=format)[@CodingCatDev62.5K / 100K Subscribers Goal](https://www.youtube.com/@codingcatdev?sub_confirmation=1)[Subscribe on YouTube→](https://www.youtube.com/@codingcatdev?sub_confirmation=1)[Watch on YouTube App ↗](https://www.youtube.com/watch?v=LLupkLEszdY&list=UUnKZ8gEb78zXKMi1ns-IQ2g)Lesson 6 / 8Course Lessons Outline▾1Section 1
- [1.1Firebase Project Hosting](/course/angularmaterial/lesson/firebase-project-hosting)
- [1.2Angular Material Router Outlet](/course/angularmaterial/lesson/angular-material-router-outlet)
- [1.3Firebase Authentication and Authorization](/course/angularmaterial/lesson/firebase-authentication-and-authorization)
- [1.4Angular Material Theming](/course/angularmaterial/lesson/angular-material-theming)
- [1.5Angular Material Dynamic Navigation using Firestore](/course/angularmaterial/lesson/angular-material-dynamic-navigation-using-firestore)
- [1.6Angular Material Forms from Firestore](/course/angularmaterial/lesson/angular-material-forms-from-firestore)
- [1.7Angular Material Reactive Forms Update Firestore](/course/angularmaterial/lesson/angular-material-reactive-forms-update-firestore)
- [1.8Angular Material Router Awareness](/course/angularmaterial/lesson/angular-material-router-awareness)

[Demo](https://ajonp-lesson-12.firebaseapp.com/books/FirstBook/edit) This lesson will cover how to create all of the [Angular Material Form Components](https://material.angular.io/components/categories/forms), the data behind many of them will be coming from [Cloud Firestore](https://firebase.google.com/docs/firestore/).

# Setup

We will start this lesson from where we left off on [Angular Navigation Firestore](https://ajonp.com/courses/angularmaterial/angular-material-dynamic-navigation-using-firestore)

## Source from Prior lesson

Clone

Copied! 🐾

```
git clone  lesson12-angular-material-forms-firestore
```

Remove Origin, just always easier up front if you want to add this to your own repo (or again you can fork and just clone.)

Copied! 🐾

```
git remote rm origin
```

## Install Dependencies

Make sure you are in the correct directory `cd lesson12-angular-material-forms-firestore`.

Copied! 🐾

```
npm install
```

# Book Edit Module

## Create

Using the Angular CLI create the module with routing and corresponding component.

Copied! 🐾

```
ng g m modules/books/book-edit --routing && ng g c modules/books/book-edit
```

# Router updates

## books-routing.module.ts

Add new lazy loaded path so that our main books-routing knows where to send requests for edit.

Copied! 🐾

```
... { path: ':bookId/edit', loadChildren: './book-edit/book-edit.module#BookEditModule' } ...
```

## book-edit-routing.module.ts

Now that we are in the book-edit module make sure it has a path to the book-edit Component.

Copied! 🐾

```
... const routes: Routes = [ { path: '', component: BookEditComponent } ]; ...
```

## Serve the edit path

Startup the server

Copied! 🐾

```
ng serve
```

Now that our router is all setup we should start to see the book-edit.component.html. Because we don't have a way to navigate to this path yet just type it in the url bar manually `localhost:4200/books/FirstBook/edit`.

# Update Book Edit

## Structure

To give our form some structure we can now add Flex Layout and Material Card centered at 75% to give a nice look to our form.book-edit.component.html

Copied! 🐾

" aria-label="Copy code" title="Copy code to clipboard">

```

            Ok

```

Because these are new Elements we need to import them into our Book Edit module. book-edit.module.ts

Copied! 🐾

```
import { FlexLayoutModule } from '@angular/flex-layout';
import {MatCardModule} from '@angular/material';

...

@NgModule({ declarations: [BookEditComponent], imports: [ CommonModule, BookEditRoutingModule, FlexLayoutModule, MatCardModule, ] })

...
```

## Getting Firestore Data for Book Edit

Because we are now navigating to an area that uses Angular router and part of the path contains a specified paramter id `:bookId/edit` we can get this `bookId` from the currently Activated Route. In order to do this we need to use dependency injection and provide this in our constructor. To then fetch that data from our `FirestoreService` we can then inject this service as well.

Copied! 🐾

```
subs: Subscription[] = [];
book$: Observable;

constructor(private router: ActivatedRoute, private fs: FirestoreService) {}
ngOnInit() {
    // Get bookId for book document selection from Firestore
    this.subs.push(
        this.router.paramMap.subscribe(params => {
            const bookId = params.get('bookId');
            this.book$ = this.fs.getBook(bookId);
        })
    );
}
```

By calling the firestore `getBook` function and passing in the current parameter `bookId` we now have an Observable reference to the Firestore data.firestore.service.ts

Copied! 🐾

```
getBook(bookId: string): Observable {
    // Start Using AngularFirebase Service!!
    return this.afb.doc$(books/${bookId});
}
```

> This is a cool wrapper that Jeff over at fireship.io created. Feel free to copy this service and use it as a nice wrapper for all of your projects, I won't include the two calls as we move forward angularfirebase.service.ts

Copied! 🐾

```
doc$(ref: DocPredicate): Observable { return this.doc(ref) .snapshotChanges() .pipe( map( ( doc: Action > ) => { return doc.payload.data() as T; } ) ); }
```

Example of Book Data in Firestore Console:

In the snippet above we are also pushing our RxJs Subscription into an array so that we can then loop through any subscriptions during the destroy method and unsubscribe. This is a pattern I often use when I cannot use `pipe(take(1))` or `| async`.

Copied! 🐾

```
ngOnDestroy() {
    this.subs.forEach(sub => {
        sub.unsubscribe();
    });
}
```

## Using Firestore Data Inside of Template

We can not use our new `book$` Observable in our template to show any of the current data. We can update our card to show only when the book data is available, otherwise show a Material Spinner. You can read more on how the [NgIf](https://angular.io/api/common/NgIf) directive works in the Angular docs if you are unfamiliar.

Copied! 🐾

```

    {{book.title}}
    ...

```

Current title:

## Adding Form Input

> Just a warning here, if you want to see detailed Angular Reactive Form usage this will be done in the next lesson.

Now that we know our Observable is working successfully we can now change the card title out and start switching our card into several form inputs.For this we will need to include `FormsModule`, `ReactiveFormsModule`, `MatFormFieldModule`, and `MatInputModule` into our Book Edit Module.book-edit.module.ts

Copied! 🐾

```
imports: [ CommonModule, BookEditRoutingModule, FlexLayoutModule, MatCardModule, FormsModule, ReactiveFormsModule, MatFormFieldModule, MatInputModule,
```

### Title Input

The entire Material Card should now look like below, the new div surrounding the card is just to allow each section to flow in its own row of the column. If you have not used [Flex Layout](https://github.com/angular/flex-layout/wiki/fxFlex-API) check out the details.book-edit.component.html

Copied! 🐾

" aria-label="Copy code" title="Copy code to clipboard">

```
           Ok
```

Above we have our first two components-[Form Field](https://material.angular.io/components/form-field/overview) which you can think of as a wrapper to all of our components allowing for styling of the other form fields.-[Input](https://material.angular.io/components/input/overview) the key with this is the directive `matInput`, this will allow you to use a native `` or `` correctly styled within ``.

> If you see any issues at this point, make sure you have imported all the modules into book-edit.module.ts

For an extra you can checkout the textarea example too.

Copied! 🐾

" aria-label="Copy code" title="Copy code to clipboard">

```

            Ok

```

### Slide Toggle

-[Slide Toggle](https://material.angular.io/components/slide-toggle/overview) is a very simple comoponent that is either on or off (binary).book-edit.module.ts

Copied! 🐾

```
imports: [ ... MatSlideToggleModule,
```

For our requirement we are going to use this to determine whether a book is Fiction or non-fiction.We will set the default `fictionSelected = true;` so that Fiction is set first.book-edit.component.ts

Copied! 🐾

```
export class BookEditComponent implements OnInit, OnDestroy {
    subs: Subscription[] = [];
    book$: Observable;
    fictionSelected = true;
}
```

book-edit.component.html

Copied! 🐾

```

Fiction

```

You can see that our input directive `checked` (denoted by `[]`), will now take the value of `fictionSelected` and every time the toggle is changed we will use the output directive `change` (denoted by `()`) to trigger function `fictionChange` passing in the current components instance of the event `$event`. You can read more about [DOM event payloads](https://angular.io/guide/user-input#get-user-input-from-the-event-object).book-edit.component.ts

Copied! 🐾

```
fictionChange(e) { this.fictionSelected = e.checked; this.genreControl.reset(); }
```

### Autocomplete

- [Autocomplete](https://material.angular.io/components/autocomplete/overview) is just another input type in our case for text. However it also has a Panel that is associated to provide a list of options. For our use case it will list out two different lists based on our Slide Toggle. So we will either filter the Genere Fiction list of values, or a Non-Fiction list of values.

book-edit.module.ts

Copied! 🐾

```
imports: [ ... MatAutocompleteModule,
```

These two lists will be contained in a new collection at the base of our Firestore Database called `config`. Within our `config` collection we will create a document called `book`, which will hold many of our different configurations. For these two specifically they will be arrays that are on the `book` object.

First we will create our Observable to the Config Book object. book-edit.component.ts

Copied! 🐾

```
bookConfig$: Observable;
...

// Set Book Config

this.bookConfig$ = this.fs.getConfigBook();
```

Create the `ConfigBook` Interface for our type.

Copied! 🐾

```
ng g i core/models/config-book
```

config-book.ts

Copied! 🐾

```
export interface ConfigBook {
    ageCategory?: Array;
    fiction?: Array;
    nonFiction?: Array;
}
```

Then we can will grab the first set of values emitted from Firestore and send those out as a [BehaviorSubject](http://reactivex.io/rxjs/manual/overview.html#behaviorsubject) with type `ConfigBook`. Our toggle has set `this.fictionSelected` so we can determine what list should be emitted in `this.genereList$`.book-edit.component.ts

Copied! 🐾

```
export class BookEditComponent implements OnInit, OnDestroy {
    ...
    genreControl = new FormControl();
    ...
    // Set default Genere
    this.bookConfig$.pipe(take(1)).subscribe(bookConfig => {
        this.subs.push(
            this.genreControl.valueChanges.pipe(startWith('')).subscribe(value => {
                const filterValue = value ? value.toLowerCase() : '';
                if (this.fictionSelected) {
                    this.genreList$.next(
                        bookConfig.fiction.filter(option => option.toLowerCase().includes(filterValue))
                    );
                }
                else {
                    this.genreList$.next(
                        bookConfig.nonFiction.filter(option => option.toLowerCase().includes(filterValue))
                    );
                }
            })
        );
    });
}
```

You will also notice above the we have subscribed to any of the `valueChanges` that are happening on our new `genreControl`. Below you will see that `formControl` input directive is passed our class parameter `genreControl` which is an instance of `FormControl`. We will dive into all of the `@angular/forms` in more detail in the next lesson. For our sake here just know that this allows us to check all of the changing values as you type. When we start to type it uses the arrays that we have passed in from Firestore and filters them based on the string we are inputing using either `bookConfig.fiction.filter` or `bookConfig.nonFiction.filter`.

Copied! 🐾

```

            {{ genre }}

```

Above we are listening to the updates from `genreList$` BehaviorSubject to create our `` list of values. Our input has an input directive `[matAutocomplete]="auto"` to attach this `` by assigning the instance variable `#auto` to the input using `matAutocomplete`.

### Checkbox

> Take a deep breath there was a lot going on with the Autocomplete, the rest get a lot easier ????

- [Checkbox](https://material.angular.io/components/checkbox/overview) is again providing the same functionality as the native `` enhanced with Material Design.

book-edit.module.ts

Copied! 🐾

```
imports: [ ... MatCheckboxModule,
```

For us this contains some more of the configuration items from our book, because we already have an Observable created in `bookConfig$` we can just tell Angular to listen for this and assign it to our local template variable `bookConfig`. We then are just assigning these to the directive [NgModel](https://angular.io/api/forms/NgModel). This is a two way binding, for our example doesn't mean much, but again we will drive these things home further in the next lesson.

Copied! 🐾

```

### Options

            Audio

            Photos

            Videos

```

### Datepicker

- [Datepicker](https://material.angular.io/components/datepicker/overview) The datepicker allows users to enter a date either through text input, or by choosing a date from the calendar. It is made up of several components and directives that work together.

> Special note here you need MatNativeDateModule in addition to the MatDatepickerModule.

book-edit.module.ts

Copied! 🐾

```
imports: [ ... MatDatepickerModule, MatNativeDateModule
```

This is just creating the pre canned datepicker. We don't have any data that will will bring in currently to update this field. We will cover this in the next lesson.book-edit.component.html

Copied! 🐾

```

```

### Select

- [Select](https://material.angular.io/components/select/overview) you can use either the Material Select, or the native select within the ``. The native control has several performance advantages...but I really like the style of using the Material Design.

book-edit.module.ts

Copied! 🐾

```
imports: [ ... MatSelectModule,
```

> It would probably be better to unwrap our bookConfig$ once in our template, but I wanted to keep each of these as seperate units.

For our book component we are once again going to get all of the age categories from our Config Book in Firestore and use those values for our select.

book-edit.component.html

Copied! 🐾

```

                {{ ageCategory }}

```

### Slider

- [Slider](https://material.angular.io/components/slider/overview)`` allows for the selection of a value from a range via mouse, touch, or keyboard, similar to ``.

book-edit.module.ts

Copied! 🐾

```
imports: [ ... MatSliderModule,
```

We once again will not be doing anything with this value, but I did want to show you how to default the value on creation. Later we will tie this directly to the Firestore value for our book.book-edit.component.ts

Copied! 🐾

```
bookRating = 3;
```

book-edit.component.html

Copied! 🐾

```

### Rating

```

### Radio button

- [Radio button](https://material.angular.io/components/radio/overview)`` provides the same functionality as a native `` enhanced with Material Design styling and animations.

book-edit.module.ts

Copied! 🐾

```
imports: [ ... MatRadioModule,
```

For us this will again in the future refer directly to a status on our Book, we could create these from a Firestore config, but I don't see us changing these options very often. If you want you could do the same loop as we did with the select option and add the config. In the next lesson we will add that config and show how to do a validation of sorts.book-edit-component.html

Copied! 🐾

```

### Status

        Draft
        Published

```

# Wrap Up

Here we have created all of the Angular Material Form Components, many of them with data being pulled from Firestore. Next is to make our form more Reactive.

✏️ Visual Architecture Diagram

## how this course connects together.

Follow AJ through the layered stack topology from input contract to production output.

Let’s build it!✏️ Architecture Diagram: Angular Material: Angular Material Forms from Fi…LAYERED SYSTEM STACKARCHITECTURE DIAGRAM[Diagram Walkthrough]Content-driven layered-stack diagram for Angular Material: Angular Material Forms from Firestore: Section 1 -> Angular Material -> Verified Production Artifact.AAA Contrast Verified

## related deep dives.

[View All&rarr;](/blog)[![Sanity Knowledge Bases](https://cdn.sanity.io/media-libraries/mlttp0BZbCpV/images/38b0f021c51d97043c8a1ebf31d505a0ea877b7c-2752x1536.jpg?w=1920&q=82&fit=max&auto=format)](/post/sanity-knowledge-bases)October 17, 2026[![](/logos/tags/mcp-symbol-light.svg)![](/logos/tags/mcp-symbol-dark.svg)MCP](/topic/mcp)[#AI & Agents](/topic/ai)[![](/logos/sanity/Sanity-Symbol--black_on_transparent.svg)![](/logos/sanity/Sanity-Symbol--white_on_transparent.svg)Sanity](/topic/sanity)[+5](/post/sanity-knowledge-bases)

### [Sanity Knowledge Bases](/post/sanity-knowledge-bases)

Eliminate query-time guessing in your AI agents. Learn how Sanity Context Knowledge Bases compile structured datasets, crawled documentation websites, and uploaded files ahead of time, resolve conflicting facts, and serve a compact outline over Context MCP.

[![Alex Patterson](https://cdn.sanity.io/media-libraries/mlttp0BZbCpV/images/a606dfaa381aab08ddbe16a33fe1d3e1fb37c927-400x400.jpg?w=96&h=96&q=82&fit=max&auto=format)Alex Patterson](/author/alex-patterson)[![Dockerless Serverless GPUs: How I Built a Fast, Cheap AI SaaS with RunPod Flash](https://cdn.sanity.io/media-libraries/mlttp0BZbCpV/images/78383c8986667bc1eaa504786488d88f212929a8-1280x720.jpg?w=1280&q=82&fit=max&auto=format)](/post/dockerless-serverless-gpus-fast-cheap-ai-saas-with-runpod-flash)October 6, 2026[#Serverless](/topic/serverless)[![](/logos/tags/cloudflare-symbol-light.svg)![](/logos/tags/cloudflare-symbol-dark.svg)Cloudflare](/topic/cloudflare)[![](/logos/tags/python-symbol-light.svg)![](/logos/tags/python-symbol-dark.svg)Python](/topic/python)[+5](/post/dockerless-serverless-gpus-fast-cheap-ai-saas-with-runpod-flash)

### [Dockerless Serverless GPUs: How I Built a Fast, Cheap AI SaaS with RunPod Flash](/post/dockerless-serverless-gpus-fast-cheap-ai-saas-with-runpod-flash)

Hosted image APIs cost 3 to 4 cents per generation. Using RunPod Flash for dockerless serverless GPUs, FlashFun.dev drops that to a fraction of a cent, turning $1 into 500 to 3,000 images. Here is the full architecture, the benchmarks, and the code.

[![Alex Patterson](https://cdn.sanity.io/media-libraries/mlttp0BZbCpV/images/a606dfaa381aab08ddbe16a33fe1d3e1fb37c927-400x400.jpg?w=96&h=96&q=82&fit=max&auto=format)Alex Patterson](/author/alex-patterson)[Course Outline](/course/angularmaterial)

## Angular Material

6/81

### Section 1

- [1.1Firebase Project Hosting](/course/angularmaterial/lesson/firebase-project-hosting)
- [1.2Angular Material Router Outlet](/course/angularmaterial/lesson/angular-material-router-outlet)
- [1.3Firebase Authentication and Authorization](/course/angularmaterial/lesson/firebase-authentication-and-authorization)
- [1.4Angular Material Theming](/course/angularmaterial/lesson/angular-material-theming)
- [1.5Angular Material Dynamic Navigation using Firestore](/course/angularmaterial/lesson/angular-material-dynamic-navigation-using-firestore)
- [1.6Angular Material Forms from Firestore▶](/course/angularmaterial/lesson/angular-material-forms-from-firestore)
- [1.7Angular Material Reactive Forms Update Firestore](/course/angularmaterial/lesson/angular-material-reactive-forms-update-firestore)
- [1.8Angular Material Router Awareness](/course/angularmaterial/lesson/angular-material-router-awareness)

@CodingCatDev on YouTube62.5K / 100K

Subscribe for new course lessons and architecture deep dives every week.

[Subscribe for New Lessons →](https://www.youtube.com/@codingcatdev?sub_confirmation=1)