---
title: "Part 2 - Angular Jamstack Site with Dynamic Data Using Sanity.io"
description: "Part 2 - Continue the Angular + Sanity.io + Netlify Function journey by building out an Angular service, components, and a build hook!"
source: "https://www.netlify.com/blog/2021/03/02/part-2-angular-jamstack-site-with-dynamic-data-using-sanity.io/"
last_updated: "2026-09-21T18:57:14.000Z"
---
If you’re just joining us, this is part two of the series where we build out an Angular site that grabs data from [Sanity.io](https://www.sanity.io/), a [headless CMS](https://jamstack.org/glossary/headless-technology/), using [Netlify Functions](https://hubs.ly/H0HlGvq0) and [Build Hooks](https://hubs.ly/H0HlGvl0).

[Part 1 of this series](https://hubs.ly/H0Hks4C0) covered:

-   Starting with a [pre-rendered Angular site template](https://github.com/tzmanics/angular-sanity)
-   Setting up a headless CMS with Sanity.io
-   Grabbing the data from the CMS using Netlify Functions

Here, in Part 2 we are going to:

-   Add an Angular Service to handle the incoming data
-   Create and update Angular components to display the data
-   Setup a build hook to update the page when new data is added

This is what the page will look like:

[![the final project](/v3/img/blog/template.jpg "The final project")](https://angular-sanity.netlify.app/)

> ⏩ Want to skip the reading and just “make it work”? Here’s is [the project repo](https://github.com/tzmanics/angular-sanity), or you can click the button below to deploy the site now!

[![Deploy to Netlify](https://www.netlify.com/img/deploy/button.svg)](https://app.netlify.com/start/deploy?repository=https://github.com/tzmanics/angular-sanity&utm_source=blog&utm_medium=angular-sanity-tzm&utm_campaign=devex)

## Making the Angular Service

In the first part of the series we [added a Netlify Function to grab the data we needed from Sanity.io](https://github.com/tzmanics/angular-sanity/blob/main/functions/getProducts.js). Now, we will use an [Angular service](https://angular.io/guide/architecture-services) to access that data.

### Adding the Product Model

One thing that we’ll need before anything else is an interface of the product data we’re receiving from Sanity.io. An [interface](https://www.typescriptlang.org/docs/handbook/interfaces.html) is a TypeScript syntactical contract defining types so they can be checked. The `Product` interface is very complicated…everything is type `string` 🙃. Rest assured you can assign other types in interfaces.

[`src/app/models/Product.ts`](https://github.com/tzmanics/angular-sanity/blob/main/src/app/models/Product.ts)

```
export interface Product {  id: string;  name: string;  price: string;  url: string;  image: string;  description: string;}
```

### HTTP For You and Me

Another thing we’ll need for the service is the [HTTP Client](https://angular.io/guide/http). This is an injectable class with methods to perform HTTP requests. We’ll import that in the app’s main module file.

[`src/app/app.module.ts`](https://github.com/tzmanics/angular-sanity/blob/main/src/app/app.module.ts)

```
import { BrowserModule } from '@angular/platform-browser';+ import { HttpClientModule } from '@angular/common/http';import { NgModule } from '@angular/core';
import { AppRoutingModule } from './app-routing.module';import { AppComponent } from './app.component';import { NavigationComponent } from './navigation/navigation.component';
@NgModule({  declarations: [AppComponent, NavigationComponent],  imports: [    BrowserModule.withServerTransition({ appId: 'serverApp' }),    AppRoutingModule,+    HttpClientModule,  ],  providers: [],  bootstrap: [AppComponent],})export class AppModule {}
```

### On To the Actual Service

To make the service we can use the handy [`generate` command from the Angular CLI](https://angular.io/cli/generate) to create the files we need.

`ng generate service services/Products`

> 💡 We’re using the full command here but we could also use the shortened version `ng g s services/Product`.

Once we have the generated files (`product.service.ts` & `product.service.spec.ts`), we can import the HTTP client and the Product interface. We’ll also import `Observable` from the [RxJS](https://rxjs.dev/guide/overview) library since the products will be coming through as an observable sequence.

Inside the `ProductService` class we’ll use [dependency injection](https://angular.io/guide/dependency-injection) to use the HTTP client in the service. Finally, we’ll make a function, `getProduct` that will be returning `Observable<Product[]>` from its HTTP GET call to where the Netlify Function lives on the live site: `/.netlify/functions/getProducts`. The last thing we’ll add to the function is an object that passes the GET call a `headers` object that sets the Content-Type to application/json.

Put it all together and what we get:

[`src/app/services/products.service.ts`](https://github.com/tzmanics/angular-sanity/blob/main/src/app/services/products.service.ts)

```
import { Injectable } from '@angular/core';+ import { HttpClient } from '@angular/common/http';+ import { Observable } from 'rxjs';
+ import { Product } from '../models/Product';
@Injectable({  providedIn: 'root',})export class ProductsService {+ constructor(private http: HttpClient) {}
+ getProducts(): Observable<Product[]> {+   return this.http.get<Product[]>('/.netlify/functions/getProducts', {+     headers: {+       'Content-Type': 'application/json',+     },+   });+ }}
```

## Adding & Updating the Components

Now it’s time to really see the work we’ve done, literally. We’ll make a new component to hold the product information. Then we’ll update the existing `Product` component with the newly created component.

### Product List Component

To make a component that lists the product, we’ll use the Angular CLI generate command again. This time we’ll use it to make a component as so:

`ng g component components/ProductList --module products.module`

We want to pass it the `products.module` module (using `--module`) so that it becomes a child component of the main products component and they can share data.

#### The TypeScript Part

Inside the `ProductList` component’s TypeScript file we will:

-   import the [`Input` directive](https://angular.io/guide/inputs-outputs) in order to share data from parent to child component
-   import the `Product` interface we made in the first step
-   inside the `ProductListComponent` declare and instantiate the product data we’re sharing from the parent Products component: `@Input() product: Product`.

All of that looks like this:

[`src/app/components/product-list/product-list.component.ts`](https://github.com/tzmanics/angular-sanity/blob/main/src/app/components/product-list/product-list.component.ts)

```
+import { Component, OnInit, Input } from '@angular/core';+import { Product } from 'src/app/models/Product';
@Component({  selector: 'app-product-list',  templateUrl: './product-list.component.html',  styleUrls: ['./product-list.component.css'],})+export class ProductListComponent implements OnInit {+  @Input() product: Product = {+    name: 'test',+    id: '1',+    price: '1.23',+    url: 'https://netlify.com',+    image: 'https://bit.ly/20o7MGL',+    description: 'test',+  };
  constructor() {}
  ngOnInit(): void {}}
```

> 💡 We instantiate the `product` data with default data because TypeScript is set to strict mode and will throw an error if it doesn’t have matching types for the Interface, Product. I will show a work around for this further into the post in case this is too cumbersome.

#### The HTML Template Part

Now we get to the actual showing of stuff! To do this we’re going to use the `product` data we’re passing in and use dot notation to grab the parts of the product data we want to use.

Like so:

[`src/app/components/product-list/product-list.component.html`](https://github.com/tzmanics/angular-sanity/blob/main/src/app/components/product-list/product-list.component.html)

```
-<p>product-list works!</p>+<div class="product-item">+  <img [src]="product.image" />+  <div class="product-item-details">+    <h3 class="product-name">{% raw %}{{ product.name }}{% endraw %}</h3>+  </div>+</div>
```

#### The CSS Styling Part

Finally, the super complicated CSS goes into the component’s CSS file. I’m kidding, we’re just going to center-align the text.

[`src/app/components/product-list/product-list.component.css`](https://github.com/tzmanics/angular-sanity/blob/main/src/app/components/product-list/product-list.component.css)

```
+.product-item-details {+  text-align: center;+}
```

Phew, we’ve got the component that shows all the products! BUT it’s still not being rendered because we haven’t passed this component to the Product template. Guess what we’re going to do next!

### Updating the Product Component

From the [template project](https://github.com/tzmanics/angular-sanity) we have a Products component to list all of the products. This is where we’ll make the changes to insert all the components for each product.

#### The Module Part

We’ve actually already changed one of its files without even opening it! When we used the generate command to make the Product List component we referenced this Products’ module. Doing so imported the Product List component in the Products’ module TypeScript file.

[`src/app/products/products.module.ts`](https://github.com/tzmanics/angular-sanity/blob/main/src/app/products/products.module.ts)

```
import { NgModule } from '@angular/core';import { CommonModule } from '@angular/common';
import { ProductsRoutingModule } from './products-routing.module';import { ProductsComponent } from './products.component';+import { ProductListComponent } from '../components/product-list/product-list.component';
@NgModule({+  declarations: [ProductsComponent, ProductListComponent],  imports: [CommonModule, ProductsRoutingModule],})export class ProductsModule {}
```

#### The TypeScript Part

The Product component is the parent component that will be sending the product data to the Product List component. Like with the Product List component, we’ll need to import `Observable` and the `Product` interface. We also need to import `ProductsService`, the service we created earlier that calls the `getProducts` Netlify Function.

Inside of the class is where we do all the work:

-   assign `products` to an observable array of the interface we made, Product
-   inside the constructor we use data injection to use the service, `ProductsService` and assign it to `productsService`
-   when the lifecycle [`ngOnInit`](https://angular.io/api/core/OnInit) is called (after Angular has initialized all data-bound properties of a directive), we assign `products` to what is returned from `getProducts` inside the service `productsService`

[`src/app/products/products.component.ts`](https://github.com/tzmanics/angular-sanity/blob/main/src/app/products/products.component.ts)

```
import { Component, OnInit } from '@angular/core';+import { Observable } from 'rxjs';
+import { ProductsService } from 'src/app/services/products.service';+import { Product } from 'src/app/models/Product';
@Component({  selector: 'app-products',  templateUrl: './products.component.html',  styleUrls: ['./products.component.css'],})export class ProductsComponent implements OnInit {+  products!: Observable<Product[]>;
+  constructor(private productsService: ProductsService) {}
  ngOnInit(): void {+    this.products = this.productsService.getProducts();  }}
```

> 💡 When `products` is declared at the beginning of the `ProductsComponent` class I use the `!`, [the TypeScript non-null assertion operator](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-0.html#non-null-assertion-operator). This is the workaround I mentioned earlier that can be used instead of initializing with default data as we did in the Product List component. I don’t recommend this but thought it was a good alternative to show instead of turning TypeScript’s strict mode off.

Okay, we have the data! Let’s get going.

#### The HTML Template Part

We’re going to change a few things inside of the Products HTML template:

-   add a section for the Product List component
-   apply an [`*ngIf`](https://angular.io/api/common/NgIf) conditional directive to say if we have the products data use an [async pipe](https://angular.io/api/common/AsyncPipe) assigning the data to `products`, else displaying a loading block [Angular template](https://angular.io/guide/structural-directives)
-   then we pass in the Product List component (`app-product-list`)
-   we also pass a few parameters to the Product list component including an [`*ngFor`](https://angular.io/api/common/NgForOf) to loop through the products and assign each one to `product`
-   inside the component we’ll use [Angular’s text interpolation](https://angular.io/guide/interpolation) to display the product’s name (`{% raw %}{{ product.name }}{% endraw %}`)
-   finally, we take the existing ‘loading’ text and place it inside the conditional `ng-template` named `#loadingBlock`

That was a lot of words but let’s take a look below to see what the code actually looks like.

[`src/app/products/products.component.html`](https://github.com/tzmanics/angular-sanity/blob/main/src/app/products/products.component.html)

```
<div class="products">+  <main+    class="products-list"+    *ngIf="products | async as products; else loadingBlock"+  >+    <app-product-list+      class="product-item"+      *ngFor="let product of products"+      [product]="product"+    >+      {% raw %}{{ product.name }}{% endraw %}+    </app-product-list>+  </main>+  <ng-template #loadingBlock>+    <p>Loading products...</p>+  </ng-template></div>
```