Skip to content

Commit

Permalink
Angular-data-snippets (#8166)
Browse files Browse the repository at this point in the history
* remove reference of renameModelFields

* add code examples for angular

* add angular specific code examples

* add angular and vue examples

* update vue example

* add angular example for polly

* add angular example for bedrock

* add angular example for rekognition

* remove complete example from angular and vue pages
  • Loading branch information
chrisbonifacio authored Jan 14, 2025
1 parent e8599e5 commit 3ff3f21
Show file tree
Hide file tree
Showing 7 changed files with 436 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ When deploying your app to production, you need to [add the database connection

## Rename generated models and fields

To improve the ergonomics of your API, you might want to rename the generate fields or types to better accommodate your use case. Use the `renameModels()` and `renameModelFields()` modifiers to rename the auto-inferred data models and their fields.
To improve the ergonomics of your API, you might want to rename the generate fields or types to better accommodate your use case. Use the `renameModels()` modifier to rename the auto-inferred data models.

```ts
// Rename models or fields to be more idiomatic for frontend code
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,7 @@ Amplify.configure(outputs);

Example frontend code to create an audio buffer for playback using a text input.

<InlineFilter filters={["react", "javascript", "nextjs", "react-native"]}>
```ts title="App.tsx"
import "./App.css";
import { generateClient } from "aws-amplify/api";
Expand Down Expand Up @@ -267,4 +268,53 @@ function App() {

export default App;
```
</InlineFilter>

<InlineFilter filters={["angular"]}>
```ts title="app.component.ts"
import type { Schema } from '../../../amplify/data/resource';
import { Component } from '@angular/core';
import { generateClient } from 'aws-amplify/api';
import { getUrl } from 'aws-amplify/storage';

const client = generateClient<Schema>();

type PollyReturnType = Schema['convertTextToSpeech']['returnType'];

@Component({
selector: 'app-root',
template: `
<div class="flex flex-col">
<button (click)="synthesize()">Synth</button>
<button (click)="fetchAudio()">Fetch audio</button>
<a [href]="src">Get audio file</a>
</div>
`,
styleUrls: ['./app.component.css'],
})
export class App {
src: string = '';
file: PollyReturnType = '';

async synthesize() {
const { data, errors } = await client.mutations.convertTextToSpeech({
text: 'Hello World!',
});

if (!errors && data) {
this.file = data;
} else {
console.log(errors);
}
}

async fetchAudio() {
const res = await getUrl({
path: 'public/' + this.file,
});

this.src = res.url.toString();
}
}
```
</InlineFilter>
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,7 @@ Amplify.configure(outputs);

This code sets up a React app to upload an image to an S3 bucket and then use Amazon Rekognition to recognize the text in the uploaded image.

<InlineFilter filters={["react", "javascript", "nextjs", "react-native"]}>
```ts title="App.tsx"
import { type ChangeEvent, useState } from "react";
import { generateClient } from "aws-amplify/api";
Expand Down Expand Up @@ -282,3 +283,71 @@ function App() {

export default App;
```
</InlineFilter>
<InlineFilter filters={["angular"]}>
```ts title="app.component.ts"
import type { Schema } from '../../../amplify/data/resource';
import { Component } from '@angular/core';
import { generateClient } from 'aws-amplify/api';
import { uploadData } from 'aws-amplify/storage';
import { CommonModule } from '@angular/common';

// Generating the client
const client = generateClient<Schema>();

type IdentifyTextReturnType = Schema['identifyText']['returnType'];

@Component({
selector: 'app-text-recognition',
standalone: true,
imports: [CommonModule],
template: `
<div>
<h1>Amazon Rekognition Text Recognition</h1>
<div>
<input type="file" (change)="handleTranslate($event)" />
<button (click)="recognizeText()">Recognize Text</button>
<div>
<h3>Recognized Text:</h3>
{{ textData }}
</div>
</div>
</div>
`,
})
export class TodosComponent {
// Component properties instead of React state
path: string = '';
textData?: IdentifyTextReturnType;

// Function to handle file upload to S3 bucket
async handleTranslate(event: Event) {
const target = event.target as HTMLInputElement;
if (target.files && target.files.length > 0) {
const file = target.files[0];
const s3Path = 'public/' + file.name;

try {
await uploadData({
path: s3Path,
data: file,
});

this.path = s3Path;
} catch (error) {
console.error(error);
}
}
}

// Function to recognize text from the uploaded image
async recognizeText() {
// Identifying text in the uploaded image
const { data } = await client.queries.identifyText({
path: this.path, // File name
});
this.textData = data;
}
}
```
</InlineFilter>
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,7 @@ const { data, errors } = await client.queries.generateHaiku({

Here's an example of a simple UI that prompts a generative AI model to create a haiku based on user input:

<InlineFilter filters={["react", "javascript", "nextjs", "react-native", "vue"]}>
```tsx title="App.tsx"
import type { Schema } from '@/amplify/data/resource';
import type { FormEvent } from 'react';
Expand Down Expand Up @@ -402,6 +403,65 @@ export default function App() {
);
}
```
</InlineFilter>

<InlineFilter filters={['angular']}>
```ts title="app.component.ts"
import type { Schema } from '../../../amplify/data/resource';
import { Component } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { Amplify } from 'aws-amplify';
import { generateClient } from 'aws-amplify/api';
import outputs from '../../../amplify_outputs.json';

Amplify.configure(outputs);

const client = generateClient<Schema>();

@Component({
selector: 'app-haiku',
standalone: true,
imports: [FormsModule],
template: `
<main
class="flex min-h-screen flex-col items-center justify-center p-24 dark:text-white"
>
<div>
<h1 class="text-3xl font-bold text-center mb-4">Haiku Generator</h1>
<form class="mb-4 self-center max-w-[500px]" (ngSubmit)="sendPrompt()">
<input
class="text-black p-2 w-full"
placeholder="Enter a prompt..."
name="prompt"
[(ngModel)]="prompt"
/>
</form>
<div class="text-center">
<pre>{{ answer }}</pre>
</div>
</div>
</main>
`,
})
export class HaikuComponent {
prompt: string = '';
answer: string | null = null;

async sendPrompt() {
const { data, errors } = await client.queries.generateHaiku({
prompt: this.prompt,
});

if (!errors) {
this.answer = data;
this.prompt = '';
} else {
console.log(errors);
}
}
}
```
</InlineFilter>

![A webpage titled "Haiku Generator" and input field. "Frank Herbert's Dune" is entered and submitted. Shortly after, a haiku is rendered to the page.](/images/haiku-generator.gif)

Expand Down
103 changes: 102 additions & 1 deletion src/pages/[platform]/build-a-backend/data/query-data/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,8 @@ const {
});
```

<InlineFilter filters={["react", "javascript", "nextjs", "react-native"]}>

If you're building a React application, you can use the `usePagination` hook in Amplify UI to help with managing the pagination user experience.

```js
Expand Down Expand Up @@ -187,6 +189,8 @@ export const PaginationHasMorePagesExample = () => {
};
```

</InlineFilter>

<Callout>

**Limitations:**
Expand Down Expand Up @@ -214,7 +218,11 @@ const { data: blogWithSubsetOfData, errors } = await client.models.Blog.get(

## TypeScript type helpers for Amplify Data

When using TypeScript, you frequently need to specify data model types for type generics. For instance, with React's `useState`, you provide a type in TypeScript to ensure type-safety in your component code using the state. Use the `Schema["MODEL_NAME"]["type"]` pattern to get TypeScript types for the shapes of data models returned from the backend API. This allows you to get consumable TypeScript types for the shapes of the data model return values coming from the backend API.
When using TypeScript, you frequently need to specify data model types for type generics.

<InlineFilter filters={["react", "javascript", "nextjs", "react-native"]}>

For instance, with React's `useState`, you provide a type in TypeScript to ensure type-safety in your component code using the state. Use the `Schema["MODEL_NAME"]["type"]` pattern to get TypeScript types for the shapes of data models returned from the backend API.

```ts
import { type Schema } from '@/amplify/data/resource';
Expand All @@ -224,8 +232,21 @@ type Post = Schema['Post']['type'];
const [posts, setPosts] = useState<Post[]>([]);
```

</InlineFilter>

<InlineFilter filters={["angular", "vue"]}>

```ts
import { type Schema } from '../../../amplify/data/resource';

type Post = Schema['Post']['type'];
```

</InlineFilter>

You can combine the `Schema["MODEL_NAME"]["type"]` type with the `SelectionSet` helper type to describe the return type of API requests using the `selectionSet` parameter:

<InlineFilter filters={["react", "javascript", "nextjs", "react-native"]}>
```ts
import type { SelectionSet } from 'aws-amplify/data';
import type { Schema } from '../amplify/data/resource';
Expand All @@ -245,6 +266,86 @@ const fetchPosts = async () => {
}
```

</InlineFilter>

<InlineFilter filters={['vue']}>
```ts
<script setup lang="ts">
import type { Schema } from '../../../amplify/data/resource';
import { ref, onMounted } from 'vue';
import { generateClient, type SelectionSet } from 'aws-amplify/data';

const client = generateClient<Schema>();

const selectionSet = ['content', 'blog.author.*', 'comments.*'] as const;

type PostWithComments = SelectionSet<
Schema['Post']['type'],
typeof selectionSet
>;

const posts = ref<PostWithComments[]>([]);

const fetchPosts = async (): Promise<void> => {
const { data: postsWithComments } = await client.models.Post.list({
selectionSet,
});
posts.value = postsWithComments;
};

onMounted(() => {
fetchPosts();
});
</script>

<template v-for="post in posts" :key="post.id">
<li>{{ post.content }}</li>
</template>
```
</InlineFilter>

<InlineFilter filters={["angular"]}>
```ts
import type { Schema } from '../../../amplify/data/resource';
import { Component, OnInit } from '@angular/core';
import { generateClient, type SelectionSet } from 'aws-amplify/data';
import { CommonModule } from '@angular/common';

const client = generateClient<Schema>();

const selectionSet = ['content', 'blog.author.*', 'comments.*'] as const;

type PostWithComments = SelectionSet<
Schema['Post']['type'],
typeof selectionSet
>;

@Component({
selector: 'app-todos',
standalone: true,
imports: [CommonModule],
templateUrl: './todos.component.html',
styleUrls: ['./todos.component.css'],
})
export class TodosComponent implements OnInit {
posts: PostWithComments[] = [];

constructor() {}

ngOnInit(): void {
this.fetchPosts();
}

async fetchPosts(): Promise<void> {
const { data: postsWithComments } = await client.models.Post.list({
selectionSet,
});
this.posts = postsWithComments;
}
}
```
</InlineFilter>

## Cancel read requests

You can cancel any query API request by calling `.cancel` on the query request promise that's returned by `.list(...)` or `.get(...)`.
Expand Down
Loading

0 comments on commit 3ff3f21

Please sign in to comment.