Use Sanity CMS with Angular - blazed-space/fire-angular GitHub Wiki

Using Sanity CMS with Angular is much more challenging, but provides a more refined experience.

  1. Install Sanity Client & Image URL Builder
npm install @sanity/client @sanity/image-url @ng-web-apis/common rxjs
  1. Add config options to environment.ts
 export const environment = {
  production: false,
  sanity: {
   projectId: '< sanity project id>',
   dataset: '< dataset, usually production|development >',
   useCdn: true,
  },
  web: {
   url: '<#< website-url >#>',
  },
 }
  1. Update typescript config files
  • tsconfig.json
 "compilerOptions": { 
  "allowSyntheticDefaultImports": true,
   ...
 },
  • tsconfig.app.json
 "compilerOptions": {
   "types": ["node"],
   ...
 },
  1. Create Sanity Service
ng generate service SanityService

inside sanity-service.service.ts:

import { HttpClient } from '@angular/common/http';
import { Inject, Injectable } from '@angular/core';
import { WINDOW } from '@ng-web-apis/common';
import sanityClient, { ClientConfig, SanityClient } from '@sanity/client';
import imageUrlBuilder from '@sanity/image-url';
import { ImageUrlBuilder } from '@sanity/image-url/lib/types/builder';
import { SanityImageSource } from '@sanity/image-url/lib/types/types';
import { map, Observable } from 'rxjs';
import { environment } from '../environments/environment';
@Injectable({
 providedIn: 'root',
})
export class SanityService {
 private client: SanityClient;
 private imageUrlBuilder: ImageUrlBuilder;
 private clientConfig: ClientConfig = {
  projectId: environment.sanity.projectId,
  dataset: environment.sanity.dataset,
  apiVersion: this.getApiVersion(),
  useCdn: false,
 };

  constructor(private http: HttpClient, @Inject(WINDOW) private wnd: Window,) {
   this.client = this.createClient();
   this.imageUrlBuilder = imageUrlBuilder(this.client);
  }

  getImageUrlBuilder(source: SanityImageSource): ImageUrlBuilder {
   return this.imageUrlBuilder.image(source);
  }

  fetch<T>(query: string): Observable<T> {
   const url = `${this.generateSanityUrl()}${encodeURIComponent(query)}`
   return this.http.get(url).pipe(map((response: any) => response.result as T));
  }

  private createClient(): SanityClient {
   return sanityClient(this.clientConfig);
  }

  private generateSanityUrl(): string {
  const { apiVersion, dataset, projectId } = this.clientConfig;

  const baseUrl = 
  this.wnd.location.href.startsWith(environment.web.url) || 
  this.wnd.location.href.startsWith('http://localhost:4200') ? 
  `https://${projectId}.api.sanity.io/` : `${this.wnd.location.origin}/api/`;

  return `${baseUrl}${apiVersion}/data/query/${dataset}?query=`;
 }

 private getApiVersion(): string {
  return `v${new Date().toISOString().split('T')[0]}`;
 }
}
  1. Create Angular Pipe
  • Create Sanity-Image-Pipe (sanity-image.pipe.ts):
import { Pipe, PipeTransform } from '@angular/core';
import { SanityService } from './sanity-service.service';
import { SanityImageSource } from '@sanity/image-url/lib/types/types';

@Pipe({ name: 'sanityImage' })
export class SanityImagePipe implements PipeTransform {
 constructor(private sanityService: SanityService) {}

 transform(value: SanityImageSource, width?: number): string {
  if (width) {
   return this.sanityService.getImageUrlBuilder(value).width(width).url();
  }
  return this.sanityService.getImageUrlBuilder(value).url();
 }
}
  • Then, insert the pipe into the declarations (in app.module.ts):
 @NgModule({
  declarations: [
  ...
  SanityImagePipe,
  ...
  ]
 })
  1. Import in your application:
 import { Observable } from 'rxjs';
 import { SanityService } from '../sanity-service.service';
  1. Use in your application:
export interface Movie {
 _id: String,
 title: String,
 releaseDate: Date,
 body: String,
 poster: SanityImageSource, // = string | SanityReference | SanityAsset | 
 SanityImageObject etc.
}

@Component({
 selector: 'app-movies',
  template: `
   <main class="container">
    <div *ngFor="let movie of movies$ | async">
     <img [src]="movie.poster | sanityImage:200" />
     <h3>{{ movie.title }}</h3>
     <p>Released on {{ movie.releaseDate | date }}</p>
    </div>
   </main>
  `,
  styleUrls: ['./movies.component.css']
})
export class MoviesComponent implements OnInit {
 movies$: Observable<Movie[]>
			
 constructor(private sanityService: SanityService ) { 
  this.movies$ = this.sanityService.fetch<Movie[]>(
   `*[_type == "movie"]{
      _id,
      title,
      releaseDate,
      poster,
      author->
   }`
  )  
 }
}
  1. Make HttpClient available to all modules
  • Add import to app.module.ts:
 import { HttpClientModule } from '@angular/common/http';
  • Add import to NgModule decorator:
@NgModule({
 ...
 imports:[ HttpClientModule ]
 ...
})
  1. Create "ToHTMLPipe" to convert block content into HTML
  • First, install to-html package
npm i @portabletext/to-html
  • Next, create to-html.pipe.ts
import { Pipe, PipeTransform } from '@angular/core';
import {toHTML} from '@portabletext/to-html';

@Pipe({ name: 'toHTML' })
export class ToHTMLPipe implements PipeTransform {
 constructor() {}

  transform(value: any): string {
   return toHTML(value);
  }
}
  • Then, insert the pipe into the declarations (in app.module.ts)
@NgModule({
 declarations: [
  ...
  ToHTMLPipe,
  ...
 ]
})
  • Finally, use it as follows in your component.html file:
 ...
  <div innerHTML="{{ post.body | toHTML }}" class="text-coolGray-800 p-10">
   <!--
    Content Will Go Here
   -->
  </div>
  ...

Sources

⚠️ **GitHub.com Fallback** ⚠️