Skip to content

docs(angular): show complete code context in the "Your First App" tutorial #4157

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

Open
wants to merge 38 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
9c0859d
docs(angular): add context to code blocks in your-first-app page
soundproofboot Jun 13, 2025
e979ea8
docs(angular): add context to code blocks and note to learn more abou…
soundproofboot Jun 13, 2025
67d1164
docs(angular): add context to method definitions, additional code blo…
soundproofboot Jun 13, 2025
ebb2a94
docs(angular): add context in code blocks, clarity, storage note in 4…
soundproofboot Jun 16, 2025
2660f4c
docs(angular): add context and clarity to code blocks in 5-adding-mob…
soundproofboot Jun 16, 2025
08cf534
docs(angular): ran linter to format changes to angular walk through
soundproofboot Jun 16, 2025
19727c5
docs(angular): update code comment
soundproofboot Jun 26, 2025
925be88
docs(angular): update code comment
soundproofboot Jun 26, 2025
8311b56
docs(angular): update code comment
soundproofboot Jun 26, 2025
dbba4e2
docs(angular): update code comment
soundproofboot Jun 26, 2025
6a93709
docs(angular): update code comment
soundproofboot Jun 26, 2025
6fb2f87
docs(angular): update code comment
soundproofboot Jun 26, 2025
0bf8977
docs(angular): update code comment
soundproofboot Jun 26, 2025
954c32b
docs(angular): update code comment
soundproofboot Jun 26, 2025
318444e
docs(angular): update code comment
soundproofboot Jun 26, 2025
2b7b188
docs(angular): update code comment
soundproofboot Jun 26, 2025
3a30866
docs(angular): update code comment
soundproofboot Jun 26, 2025
1a83529
docs(angular): update code comment
soundproofboot Jun 26, 2025
9c926b4
docs(angular): update code comment
soundproofboot Jun 26, 2025
082bdef
docs(angular): update code comment
soundproofboot Jun 26, 2025
ae805bb
docs(angular): update code comment
soundproofboot Jun 26, 2025
3e77409
docs(angular): remove link to ngFor docs
soundproofboot Jun 26, 2025
32fc888
docs(angular): update code block
soundproofboot Jun 26, 2025
c3ee323
docs(angular): add new line
soundproofboot Jun 26, 2025
22165d1
docs(angular): update code comment
soundproofboot Jun 26, 2025
9c6729f
docs(angular): update code comment
soundproofboot Jun 26, 2025
a5069fd
docs(angular): update code comment
soundproofboot Jun 26, 2025
35db01b
docs(angular): update language
soundproofboot Jun 26, 2025
16ade0c
docs(angular): remove code block
soundproofboot Jun 26, 2025
a21d5e8
docs(angular): update code blocks
soundproofboot Jun 26, 2025
effdaaa
docs(angular): update code blocks
soundproofboot Jun 26, 2025
2aeb4fb
docs(angular): update code blocks
soundproofboot Jun 26, 2025
20aa15b
docs(angular): update code blocks
soundproofboot Jun 26, 2025
872a89f
docs(angular): add context in localStorage and IndexedDB note
soundproofboot Jun 26, 2025
a2d98da
docs(angular): added code block
soundproofboot Jun 26, 2025
a180cad
docs(angular): add context to code blocks
soundproofboot Jun 26, 2025
977db91
docs(angular): truncate longer code blocks, fix typos
soundproofboot Jun 30, 2025
ff70a52
docs(angular): run linter
soundproofboot Jun 30, 2025
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
55 changes: 48 additions & 7 deletions docs/angular/your-first-app.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,10 +110,17 @@ npm install @ionic/pwa-elements
Next, import `@ionic/pwa-elements` by editing `src/main.ts`.

```tsx
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';
// CHANGE: Add the following import.
import { defineCustomElements } from '@ionic/pwa-elements/loader';

// Call the element loader before the bootstrapModule/bootstrapApplication call
defineCustomElements(window);

platformBrowserDynamic()
.bootstrapModule(AppModule)
.catch((err) => console.log(err));
```

That’s it! Now for the fun part - let’s see the app in action.
Expand All @@ -137,18 +144,20 @@ There are three tabs. Click on the Tab2 tab. It’s a blank canvas, aka the perf
Open the photo-gallery app folder in your code editor of choice, then navigate to `/src/app/tab2/tab2.page.html`. We see:

```html
<ion-header>
<ion-header [translucent]="true">
<ion-toolbar>
<ion-title>Tab 2</ion-title>
<ion-title> Tab 2 </ion-title>
</ion-toolbar>
</ion-header>

<ion-content>
<ion-content [fullscreen]="true">
<ion-header collapse="condense">
<ion-toolbar>
<ion-title size="large">Tab 2</ion-title>
</ion-toolbar>
</ion-header>

<app-explore-container name="Tab 2 page"></app-explore-container>
</ion-content>
```

Expand All @@ -161,22 +170,54 @@ Open the photo-gallery app folder in your code editor of choice, then navigate t
We put the visual aspects of our app into `<ion-content>`. In this case, it’s where we’ll add a button that opens the device’s camera as well as displays the image captured by the camera. Start by adding a [floating action button](https://ionicframework.com/docs/api/fab) (FAB) to the bottom of the page and set the camera image as the icon.

```html
<ion-header [translucent]="true">
<ion-toolbar>
<ion-title> Tab 2 </ion-title>
</ion-toolbar>
</ion-header>

<ion-content>
<ion-header collapse="condense">
<ion-toolbar>
<ion-title size="large">Tab 2</ion-title>
</ion-toolbar>
</ion-header>

<!-- CHANGE: Add the floating action button. -->
<ion-fab vertical="bottom" horizontal="center" slot="fixed">
<ion-fab-button>
<ion-icon name="camera"></ion-icon>
</ion-fab-button>
</ion-fab>

<!-- CHANGE: Remove or comment out `app-explore-container`. -->
<!-- <app-explore-container name="Tab 2 page"></app-explore-container> -->
</ion-content>
```

Next, open `src/app/tabs/tabs.page.html`. Change the label to “Photos” and the icon name to “images”:

```html
<ion-tab-button tab="tab2">
<ion-icon name="images"></ion-icon>
<ion-label>Photos</ion-label>
</ion-tab-button>
<ion-tabs>
<ion-tab-bar slot="bottom">
<ion-tab-button tab="tab1" href="/tabs/tab1">
<ion-icon aria-hidden="true" name="triangle"></ion-icon>
<ion-label>Tab 1</ion-label>
</ion-tab-button>

<ion-tab-button tab="tab2" href="/tabs/tab2">
<!-- CHANGE: Update icon. -->
<ion-icon aria-hidden="true" name="images"></ion-icon>
<!-- CHANGE: Update label. -->
<ion-label>Photos</ion-label>
</ion-tab-button>

<ion-tab-button tab="tab3" href="/tabs/tab3">
<ion-icon aria-hidden="true" name="square"></ion-icon>
<ion-label>Tab 3</ion-label>
</ion-tab-button>
</ion-tab-bar>
</ion-tabs>
```

Save all changes to see them automatically applied in the browser. That’s just the start of all the cool things we can do with Ionic. Up next, implement camera taking functionality on the web, then build it for iOS and Android.
135 changes: 122 additions & 13 deletions docs/angular/your-first-app/2-taking-photos.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,21 +24,40 @@ ionic g service services/photo
Open the new `services/photo.service.ts` file, and let’s add the logic that will power the camera functionality. First, import Capacitor dependencies and get references to the Camera, Filesystem, and Storage plugins:

```tsx
import { Injectable } from '@angular/core';
// CHANGE: Add the following imports.
import { Camera, CameraResultType, CameraSource, Photo } from '@capacitor/camera';
import { Filesystem, Directory } from '@capacitor/filesystem';
import { Preferences } from '@capacitor/preferences';

@Injectable({
providedIn: 'root',
})
export class PhotoService {
constructor() {}
}
```

Next, define a new class method, `addNewToGallery`, that will contain the core logic to take a device photo and save it to the filesystem. Let’s start by opening the device camera:

```tsx
public async addNewToGallery() {
// Take a photo
const capturedPhoto = await Camera.getPhoto({
resultType: CameraResultType.Uri,
source: CameraSource.Camera,
quality: 100
});
import { Injectable } from '@angular/core';
import { Camera, CameraResultType, CameraSource, Photo } from '@capacitor/camera';
import { Filesystem, Directory } from '@capacitor/filesystem';
import { Preferences } from '@capacitor/preferences';

export class PhotoService {
constructor() {}

// CHANGE: Add the gallery function.
public async addNewToGallery() {
// Take a photo
const capturedPhoto = await Camera.getPhoto({
resultType: CameraResultType.Uri,
source: CameraSource.Camera,
quality: 100,
});
}
}
```

Expand All @@ -47,20 +66,44 @@ Notice the magic here: there's no platform-specific code (web, iOS, or Android)!
Next, open up `tab2.page.ts` and import the PhotoService class and add a method that calls the `addNewToGallery` method on the imported service:

```tsx
import { Component } from '@angular/core';
import { PhotoService } from '../services/photo.service';

constructor(public photoService: PhotoService) { }

addPhotoToGallery() {
this.photoService.addNewToGallery();
@Component({
selector: 'app-tab2',
templateUrl: 'tab2.page.html',
styleUrls: ['tab2.page.scss'],
standalone: false,
})
export class Tab2Page {
// CHANGE: Update constructor to include `photoService`.
constructor(public photoService: PhotoService) {}

// CHANGE: Add `addNewToGallery` method.
addPhotoToGallery() {
this.photoService.addNewToGallery();
}
}
```

Then, open `tab2.page.html` and call the `addPhotoToGallery()` function when the FAB is tapped/clicked:

```html
<ion-header [translucent]="true">
<ion-toolbar>
<ion-title> Tab 2 </ion-title>
</ion-toolbar>
</ion-header>

<ion-content>
<ion-header collapse="condense">
<ion-toolbar>
<ion-title size="large">Tab 2</ion-title>
</ion-toolbar>
</ion-header>

<ion-fab vertical="bottom" horizontal="center" slot="fixed">
<!-- CHANGE: Add a click event listener to the floating action button. -->
<ion-fab-button (click)="addPhotoToGallery()">
<ion-icon name="camera"></ion-icon>
</ion-fab-button>
Expand All @@ -78,54 +121,120 @@ After taking a photo, it disappears right away. We need to display it within our

## Displaying Photos

Return to `photo.service.ts`.

Outside of the `PhotoService` class definition (the very bottom of the file), create a new interface, `UserPhoto`, to hold our photo metadata:

```tsx
export class PhotoService {
// Same old code from before.
}

// CHANGE: Add the interface.
export interface UserPhoto {
filepath: string;
webviewPath?: string;
}
```

Back at the top of the file, define an array of Photos, which will contain a reference to each photo captured with the Camera.
Above the constructor, define an array of `UserPhoto`, which will contain a reference to each photo captured with the Camera.

```tsx
export class PhotoService {
// CHANGE: Add the photos array.
public photos: UserPhoto[] = [];

constructor() {}

// other code
}
```

Over in the `addNewToGallery` function, add the newly captured photo to the beginning of the Photos array.

```tsx
public async addNewToGallery() {
const capturedPhoto = await Camera.getPhoto({
resultType: CameraResultType.Uri,
source: CameraSource.Camera,
quality: 100
});

// CHANGE: Add the new photo to the photos array.
this.photos.unshift({
filepath: "soon...",
webviewPath: capturedPhoto.webPath!
});
}
```

`photo.service.ts` should now look like this:

```tsx
import { Injectable } from '@angular/core';
import { Camera, CameraResultType, CameraSource, Photo } from '@capacitor/camera';
import { Filesystem, Directory } from '@capacitor/filesystem';
import { Preferences } from '@capacitor/preferences';

@Injectable({
providedIn: 'root',
})
export class PhotoService {
public photos: UserPhoto[] = [];
constructor() {}

public async addNewToGallery() {
const capturedPhoto = await Camera.getPhoto({
resultType: CameraResultType.Uri,
source: CameraSource.Camera,
quality: 100,
});

// add new photo to photos array
this.photos.unshift({
filepath: 'soon...',
webviewPath: capturedPhoto.webPath!,
});
}
}

export interface UserPhoto {
filepath: string;
webviewPath?: string;
}
```

Next, move over to `tab2.page.html` so we can display the image on the screen. Add a [Grid component](https://ionicframework.com/docs/api/grid) so that each photo will display nicely as photos are added to the gallery, and loop through each photo in the `PhotoServices`'s Photos array, adding an Image component (`<ion-img>`) for each. Point the `src` (source) at the photo’s path:

```html
<ion-header [translucent]="true">
<ion-toolbar>
<ion-title> Tab 2 </ion-title>
</ion-toolbar>
</ion-header>

<ion-content>
<ion-header collapse="condense">
<ion-toolbar>
<ion-title size="large">Tab 2</ion-title>
</ion-toolbar>
</ion-header>

<!-- CHANGE: Add a grid component to display the photos. -->
<ion-grid>
<ion-row>
<!-- CHANGE: Create a new column and image component for each photo. -->
<ion-col size="6" *ngFor="let photo of photoService.photos; index as position">
<ion-img [src]="photo.webviewPath"></ion-img>
</ion-col>
</ion-row>
</ion-grid>

<!-- ion-fab markup -->
<ion-fab vertical="bottom" horizontal="center" slot="fixed">
<ion-fab-button (click)="addPhotoToGallery()">
<ion-icon name="camera"></ion-icon>
</ion-fab-button>
</ion-fab>
</ion-content>
```

Expand Down
Loading