{"id":5789,"date":"2023-12-14T11:08:43","date_gmt":"2023-12-14T16:08:43","guid":{"rendered":"https:\/\/ionic.io\/blog\/?p=5789"},"modified":"2023-12-14T14:47:34","modified_gmt":"2023-12-14T19:47:34","slug":"building-angular-apps-with-ionic-and-standalone-components","status":"publish","type":"post","link":"https:\/\/ionic.io\/blog\/building-angular-apps-with-ionic-and-standalone-components","title":{"rendered":"Building Modern Angular Apps with Ionic and Standalone Components"},"content":{"rendered":"\n<p><em>This is a guest post from Simon Grimm, Ionic Developer Expert and educator at <\/em><a href=\"https:\/\/ionicacademy.com\/\"><em>the Ionic Academy<\/em><\/a><em>, an online school with 70+ video courses focused entirely on building awesome mobile apps with Ionic and Capacitor!<\/em><\/p>\n\n\n\n<p>With Angular v17, the Angular team introduced many groundbreaking changes to how you can write powerful and performant Angular apps. All these changes are available in Ionic as well, and in this tutorial, we&#8217;ll take a look at how you can use them to build modern Angular apps with Ionic.<\/p>\n\n\n\n<p>By the end of this tutorial, you will know how to:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Use the new Angular Control Flow<\/li>\n\n\n\n<li>Work with Signals<\/li>\n\n\n\n<li>Use Deferred Blocks<\/li>\n\n\n\n<li>Import Ionic Standalone Components<\/li>\n<\/ul>\n\n\n\n<p>We will build a simple movie app that shows a list of trending movies and allows us to view the details of each movie, as below.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" data-src=\"https:\/\/lh7-us.googleusercontent.com\/LEe8-puthhaQhCGy23ZVSGch9tmFawKK2908t251sUyIGawbYNLt2bLMAELVHzK42cErDipGSia8PVJM4I6k-nqZdL9XCqjW2iDCGalPLXGFd35L34pYU8ulvhgcVSisqIIRZrgUfqLGFV676hkXP7g\" alt=\"\" src=\"data:image\/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\" class=\"lazyload\" \/><noscript><img decoding=\"async\" src=\"https:\/\/lh7-us.googleusercontent.com\/LEe8-puthhaQhCGy23ZVSGch9tmFawKK2908t251sUyIGawbYNLt2bLMAELVHzK42cErDipGSia8PVJM4I6k-nqZdL9XCqjW2iDCGalPLXGFd35L34pYU8ulvhgcVSisqIIRZrgUfqLGFV676hkXP7g\" alt=\"\"\/><\/noscript><\/figure>\n\n\n\n<p>You can also <a href=\"https:\/\/github.com\/Galaxies-dev\/ionic-modern-angular\">find the full source code on GitHub<\/a>. Now roll up your sleeves, and let&#8217;s get started!<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Setting up the Project with Angular 17<\/strong><\/h2>\n\n\n\n<p>To get started, bring up a terminal, create a new Ionic app, and navigate into the project folder. We will use the blank template and the <code>--type angular<\/code> flag to create an Angular app &#8211; make sure you pick Standalone Components when asked about how you want to build your app!<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-\">ionic start modernApp blank --type angular\n\ncd .\/modernApp\n\nionic g service services\/movie\n\nionic g page details<\/code><\/pre>\n\n\n\n<p>Additionally we generate a service to fetch the movie data and a details page to show the details of a movie. To correctly make HTTP calls, we can now add the <code>provideHttpClient<\/code> to the <code>src\/main.ts<\/code> as we don&#8217;t have a main module anymore:<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-typescript\">import { enableProdMode, importProvidersFrom } from &#039;@angular\/core&#039;;\nimport { bootstrapApplication } from &#039;@angular\/platform-browser&#039;;\nimport { RouteReuseStrategy, provideRouter, withComponentInputBinding } from &#039;@angular\/router&#039;;\nimport { IonicRouteStrategy, provideIonicAngular } from &#039;@ionic\/angular\/standalone&#039;;\n\nimport { routes } from &#039;.\/app\/app.routes&#039;;\nimport { AppComponent } from &#039;.\/app\/app.component&#039;;\nimport { environment } from &#039;.\/environments\/environment&#039;;\nimport { provideHttpClient } from &#039;@angular\/common\/http&#039;;\n\nif (environment.production) {\n  enableProdMode();\n}\n\nbootstrapApplication(AppComponent, {\n  providers: [\n    { provide: RouteReuseStrategy, useClass: IonicRouteStrategy },\n    provideIonicAngular(),\n    provideRouter(routes, withComponentInputBinding()),\n    provideHttpClient(),\n  ],\n});<\/code><\/pre>\n\n\n\n<p>In addition to <code>provideHttpClient<\/code>, we also added <code>withComponentInputBinding<\/code> to the <code>provideRouter<\/code> call to enable the automatic binding of route parameters to components. We&#8217;ll come back to this once we build the details page.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Angular ESBuild<\/strong><\/h2>\n\n\n\n<p>To make use of all of the powerful new features of Angular 17, we can now enable <a href=\"https:\/\/angular.io\/guide\/esbuild\">ESBuild<\/a>, which makes building our app much faster.<\/p>\n\n\n\n<p>To do so, we need to change the <code>@angular-devkit\/build-angular:browser<\/code> package to <code>@angular-devkit\/build-angular:application<\/code>, end then rename the main property to browser inside the <code>angular.json<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-json\">&quot;architect&quot;: {\n        &quot;build&quot;: {\n          &quot;builder&quot;: &quot;@angular-devkit\/build-angular:application&quot;,\n          &quot;options&quot;: {\n            &quot;browser&quot;: &quot;src\/main.ts&quot;,\n            &quot;polyfills&quot;: [&quot;src\/polyfills.ts&quot;],<\/code><\/pre>\n\n\n\n<p>Additionally, <code>polyfills<\/code> is now an array, and you need to remove <code>buildOptimizer<\/code> &amp; <code>vendorChunk<\/code> from the configurations block. With all of that in place, you are now on the fastest possible Angular build!<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Adding the Movie Database API<\/strong><\/h2>\n\n\n\n<p>To follow along with this tutorial, you also need to get an API key for the <a href=\"https:\/\/developers.themoviedb.org\/3\/getting-started\/introduction\">The Movie Database API<\/a>.<\/p>\n\n\n\n<p>Simply create an account and request one for free, then open the <code>src\/environments\/environment.ts<\/code> file and add your API key:<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-typescript\">export const environment = {\n\u00a0\u00a0production: false,\n\u00a0\u00a0apiKey: &#039;YOURKEY&#039;,\n};<\/code><\/pre>\n\n\n\n<p>If you create a production build later, also update the <code>src\/environments\/environment.prod.ts<\/code> file with that key.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Creating a Service<\/strong><\/h2>\n\n\n\n<p>To use Typescript in the best possible way, I like to generate Typescript interfaces based on JSON responses from APIs using <a href=\"https:\/\/marketplace.visualstudio.com\/items?itemName=MariusAlchimavicius.json-to-ts\">this VSC Extension<\/a>.<\/p>\n\n\n\n<p>In our case, this results in some interfaces we can rename and put into a new <code>src\/app\/services\/interfaces.ts<\/code> file:<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-typescript\">export interface ApiResult {\n  page: number;\n  results: any[];\n  total_pages: number;\n  total_results: number;\n}\n\nexport interface MovieResult {\n  adult: boolean;\n  backdrop_path: string;\n  belongs_to_collection?: any;\n  budget: number;\n  genres: Genre[];\n  homepage: string;\n  id: number;\n  imdb_id: string;\n  original_language: string;\n  original_title: string;\n  overview: string;\n  popularity: number;\n  poster_path: string;\n  production_companies: Productioncompany[];\n  production_countries: Productioncountry[];\n  release_date: string;\n  revenue: number;\n  runtime: number;\n  spoken_languages: Spokenlanguage[];\n  status: string;\n  tagline: string;\n  title: string;\n  video: boolean;\n  vote_average: number;\n  vote_count: number;\n}\n\ninterface Spokenlanguage {\n  english_name: string;\n  iso_639_1: string;\n  name: string;\n}\n\ninterface Productioncountry {\n  iso_3166_1: string;\n  name: string;\n}\n\ninterface Productioncompany {\n  id: number;\n  logo_path?: string;\n  name: string;\n  origin_country: string;\n}\n\ninterface Genre {\n  id: number;\n  name: string;\n}<\/code><\/pre>\n\n\n\n<p>Now we can create a new service to fetch the movie data from the API. Services haven&#8217;t changed much, but they don&#8217;t have to: React developers would love to have a singleton service construct to inject anywhere!<\/p>\n\n\n\n<p>Services structure our Angular app, split up view and business logic and make it easy to share data between components. Enough of the praise, let&#8217;s implement our service to make two API calls inside the <code>src\/app\/services\/movie.service.ts<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-typescript\">import { HttpClient } from &#039;@angular\/common\/http&#039;;\nimport { Injectable } from &#039;@angular\/core&#039;;\nimport { delay } from &#039;rxjs\/operators&#039;;\nimport { Observable } from &#039;rxjs\/internal\/Observable&#039;;\nimport { ApiResult, MovieResult } from &#039;.\/interfaces&#039;;\nimport { environment } from &#039;src\/environments\/environment&#039;;\n\nconst BASE_URL = &#039;https:\/\/api.themoviedb.org\/3&#039;;\nconst API_KEY = environment.apiKey;\n\n@Injectable({\n  providedIn: &#039;root&#039;,\n})\nexport class MovieService {\n  private http = inject(HttpClient);\n\n  constructor() {}\n\n  getTopRatedMovies(page = 1): Observable&lt;ApiResult&gt; {\n    return this.http\n      .get&lt;ApiResult&gt;(`${BASE_URL}\/movie\/popular?page=${page}&amp;api_key=${API_KEY}`)\n      .pipe(\n        delay(2000) \/\/ Simulate slow network\n      );\n  }\n\n  getMovieDetails(id: string): Observable&lt;MovieResult&gt; {\n    return this.http.get&lt;MovieResult&gt;(`${BASE_URL}\/movie\/${id}?api_key=${API_KEY}`);\n  }\n}<\/code><\/pre>\n\n\n\n<p>As we use the latest and greatest, we now also moved the dependency injection to the class level by using inject directly.&nbsp;<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Using the new Angular Control Flow<\/h2>\n\n\n\n<p>We can now start building our app by adding a new page to show the list of trending movies, based on the data we get from our MovieService. Additionally, we will now add imports for all Ionic components that we will later use in our view.<\/p>\n\n\n\n<p><em>Why? <\/em>Because we can, thanks to <a href=\"https:\/\/ionic.io\/blog\/announcing-ionic-7-5\">Ionic 7.5<\/a>!<\/p>\n\n\n\n<p>Instead of importing all Ionic components, we can now pick the ones we actually need from the <code>@ionic\/angular\/standalone<\/code> package and include them in our Angular standalone components. While this does add a few lines to our imports, it also makes it easier to see which components we actually use in our view. Additionally, this makes loading pages faster and reduces the final bundle size of our app.<\/p>\n\n\n\n<p>We now need one function to load the trending movies, and another one to load more movies when we scroll to the bottom of the list which triggers the infinite scroll event.<\/p>\n\n\n\n<p>Go ahead by changing the <code>src\/app\/home\/home.page.ts<\/code> to:<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-typescript\">import { Component, OnInit, inject } from &#039;@angular\/core&#039;;\nimport {\n  IonHeader,\n  IonToolbar,\n  IonTitle,\n  IonContent,\n  InfiniteScrollCustomEvent,\n  IonBadge,\n  IonLabel,\n  IonAvatar,\n  IonItem,\n  IonList,\n  IonLoading,\n  IonInfiniteScroll,\n  IonInfiniteScrollContent,\n  IonSkeletonText,\n  IonAlert,\n} from &#039;@ionic\/angular\/standalone&#039;;\nimport { MovieService } from &#039;..\/services\/movie.service&#039;;\nimport { DatePipe } from &#039;@angular\/common&#039;;\nimport { RouterModule } from &#039;@angular\/router&#039;;\nimport { catchError, finalize } from &#039;rxjs&#039;;\n\n@Component({\n  selector: &#039;app-home&#039;,\n  templateUrl: &#039;home.page.html&#039;,\n  styleUrls: [&#039;home.page.scss&#039;],\n  standalone: true,\n  imports: [\n    IonHeader,\n    IonToolbar,\n    IonTitle,\n    IonContent,\n    IonLabel,\n    IonBadge,\n    IonAvatar,\n    IonItem,\n    IonList,\n    IonLoading,\n    IonInfiniteScroll,\n    IonInfiniteScrollContent,\n    IonSkeletonText,\n    IonAlert,\n    DatePipe,\n    RouterModule,\n  ],\n})\nexport class HomePage implements OnInit {\n  private movieService = inject(MovieService);\n\n  private currentPage = 1;\n  public movies: any[] = [];\n  public imageBaseUrl = &#039;https:\/\/image.tmdb.org\/t\/p&#039;;\n  public isLoading = true;\n  public error = null;\n  public dummyArray = new Array(5);\n\n  \/\/ Load the first page of movies during component initialization\n  ngOnInit() {\n    this.loadMovies();\n  }\n\n  async loadMovies(event?: InfiniteScrollCustomEvent) {\n    this.error = null;\n\n    \/\/ Only show loading indicator on initial load\n    if (!event) {\n      this.isLoading = true;\n    }\n\n    \/\/ Get the next page of movies from the MovieService\n    this.movieService\n      .getTopRatedMovies(this.currentPage)\n      .pipe(\n        finalize(() =&gt; {\n          this.isLoading = false;\n        }),\n        catchError((err: any) =&gt; {\n          this.error = err.error.status_message;\n          return [];\n        })\n      )\n      .subscribe({\n        next: (res) =&gt; {\n          \/\/ Append the results to our movies array\n          this.movies.push(...res.results);\n\n          \/\/ Resolve the infinite scroll promise to tell Ionic that we are done\n          event?.target.complete();\n\n          \/\/ Disable the infinite scroll when we reach the end of the list\n          if (event) {\n            event.target.disabled = res.total_pages === this.currentPage;\n          }\n        },\n      });\n  }\n\n  \/\/ This method is called by the infinite scroll event handler\n  loadMore(event: InfiniteScrollCustomEvent) {\n    this.currentPage++;\n    this.loadMovies(event);\n  }\n}<\/code><\/pre>\n\n\n\n<p>Nothing too crazy going on here, so let&#8217;s move to the view now. The new Angular Control Flow is a powerful feature that allows us to write cleaner and more readable code.<\/p>\n\n\n\n<p>It&#8217;s based on the <code>@if<\/code> and <code>@for<\/code> directives, which are similar to the <code>*ngIf<\/code> and <code>*ngFor<\/code> directives, but with some important differences.<\/p>\n\n\n\n<p>The <code>@if<\/code> directive is used to conditionally render a block of code, and it can be used just as you would expect &#8211; but inside the HTML:<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-\">@if (condition) { ... }\u00a0\n\n@else if { ... }\u00a0\n\n@else { ... }<\/code><\/pre>\n\n\n\n<p>The second new directive is <code>@for<\/code>, which is used to iterate over an array and render a block of code for each item in the array:<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-\">@for (item of items; track item.id) { ... }<\/code><\/pre>\n\n\n\n<p>The track keyword is used to tell Angular how to track the items in the array, and it&#8217;s required when using <code>@for<\/code>.<\/p>\n\n\n\n<p>Let&#8217;s build a view around our isLoading state and the value from our API call:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>If we are loading, we show a list of six <a href=\"https:\/\/ionicframework.com\/docs\/api\/skeleton-text\">Ionic skeleton<\/a> items<\/li>\n\n\n\n<li>If we have an error, we show an <a href=\"https:\/\/ionicframework.com\/docs\/api\/alert\">Ionic inline alert<\/a> with the error message<\/li>\n\n\n\n<li>When we have an array, we show a list of movies<\/li>\n\n\n\n<li>When the array is empty and we finished loading, we show a message that no movies were found.<\/li>\n<\/ul>\n\n\n\n<p>Bring up the <code>src\/app\/home\/home.page.html<\/code> now and change it to:<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-xml\">&lt;ion-header&gt;\n  &lt;ion-toolbar color=&quot;primary&quot;&gt;\n    &lt;ion-title&gt;Trending Movies&lt;\/ion-title&gt;\n  &lt;\/ion-toolbar&gt;\n&lt;\/ion-header&gt;\n\n&lt;ion-content&gt;\n  @if (isLoading) {\n  &lt;ion-list class=&quot;ion-padding-top&quot;&gt;\n    @for (i of dummyArray; track i) {\n    &lt;ion-item lines=&quot;none&quot; class=&quot;ion-padding-bottom&quot;&gt;\n      &lt;ion-avatar slot=&quot;start&quot;&gt;\n        &lt;ion-skeleton-text&gt;&lt;\/ion-skeleton-text&gt;\n      &lt;\/ion-avatar&gt;\n      &lt;ion-skeleton-text animated style=&quot;height: 40px&quot; \/&gt;\n    &lt;\/ion-item&gt;\n    }\n  &lt;\/ion-list&gt;\n  } @else if (error) {\n  &lt;ion-alert\n    header=&quot;Error&quot;\n    [message]=&quot;error&quot;\n    isOpen=&quot;true&quot;\n    [buttons]=&quot;[&#039;Ok&#039;]&quot;\n  \/&gt;\n  }\n\n  &lt;ion-list class=&quot;ion-padding-top&quot;&gt;\n    @for (item of movies; track item.id) {\n    &lt;ion-item button [routerLink]=&quot;[&#039;\/details&#039;, item.id]&quot;&gt;\n      &lt;ion-avatar slot=&quot;start&quot;&gt;\n        &lt;img [src]=&quot;imageBaseUrl + &#039;\/w92&#039; + item.poster_path&quot; \/&gt;\n      &lt;\/ion-avatar&gt;\n\n      &lt;ion-label class=&quot;ion-text-wrap&quot;&gt;\n        &lt;h3&gt;{{ item.title }}&lt;\/h3&gt;\n        &lt;p&gt;{{ item.release_date | date:&#039;y&#039; }}&lt;\/p&gt;\n      &lt;\/ion-label&gt;\n\n      &lt;ion-badge slot=&quot;end&quot;&gt; {{ item.vote_average }} &lt;\/ion-badge&gt;\n    &lt;\/ion-item&gt;\n    } \n    @empty { \n      @if (!isLoading) {\n      &lt;ion-item lines=&quot;none&quot;&gt;\n        &lt;ion-label class=&quot;ion-text-center&quot;&gt;No movies found&lt;\/ion-label&gt;\n      &lt;\/ion-item&gt;\n      } \n    }\n  &lt;\/ion-list&gt;\n\n  &lt;ion-infinite-scroll (ionInfinite)=&quot;loadMore($event)&quot;&gt;\n    &lt;ion-infinite-scroll-content\n      loadingSpinner=&quot;bubbles&quot;\n      loadingText=&quot;Loading more data...&quot;\n    \/&gt;\n  &lt;\/ion-infinite-scroll&gt;\n&lt;\/ion-content&gt;<\/code><\/pre>\n\n\n\n<p>If you added your API key correctly, you should now see a list of trending movies.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" data-src=\"https:\/\/lh7-us.googleusercontent.com\/b25cBZwGuWI_cgYvxi9YzniNIRCg57g0QZOoql6xWw8uQeYST540hIJz8nDtbLY1H6kYVWLb2Ry8dsaFxb15DVOYPlUveC-kTP3J2qplOxXrT8Hbl8BDbfCP_YYPawsVumFc1ovCzgIiXr6O5503Yfc\" alt=\"\" src=\"data:image\/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\" class=\"lazyload\" \/><noscript><img decoding=\"async\" src=\"https:\/\/lh7-us.googleusercontent.com\/b25cBZwGuWI_cgYvxi9YzniNIRCg57g0QZOoql6xWw8uQeYST540hIJz8nDtbLY1H6kYVWLb2Ry8dsaFxb15DVOYPlUveC-kTP3J2qplOxXrT8Hbl8BDbfCP_YYPawsVumFc1ovCzgIiXr6O5503Yfc\" alt=\"\"\/><\/noscript><\/figure>\n\n\n\n<p>Congratulations on using the new Angular Control Flow with Ionic! You are now officially among the <em>cool kids<\/em>. Clicking on one of the movies should take us to the details page, so let&#8217;s work on that now.<\/p>\n\n\n\n<h1 class=\"wp-block-heading\"><strong>Working with Signals<\/strong><\/h1>\n\n\n\n<p>In the beginning, the CLI automatically changed our routing, but we need to include the :id parameter manually now.<\/p>\n\n\n\n<p>Open up the <code>src\/app\/app.routes.ts<\/code> file and change it to:<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-typescript\">import { Routes } from &#039;@angular\/router&#039;;\n\nexport const routes: Routes = [\n  {\n    path: &#039;home&#039;,\n    loadComponent: () =&gt; import(&#039;.\/home\/home.page&#039;).then((m) =&gt; m.HomePage),\n  },\n  {\n    path: &#039;&#039;,\n    redirectTo: &#039;home-defer&#039;,\n    pathMatch: &#039;full&#039;,\n  },\n  {\n    path: &#039;details\/:id&#039;, \/\/ &lt;-- Add the :id parameter\n    loadComponent: () =&gt;\n      import(&#039;.\/details\/details.page&#039;).then((m) =&gt; m.DetailsPage),\n  },\n  {\n    path: &#039;home-defer&#039;,\n    loadComponent: () =&gt;\n      import(&#039;.\/home-defer\/home-defer.page&#039;).then((m) =&gt; m.HomeDeferPage),\n  },\n];<\/code><\/pre>\n\n\n\n<p>Now the buttons in our list work, and we are able to retrieve the id parameter from the URL on the details page. Remember how we added the <code>withComponentInputBinding<\/code> to the <code>provideRouter<\/code> call in the <code>src\/main.ts<\/code>? This is where it comes into play!<\/p>\n\n\n\n<p>We can now use the <code>@Input<\/code> decorator to bind the id parameter to a variable in our details page, and the set function will be called whenever the value changes.<\/p>\n\n\n\n<p>This makes it really easy for us to immediately load the movie details when the id changes!<\/p>\n\n\n\n<p>On top of that we will also use the new signal function to create a <a href=\"https:\/\/angular.io\/api\/core\/Signal\">Signal<\/a> that we can use in our view to show the movie details.<\/p>\n\n\n\n<p>Not really required here, but I wanted to show you how to use Signals! The usage is really easy:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Create a new Signal with <code>signal&lt;T&gt;(initialValue)<\/code><\/li>\n\n\n\n<li>Set the value with <code>set(newValue)<\/code><\/li>\n\n\n\n<li>Get the value by calling the <code>signal()<\/code><\/li>\n<\/ul>\n\n\n\n<p>Open the <code>src\/app\/details\/details.page.ts<\/code> now and change it to:<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-typescript\">import {\n  Component,\n  Input,\n  WritableSignal,\n  inject,\n  signal,\n} from &#039;@angular\/core&#039;;\nimport { MovieService } from &#039;..\/services\/movie.service&#039;;\nimport { MovieResult } from &#039;..\/services\/interfaces&#039;;\nimport {\n  IonBackButton,\n  IonButtons,\n  IonCard,\n  IonCardContent,\n  IonCardHeader,\n  IonCardSubtitle,\n  IonCardTitle,\n  IonContent,\n  IonHeader,\n  IonIcon,\n  IonItem,\n  IonLabel,\n  IonText,\n  IonTitle,\n  IonToolbar,\n} from &#039;@ionic\/angular\/standalone&#039;;\nimport { CurrencyPipe, DatePipe } from &#039;@angular\/common&#039;;\nimport { addIcons } from &#039;ionicons&#039;;\nimport { cashOutline, calendarOutline } from &#039;ionicons\/icons&#039;;\n\n@Component({\n  selector: &#039;app-details&#039;,\n  templateUrl: &#039;.\/details.page.html&#039;,\n  styleUrls: [&#039;.\/details.page.scss&#039;],\n  standalone: true,\n  imports: [\n    IonHeader,\n    IonToolbar,\n    IonTitle,\n    IonContent,\n    IonIcon,\n    IonCard,\n    IonCardHeader,\n    IonCardTitle,\n    IonCardSubtitle,\n    IonCardContent,\n    IonText,\n    IonLabel,\n    IonButtons,\n    IonBackButton,\n    IonItem,\n    CurrencyPipe,\n    DatePipe,\n  ],\n})\nexport class DetailsPage {\n  private movieService = inject(MovieService);\n  public movie: WritableSignal&lt;MovieResult | null&gt; = signal&lt;MovieResult | null&gt;(\n    null,\n  );\n  public imageBaseUrl = &#039;https:\/\/image.tmdb.org\/t\/p&#039;;\n\n  \/\/ Load the movie details when the id changes through the URL :id parameter\n  @Input()\n  set id(movieId: string) {\n    \/\/ This is just to show Signal usage\n    \/\/ You could also just assign the value to a variable directly\n    this.movieService.getMovieDetails(movieId).subscribe((movie) =&gt; {\n      this.movie.set(movie);\n    });\n  }\n\n  constructor() {\n    \/\/ Load the the required ionicons\n    addIcons({\n      cashOutline,\n      calendarOutline,\n    });\n  }\n}<\/code><\/pre>\n\n\n\n<p>Almost forgot another change of Ionic standalone components: We can manually add the icons we need to the addIcons function, which will then be included in the final bundle.<\/p>\n\n\n\n<p>Now we can use the movie signal in our view to show the movie details.<\/p>\n\n\n\n<p>We will also use the new control flow again and cast the movie signal to a variable with <code>as movie<\/code> to make it easier to work with.<\/p>\n\n\n\n<p>Open up the <code>src\/app\/details\/details.page.html<\/code> file and change it to:<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-xml\">&lt;ion-header&gt;\n  &lt;ion-toolbar color=&quot;primary&quot;&gt;\n    &lt;ion-buttons slot=&quot;start&quot;&gt;\n      &lt;ion-back-button defaultHref=&quot;\/&quot;&gt;&lt;\/ion-back-button&gt;\n    &lt;\/ion-buttons&gt;\n    &lt;ion-title&gt;{{ movie()?.title }}&lt;\/ion-title&gt;\n  &lt;\/ion-toolbar&gt;\n&lt;\/ion-header&gt;\n\n&lt;ion-content&gt;\n  @if (movie(); as movie) {\n  &lt;ion-card&gt;\n    &lt;div\n      [style.height.px]=&quot;300&quot;\n      [style.background-image]=&quot;&#039;url(&#039; + imageBaseUrl + &#039;\/w400&#039; + movie?.poster_path + &#039;)&#039;&quot;&gt;&lt;\/div&gt;\n\n    &lt;ion-card-header&gt;\n      &lt;ion-card-title&gt; {{ movie?.title }} &lt;\/ion-card-title&gt;\n      &lt;ion-card-subtitle&gt; {{ movie.tagline }} &lt;\/ion-card-subtitle&gt;\n      &lt;ion-text color=&quot;tertiary&quot;&gt;\n        @for (g of movie.genres; track g.id; let isLast = $last;) {\n        &lt;span&gt; {{ g.name }} {{ !isLast ? &#039;\u00b7&#039; : &#039;&#039; }}&lt;\/span&gt;\n        }\n      &lt;\/ion-text&gt;\n    &lt;\/ion-card-header&gt;\n    &lt;ion-card-content&gt;\n      &lt;ion-label color=&quot;medium&quot;&gt;{{ movie.overview }}&lt;\/ion-label&gt;\n\n      &lt;ion-item lines=&quot;none&quot;&gt;\n        &lt;ion-icon name=&quot;calendar-outline&quot; slot=&quot;start&quot;&gt;&lt;\/ion-icon&gt;\n        &lt;ion-label&gt;{{ movie.release_date | date: &#039;y&#039;}}&lt;\/ion-label&gt;\n      &lt;\/ion-item&gt;\n\n      &lt;ion-item lines=&quot;none&quot;&gt;\n        &lt;ion-icon name=&quot;cash-outline&quot; slot=&quot;start&quot;&gt;&lt;\/ion-icon&gt;\n        &lt;ion-label&gt;{{ movie.budget| currency: &#039;USD&#039; }}&lt;\/ion-label&gt;\n      &lt;\/ion-item&gt;\n    &lt;\/ion-card-content&gt;\n  &lt;\/ion-card&gt;\n  }\n&lt;\/ion-content&gt;<\/code><\/pre>\n\n\n\n<p>Hidden inside is also another iteration over the genres of the movie. As you can see, stuff like isLast is still available just like before!<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" data-src=\"https:\/\/lh7-us.googleusercontent.com\/AHCzoYetuhERbaecIr1KuceE9Xd5acrFbLWpjLIfo5bR4CkdQNa2Rimd5QUeIWVpdrw_n9y7_7dUYafLdI0erflHblxQ4eESrfGQrAdNYGtA8nMEdwZHrev9rfy5TadKKpVHbDAct5FiROxC3FPgD_A\" alt=\"\" src=\"data:image\/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\" class=\"lazyload\" \/><noscript><img decoding=\"async\" src=\"https:\/\/lh7-us.googleusercontent.com\/AHCzoYetuhERbaecIr1KuceE9Xd5acrFbLWpjLIfo5bR4CkdQNa2Rimd5QUeIWVpdrw_n9y7_7dUYafLdI0erflHblxQ4eESrfGQrAdNYGtA8nMEdwZHrev9rfy5TadKKpVHbDAct5FiROxC3FPgD_A\" alt=\"\"\/><\/noscript><\/figure>\n\n\n\n<p>That means, everything you learned about Angular in the past is still valuable and can be used with the new Angular 17 features.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Working with Deferred Blocks<\/strong><\/h2>\n\n\n\n<p>The last thing we want to do is to add a new page that uses a <a href=\"https:\/\/angular.io\/api\/core\/defer\">deferred block<\/a> &#8211; one of the coolest additions of Angular 17!<\/p>\n\n\n\n<p>A deferred block is a block of code that is only rendered when a condition is met.<\/p>\n\n\n\n<p>That means, we can use it to show a loading indicator or skeleton while we are loading data, and then show the actual data when it&#8217;s available.<\/p>\n\n\n\n<p><em>Ok, that\u2019s nothing new. We could do that before, right?<\/em><\/p>\n\n\n\n<p>Well, the great thing is that now the deferred template is not loaded until required!<\/p>\n\n\n\n<p>This means, the items will not appear in the DOM before, which can massively improve performance.<\/p>\n\n\n\n<p>There&#8217;s even a block <code>@loading<\/code> which describes the transition from empty state to the actual data, which makes it great for SSR. Let&#8217;s build a new page that uses a deferred block to show a list of trending movies, based on the code from our previous page.<\/p>\n\n\n\n<p>Bring up the <code>src\/app\/home-defer\/home-defer.page.html<\/code> file and change it to:<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-\">&lt;ion-header&gt;\n  &lt;ion-toolbar color=&quot;primary&quot;&gt;\n    &lt;ion-title&gt;Trending Movies&lt;\/ion-title&gt;\n  &lt;\/ion-toolbar&gt;\n&lt;\/ion-header&gt;\n\n&lt;ion-content&gt;\n  @defer (when !isLoading) {\n  &lt;ion-list&gt;\n    @for (item of movies; track item.id) {\n    &lt;ion-item button [routerLink]=&quot;[&#039;\/details&#039;, item.id]&quot;&gt;\n      &lt;ion-avatar slot=&quot;start&quot;&gt;\n        &lt;img [src]=&quot;imageBaseUrl + &#039;\/w92&#039; + item.poster_path&quot; alt=&quot;poster&quot; \/&gt;\n      &lt;\/ion-avatar&gt;\n\n      &lt;ion-label class=&quot;ion-text-wrap&quot;&gt;\n        &lt;h3&gt;{{ item.title }}&lt;\/h3&gt;\n        &lt;p&gt;{{ item.release_date | date:&#039;y&#039; }}&lt;\/p&gt;\n      &lt;\/ion-label&gt;\n\n      &lt;ion-badge slot=&quot;end&quot;&gt; {{ item.vote_average }} &lt;\/ion-badge&gt;\n    &lt;\/ion-item&gt;\n    } \n    @empty { \n      @if (!isLoading) {\n      &lt;ion-item lines=&quot;none&quot;&gt;\n        &lt;ion-label class=&quot;ion-text-center&quot;&gt;No movies found&lt;\/ion-label&gt;\n      &lt;\/ion-item&gt;\n      } \n    }\n  &lt;\/ion-list&gt;\n\n  &lt;ion-infinite-scroll (ionInfinite)=&quot;loadMore($event)&quot;&gt;\n    &lt;ion-infinite-scroll-content\n      loadingSpinner=&quot;bubbles&quot;\n      loadingText=&quot;Loading more data...&quot;\n    \/&gt;\n  &lt;\/ion-infinite-scroll&gt;\n  } \n  @placeholder {\n    &lt;ion-list class=&quot;ion-padding-top&quot;&gt;\n      @for (i of dummyArray; track i) {\n      &lt;ion-item lines=&quot;none&quot; class=&quot;ion-padding-bottom&quot;&gt;\n        &lt;ion-avatar slot=&quot;start&quot;&gt;\n          &lt;ion-skeleton-text&gt;&lt;\/ion-skeleton-text&gt;\n        &lt;\/ion-avatar&gt;\n        &lt;ion-skeleton-text animated style=&quot;height: 40px&quot; \/&gt;\n      &lt;\/ion-item&gt;\n    }\n  &lt;\/ion-list&gt;\n  } \n  @error {\n    &lt;ion-alert\n      header=&quot;Error&quot;\n      [message]=&quot;error&quot;\n      isOpen=&quot;true&quot;\n      [buttons]=&quot;[&#039;Ok&#039;]&quot;\n    \/&gt;\n  } \n  @loading(minimum 2s) { \n    Transition to list.... \n  }\n&lt;\/ion-content&gt;<\/code><\/pre>\n\n\n\n<p>It&#8217;s mostly the same, but we now use the <code>@defer<\/code> directive to only render the list and infinite scroll when we are not loading anymore.<\/p>\n\n\n\n<p>Additionally, we use the <code>@placeholder<\/code> directive to show the skeleton items while we are loading.<\/p>\n\n\n\n<p>The <code>@error<\/code> directive is used to show the error message, and the <code>@loading<\/code> directive is used to show a transition message while we go from placeholder to deferred block.<\/p>\n\n\n\n<p>Things like this make Angular 17 a great choice for building modern apps, and Ionic remains the most popular UI framework to build mobile apps with Angular and Capacitor!<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Conclusion<\/strong><\/h2>\n\n\n\n<p>In this tutorial, we learned how to use the new Angular Control Flow, Signals, and Deferred Blocks to build a modern Angular app with Ionic.We also learned how to use the new Ionic standalone components to only include the components we actually use in our app.<\/p>\n\n\n\n<p>Ionic is ready for the latest updates of Angular 17, and if you want to get the most performance out of your app, you should definitely give it a try and <a href=\"https:\/\/update.angular.io\/\">upgrade your apps<\/a> to the latest version!<\/p>\n\n\n\n<p>If you want to learn more about Ionic, make sure to check out <a href=\"https:\/\/ionicacademy.com\/\">the Ionic Academy<\/a>, an online school with 70+ video courses focused entirely on building awesome mobile apps with Ionic and <a href=\"https:\/\/capacitorjs.com\/\">Capacitor<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Learn to build a simple movie app that shows a list + details of trending movies with Ionic + Angular<\/p>\n","protected":false},"author":11,"featured_media":5790,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"publish_to_discourse":"1","publish_post_category":"4","wpdc_auto_publish_overridden":"","wpdc_topic_tags":"","wpdc_pin_topic":"","wpdc_pin_until":"","discourse_post_id":"571292","discourse_permalink":"http:\/\/forum.ionicframework.com\/t\/building-modern-angular-apps-with-ionic-and-standalone-components\/238451","wpdc_publishing_response":"success","wpdc_publishing_error":"","_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[1,124],"tags":[60,25],"class_list":["post-5789","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-all","category-tutorials","tag-angular","tag-tutorials"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v23.0 (Yoast SEO v23.0) - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Building Modern Angular Apps with Ionic and Standalone Components - Ionic Blog<\/title>\n<meta name=\"description\" content=\"This tutorial will show you how to build a simple movie app that shows a list + details of trending movies with Ionic + Angular\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/ionic.io\/blog\/building-angular-apps-with-ionic-and-standalone-components\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Building Modern Angular Apps with Ionic and Standalone Components\" \/>\n<meta property=\"og:description\" content=\"This tutorial will show you how to build a simple movie app that shows a list + details of trending movies with Ionic + Angular\" \/>\n<meta property=\"og:url\" content=\"https:\/\/ionic.io\/blog\/building-angular-apps-with-ionic-and-standalone-components\" \/>\n<meta property=\"og:site_name\" content=\"Ionic Blog\" \/>\n<meta property=\"article:published_time\" content=\"2023-12-14T16:08:43+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2023-12-14T19:47:34+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/12\/angular-feature-image.png\" \/>\n\t<meta property=\"og:image:width\" content=\"2240\" \/>\n\t<meta property=\"og:image:height\" content=\"1120\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"Simon Grimm\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@schlimmson\" \/>\n<meta name=\"twitter:site\" content=\"@ionicframework\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Simon Grimm\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"13 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/ionic.io\/blog\/building-angular-apps-with-ionic-and-standalone-components#article\",\"isPartOf\":{\"@id\":\"https:\/\/ionic.io\/blog\/building-angular-apps-with-ionic-and-standalone-components\"},\"author\":{\"name\":\"Simon Grimm\",\"@id\":\"https:\/\/ionic.io\/blog\/#\/schema\/person\/24d44b251756bd6488dcb741eec0bef6\"},\"headline\":\"Building Modern Angular Apps with Ionic and Standalone Components\",\"datePublished\":\"2023-12-14T16:08:43+00:00\",\"dateModified\":\"2023-12-14T19:47:34+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/ionic.io\/blog\/building-angular-apps-with-ionic-and-standalone-components\"},\"wordCount\":1687,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\/\/ionic.io\/blog\/#organization\"},\"image\":{\"@id\":\"https:\/\/ionic.io\/blog\/building-angular-apps-with-ionic-and-standalone-components#primaryimage\"},\"thumbnailUrl\":\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/12\/angular-feature-image.png\",\"keywords\":[\"Angular\",\"Tutorials\"],\"articleSection\":[\"All\",\"Tutorials\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/ionic.io\/blog\/building-angular-apps-with-ionic-and-standalone-components#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/ionic.io\/blog\/building-angular-apps-with-ionic-and-standalone-components\",\"url\":\"https:\/\/ionic.io\/blog\/building-angular-apps-with-ionic-and-standalone-components\",\"name\":\"Building Modern Angular Apps with Ionic and Standalone Components - Ionic Blog\",\"isPartOf\":{\"@id\":\"https:\/\/ionic.io\/blog\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/ionic.io\/blog\/building-angular-apps-with-ionic-and-standalone-components#primaryimage\"},\"image\":{\"@id\":\"https:\/\/ionic.io\/blog\/building-angular-apps-with-ionic-and-standalone-components#primaryimage\"},\"thumbnailUrl\":\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/12\/angular-feature-image.png\",\"datePublished\":\"2023-12-14T16:08:43+00:00\",\"dateModified\":\"2023-12-14T19:47:34+00:00\",\"description\":\"This tutorial will show you how to build a simple movie app that shows a list + details of trending movies with Ionic + Angular\",\"breadcrumb\":{\"@id\":\"https:\/\/ionic.io\/blog\/building-angular-apps-with-ionic-and-standalone-components#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/ionic.io\/blog\/building-angular-apps-with-ionic-and-standalone-components\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/ionic.io\/blog\/building-angular-apps-with-ionic-and-standalone-components#primaryimage\",\"url\":\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/12\/angular-feature-image.png\",\"contentUrl\":\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/12\/angular-feature-image.png\",\"width\":2240,\"height\":1120},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/ionic.io\/blog\/building-angular-apps-with-ionic-and-standalone-components#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/ionic.io\/blog\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Building Modern Angular Apps with Ionic and Standalone Components\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/ionic.io\/blog\/#website\",\"url\":\"https:\/\/ionic.io\/blog\/\",\"name\":\"ionic.io\/blog\",\"description\":\"Build amazing native and progressive web apps with the web\",\"publisher\":{\"@id\":\"https:\/\/ionic.io\/blog\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/ionic.io\/blog\/?s={search_term_string}\"},\"query-input\":\"required name=search_term_string\"}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/ionic.io\/blog\/#organization\",\"name\":\"Ionic\",\"url\":\"https:\/\/ionic.io\/blog\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/ionic.io\/blog\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2020\/10\/white-on-color.png\",\"contentUrl\":\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2020\/10\/white-on-color.png\",\"width\":1920,\"height\":854,\"caption\":\"Ionic\"},\"image\":{\"@id\":\"https:\/\/ionic.io\/blog\/#\/schema\/logo\/image\/\"},\"sameAs\":[\"https:\/\/x.com\/ionicframework\"]},{\"@type\":\"Person\",\"@id\":\"https:\/\/ionic.io\/blog\/#\/schema\/person\/24d44b251756bd6488dcb741eec0bef6\",\"name\":\"Simon Grimm\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/ionic.io\/blog\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2020\/08\/simon-150x150.jpg\",\"contentUrl\":\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2020\/08\/simon-150x150.jpg\",\"caption\":\"Simon Grimm\"},\"sameAs\":[\"https:\/\/x.com\/schlimmson\"],\"url\":\"https:\/\/ionic.io\/blog\/author\/schlimmson\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Building Modern Angular Apps with Ionic and Standalone Components - Ionic Blog","description":"This tutorial will show you how to build a simple movie app that shows a list + details of trending movies with Ionic + Angular","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/ionic.io\/blog\/building-angular-apps-with-ionic-and-standalone-components","og_locale":"en_US","og_type":"article","og_title":"Building Modern Angular Apps with Ionic and Standalone Components","og_description":"This tutorial will show you how to build a simple movie app that shows a list + details of trending movies with Ionic + Angular","og_url":"https:\/\/ionic.io\/blog\/building-angular-apps-with-ionic-and-standalone-components","og_site_name":"Ionic Blog","article_published_time":"2023-12-14T16:08:43+00:00","article_modified_time":"2023-12-14T19:47:34+00:00","og_image":[{"width":2240,"height":1120,"url":"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/12\/angular-feature-image.png","type":"image\/png"}],"author":"Simon Grimm","twitter_card":"summary_large_image","twitter_creator":"@schlimmson","twitter_site":"@ionicframework","twitter_misc":{"Written by":"Simon Grimm","Est. reading time":"13 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/ionic.io\/blog\/building-angular-apps-with-ionic-and-standalone-components#article","isPartOf":{"@id":"https:\/\/ionic.io\/blog\/building-angular-apps-with-ionic-and-standalone-components"},"author":{"name":"Simon Grimm","@id":"https:\/\/ionic.io\/blog\/#\/schema\/person\/24d44b251756bd6488dcb741eec0bef6"},"headline":"Building Modern Angular Apps with Ionic and Standalone Components","datePublished":"2023-12-14T16:08:43+00:00","dateModified":"2023-12-14T19:47:34+00:00","mainEntityOfPage":{"@id":"https:\/\/ionic.io\/blog\/building-angular-apps-with-ionic-and-standalone-components"},"wordCount":1687,"commentCount":0,"publisher":{"@id":"https:\/\/ionic.io\/blog\/#organization"},"image":{"@id":"https:\/\/ionic.io\/blog\/building-angular-apps-with-ionic-and-standalone-components#primaryimage"},"thumbnailUrl":"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/12\/angular-feature-image.png","keywords":["Angular","Tutorials"],"articleSection":["All","Tutorials"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/ionic.io\/blog\/building-angular-apps-with-ionic-and-standalone-components#respond"]}]},{"@type":"WebPage","@id":"https:\/\/ionic.io\/blog\/building-angular-apps-with-ionic-and-standalone-components","url":"https:\/\/ionic.io\/blog\/building-angular-apps-with-ionic-and-standalone-components","name":"Building Modern Angular Apps with Ionic and Standalone Components - Ionic Blog","isPartOf":{"@id":"https:\/\/ionic.io\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/ionic.io\/blog\/building-angular-apps-with-ionic-and-standalone-components#primaryimage"},"image":{"@id":"https:\/\/ionic.io\/blog\/building-angular-apps-with-ionic-and-standalone-components#primaryimage"},"thumbnailUrl":"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/12\/angular-feature-image.png","datePublished":"2023-12-14T16:08:43+00:00","dateModified":"2023-12-14T19:47:34+00:00","description":"This tutorial will show you how to build a simple movie app that shows a list + details of trending movies with Ionic + Angular","breadcrumb":{"@id":"https:\/\/ionic.io\/blog\/building-angular-apps-with-ionic-and-standalone-components#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/ionic.io\/blog\/building-angular-apps-with-ionic-and-standalone-components"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/ionic.io\/blog\/building-angular-apps-with-ionic-and-standalone-components#primaryimage","url":"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/12\/angular-feature-image.png","contentUrl":"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/12\/angular-feature-image.png","width":2240,"height":1120},{"@type":"BreadcrumbList","@id":"https:\/\/ionic.io\/blog\/building-angular-apps-with-ionic-and-standalone-components#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/ionic.io\/blog"},{"@type":"ListItem","position":2,"name":"Building Modern Angular Apps with Ionic and Standalone Components"}]},{"@type":"WebSite","@id":"https:\/\/ionic.io\/blog\/#website","url":"https:\/\/ionic.io\/blog\/","name":"ionic.io\/blog","description":"Build amazing native and progressive web apps with the web","publisher":{"@id":"https:\/\/ionic.io\/blog\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/ionic.io\/blog\/?s={search_term_string}"},"query-input":"required name=search_term_string"}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/ionic.io\/blog\/#organization","name":"Ionic","url":"https:\/\/ionic.io\/blog\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/ionic.io\/blog\/#\/schema\/logo\/image\/","url":"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2020\/10\/white-on-color.png","contentUrl":"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2020\/10\/white-on-color.png","width":1920,"height":854,"caption":"Ionic"},"image":{"@id":"https:\/\/ionic.io\/blog\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/x.com\/ionicframework"]},{"@type":"Person","@id":"https:\/\/ionic.io\/blog\/#\/schema\/person\/24d44b251756bd6488dcb741eec0bef6","name":"Simon Grimm","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/ionic.io\/blog\/#\/schema\/person\/image\/","url":"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2020\/08\/simon-150x150.jpg","contentUrl":"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2020\/08\/simon-150x150.jpg","caption":"Simon Grimm"},"sameAs":["https:\/\/x.com\/schlimmson"],"url":"https:\/\/ionic.io\/blog\/author\/schlimmson"}]}},"jetpack_sharing_enabled":true,"jetpack_featured_media_url":"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/12\/angular-feature-image.png","_links":{"self":[{"href":"https:\/\/ionic.io\/blog\/wp-json\/wp\/v2\/posts\/5789","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/ionic.io\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/ionic.io\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/ionic.io\/blog\/wp-json\/wp\/v2\/users\/11"}],"replies":[{"embeddable":true,"href":"https:\/\/ionic.io\/blog\/wp-json\/wp\/v2\/comments?post=5789"}],"version-history":[{"count":4,"href":"https:\/\/ionic.io\/blog\/wp-json\/wp\/v2\/posts\/5789\/revisions"}],"predecessor-version":[{"id":5799,"href":"https:\/\/ionic.io\/blog\/wp-json\/wp\/v2\/posts\/5789\/revisions\/5799"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/ionic.io\/blog\/wp-json\/wp\/v2\/media\/5790"}],"wp:attachment":[{"href":"https:\/\/ionic.io\/blog\/wp-json\/wp\/v2\/media?parent=5789"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/ionic.io\/blog\/wp-json\/wp\/v2\/categories?post=5789"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/ionic.io\/blog\/wp-json\/wp\/v2\/tags?post=5789"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}