Skip to content

Commit e62ea99

Browse files
committed
Angular Example for Template Hierarchy and Data Fetching
1 parent b35681e commit e62ea99

File tree

21 files changed

+123
-362
lines changed

21 files changed

+123
-362
lines changed
Lines changed: 8 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,59 +1,18 @@
1-
# MyApp
1+
# Template Hierarchy and Data Fetching
22

33
This project was generated using [Angular CLI](https://github.com/angular/angular-cli) version 20.0.5.
44

55
## Development server
66

77
To start a local development server, run:
88

9-
```bash
10-
ng serve
11-
```
9+
`npm run backend:start` - starts the backend server for template fetching at http://localhost:3000/api/templates
1210

13-
Once the server is running, open your browser and navigate to `http://localhost:4200/`. The application will automatically reload whenever you modify any of the source files.
14-
15-
## Code scaffolding
16-
17-
Angular CLI includes powerful code scaffolding tools. To generate a new component, run:
18-
19-
```bash
20-
ng generate component component-name
21-
```
22-
23-
For a complete list of available schematics (such as `components`, `directives`, or `pipes`), run:
24-
25-
```bash
26-
ng generate --help
27-
```
28-
29-
## Building
30-
31-
To build the project run:
32-
33-
```bash
34-
ng build
35-
```
11+
`npm run example:start` - starts WordPress and Angular project
3612

37-
This will compile your project and store the build artifacts in the `dist/` directory. By default, the production build optimizes your application for performance and speed.
38-
39-
## Running unit tests
40-
41-
To execute unit tests with the [Karma](https://karma-runner.github.io) test runner, use the following command:
42-
43-
```bash
44-
ng test
45-
```
46-
47-
## Running end-to-end tests
48-
49-
For end-to-end (e2e) testing, run:
50-
51-
```bash
52-
ng e2e
53-
```
54-
55-
Angular CLI does not come with an end-to-end testing framework by default. You can choose one that suits your needs.
56-
57-
## Additional Resources
13+
Once the server is running, open your browser and navigate to `http://localhost:4200/`. The application will automatically reload whenever you modify any of the source files.
5814

59-
For more information on using the Angular CLI, including detailed command references, visit the [Angular CLI Overview and Command Reference](https://angular.dev/tools/cli) page.
15+
## FAQ
16+
1. I get "Page Not Found
17+
Sorry, the page you are looking for does not exist. Please check the URL." when opening the Angular app.
18+
- Run `npm run backend:start` and verify that http://localhost:3000/api/templates returns correct data
Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,17 @@
11
import { RenderMode, ServerRoute } from '@angular/ssr';
22

3+
//https://angular.dev/guide/ssr
34
export const serverRoutes: ServerRoute[] = [
45
{
5-
path: '**',
6+
path: '',
7+
renderMode: RenderMode.Client,
8+
},
9+
{
10+
path: 'about',
611
renderMode: RenderMode.Prerender,
712
},
13+
{
14+
path: '**',
15+
renderMode: RenderMode.Server,
16+
},
817
];

examples/angular/template-hierarchy-data-fetching/example-app/src/app/app.routes.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,7 @@ import { Routes } from '@angular/router';
22
import { DynamicContentComponent } from './components/dynamic-content/dynamic-content.component';
33

44
export const routes: Routes = [
5+
{ path: '', component: DynamicContentComponent },
6+
{ path: 'about', component: DynamicContentComponent },
57
{ path: '**', component: DynamicContentComponent },
68
];

examples/angular/template-hierarchy-data-fetching/example-app/src/app/components/comments/comment-form/comment-form.component.ts

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -180,18 +180,4 @@ export class CommentFormComponent {
180180
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
181181
return emailRegex.test(email);
182182
}
183-
184-
/**
185-
* Simple URL validation (if provided)
186-
*/
187-
private isValidUrl(url: string): boolean {
188-
if (!url.trim()) return true; // Empty URL is valid (optional field)
189-
190-
try {
191-
new URL(url);
192-
return true;
193-
} catch {
194-
return false;
195-
}
196-
}
197183
}

examples/angular/template-hierarchy-data-fetching/example-app/src/app/components/comments/comment-thread/comment-thread.component.ts

Lines changed: 3 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,7 @@
11
import { Component, Input, Output, EventEmitter } from '@angular/core';
22
import { CommonModule } from '@angular/common';
3-
4-
export interface Comment {
5-
id: string;
6-
content: string;
7-
author: {
8-
node: {
9-
name: string;
10-
url?: string;
11-
avatar?: {
12-
url: string;
13-
};
14-
};
15-
};
16-
date: string;
17-
parentId?: string;
18-
replies?: Comment[];
19-
}
3+
import { formatDate } from '../../../utils/utils';
4+
import { Comment } from '../../../interfaces/comment.interface';
205

216
@Component({
227
selector: 'app-comment-thread',
@@ -29,40 +14,21 @@ export class CommentThreadComponent {
2914
@Input() comment!: Comment;
3015
@Output() reply = new EventEmitter<{ author: string; parentId: string }>();
3116

32-
/**
33-
* Handle reply button click
34-
*/
3517
onReply(): void {
3618
this.reply.emit({
3719
author: this.comment.author.node.name,
3820
parentId: this.comment.id,
3921
});
4022
}
4123

42-
/**
43-
* Handle nested reply events
44-
*/
4524
onNestedReply(replyData: { author: string; parentId: string }): void {
4625
this.reply.emit(replyData);
4726
}
4827

49-
/**
50-
* Format date for display
51-
*/
5228
formatDate(dateString: string): string {
53-
const date = new Date(dateString);
54-
return date.toLocaleDateString('en-US', {
55-
year: 'numeric',
56-
month: 'short',
57-
day: 'numeric',
58-
hour: '2-digit',
59-
minute: '2-digit',
60-
});
29+
return formatDate(dateString);
6130
}
6231

63-
/**
64-
* Get initials from name for avatar placeholder
65-
*/
6632
getInitials(name: string): string {
6733
return name
6834
.split(' ')
@@ -72,9 +38,6 @@ export class CommentThreadComponent {
7238
.toUpperCase();
7339
}
7440

75-
/**
76-
* Track by function for *ngFor performance
77-
*/
7841
trackByCommentId(index: number, comment: Comment): string {
7942
return comment.id;
8043
}

examples/angular/template-hierarchy-data-fetching/example-app/src/app/components/comments/comments.component.ts

Lines changed: 26 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,14 @@ import { Component, Input, OnInit, signal, computed } from '@angular/core';
22
import { CommonModule } from '@angular/common';
33
import { CommentFormComponent } from './comment-form/comment-form.component';
44
import { CommentThreadComponent } from './comment-thread/comment-thread.component';
5-
import { GraphQLService, gql } from '../../utils/graphql.service';
5+
import { GraphQLService, gql, fetchGraphQL } from '../../utils/graphql.service';
66
import { EmptyStateComponent } from '../empty-state/empty-state.component';
77
import { LoadingComponent } from '../loading/loading.component';
88
import {
99
Comment,
1010
ReplyData,
11-
PageInfo,
11+
CommentPageInfo,
12+
CommentResponse,
1213
} from '../../interfaces/comment.interface';
1314

1415
@Component({
@@ -34,7 +35,7 @@ export class CommentsComponent implements OnInit {
3435
error = signal<any>(null);
3536

3637
allComments = signal<Comment[]>([]);
37-
pageInfo = signal<PageInfo>({ hasNextPage: false, endCursor: null });
38+
pageInfo = signal<CommentPageInfo>({ hasNextPage: false, endCursor: null });
3839
loadingMore = signal<boolean>(false);
3940

4041
replyData = signal<ReplyData | null>(null);
@@ -59,44 +60,28 @@ export class CommentsComponent implements OnInit {
5960
}
6061

6162
private async loadInitialComments(): Promise<void> {
62-
try {
63-
this.loading.set(true);
64-
this.error.set(null);
65-
66-
const query = this.getCommentsQuery(this.contentType);
67-
const variables = {
68-
postId: this.postId,
69-
first: this.commentsPerPage,
70-
after: null, // Always null for initial load
71-
};
72-
73-
return new Promise((resolve, reject) => {
74-
this.graphqlService.query<any>(query, variables).subscribe({
75-
next: (response) => {
76-
this.data.set(response);
77-
78-
const contentData = response[this.contentType];
79-
if (contentData?.comments?.nodes) {
80-
this.allComments.set([...contentData.comments.nodes]);
81-
this.pageInfo.set(contentData.comments.pageInfo);
82-
}
83-
84-
this.loading.set(false);
85-
resolve();
86-
},
87-
error: (error) => {
88-
console.error('❌ Error loading comments:', error);
89-
this.error.set(error);
90-
this.loading.set(false);
91-
reject(error);
92-
},
93-
});
63+
this.loading.set(true);
64+
this.error.set(null);
65+
66+
fetchGraphQL<CommentResponse>(this.getCommentsQuery(this.contentType), {
67+
postId: this.postId,
68+
first: this.commentsPerPage,
69+
after: null,
70+
})
71+
.then((response) => {
72+
this.data.set(response);
73+
const contentData = (response as any)[this.contentType];
74+
if (contentData?.comments?.nodes) {
75+
this.allComments.set([...contentData.comments.nodes]);
76+
this.pageInfo.set(contentData.comments.pageInfo);
77+
}
78+
})
79+
.catch((error) => {
80+
this.error.set(error);
81+
})
82+
.finally(() => {
83+
this.loading.set(false);
9484
});
95-
} catch (error) {
96-
console.error('Error loading comments:', error);
97-
this.error.set(error);
98-
this.loading.set(false);
99-
}
10085
}
10186

10287
private getCommentsQuery(contentType: string): string {
@@ -216,7 +201,7 @@ export class CommentsComponent implements OnInit {
216201
}
217202

218203
handleCommentSubmit(commentData: any): void {
219-
console.log('Comment submitted:', commentData);
204+
//console.log('Comment submitted:', commentData);
220205
}
221206

222207
/**

examples/angular/template-hierarchy-data-fetching/example-app/src/app/components/dynamic-content/dynamic-content.component.html

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,3 @@
1-
<!-- Debug info (remove in production) -->
2-
<div class="debug-info" *ngIf="false">
3-
<pre>{{ getDebugInfo() | json }}</pre>
4-
</div>
5-
61
<!-- Loading state -->
72
<ng-container *ngIf="isLoading()">
83
<app-loading></app-loading>

0 commit comments

Comments
 (0)