Skip to content

Commit 64f3b0a

Browse files
committed
feat(): create async redirect
1 parent bfa1d6d commit 64f3b0a

File tree

12 files changed

+215
-14
lines changed

12 files changed

+215
-14
lines changed

.github/copilot-guidelines.md

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# GitHub Copilot Custom Guidelines
2+
3+
You are an expert in TypeScript, Angular, and scalable web application development. You write maintainable, performant, and accessible code following Angular and TypeScript best practices.
4+
5+
## TypeScript Best Practices
6+
7+
- Use strict type checking
8+
- Prefer type inference when the type is obvious
9+
- Avoid the `any` type; use `unknown` when type is uncertain
10+
11+
## Angular Best Practices
12+
13+
- Always use standalone components over NgModules
14+
- Don't use explicit `standalone: true` (it is implied by default)
15+
- Use signals for state management
16+
- Implement lazy loading for feature routes
17+
- Use `NgOptimizedImage` for all static images.
18+
19+
## Components
20+
21+
- Keep components small and focused on a single responsibility
22+
- Use `input()` and `output()` functions instead of decorators
23+
- Use `computed()` for derived state
24+
- Set `changeDetection: ChangeDetectionStrategy.OnPush` in `@Component` decorator
25+
- Prefer inline templates for small components
26+
- Prefer Reactive forms instead of Template-driven ones
27+
- Do NOT use `ngClass`, use `class` bindings instead
28+
- DO NOT use `ngStyle`, use `style` bindings instead
29+
30+
## State Management
31+
32+
- Use signals for local component state
33+
- Use `computed()` for derived state
34+
- Keep state transformations pure and predictable
35+
36+
## Templates
37+
38+
- Keep templates simple and avoid complex logic
39+
- Use native control flow (`@if`, `@for`, `@switch`) instead of `*ngIf`, `*ngFor`, `*ngSwitch`
40+
- Use the async pipe to handle observables
41+
42+
## Services
43+
44+
- Design services around a single responsibility
45+
- Use the `providedIn: 'root'` option for singleton services
46+
- Use the `inject()` function instead of constructor injection
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { ChangeDetectionStrategy, Component } from '@angular/core';
2+
import { RouterLink } from '@angular/router';
3+
4+
@Component({
5+
selector: 'app-page-a',
6+
template: `
7+
<button
8+
class="mb-4 rounded bg-gray-400 px-4 py-2 text-white"
9+
routerLink="/">
10+
Back
11+
</button>
12+
<h2>Page A (Admin)</h2>
13+
`,
14+
15+
changeDetection: ChangeDetectionStrategy.OnPush,
16+
imports: [RouterLink],
17+
})
18+
export class AdminPage {}

apps/angular/60-async-redirect/src/app/app.component.ts

Lines changed: 0 additions & 9 deletions
This file was deleted.

apps/angular/60-async-redirect/src/app/app.config.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,14 @@ import {
33
provideBrowserGlobalErrorListeners,
44
provideZoneChangeDetection,
55
} from '@angular/core';
6+
import { provideRouter } from '@angular/router';
7+
8+
import { routes } from './routes';
69

710
export const appConfig: ApplicationConfig = {
811
providers: [
912
provideBrowserGlobalErrorListeners(),
1013
provideZoneChangeDetection({ eventCoalescing: true }),
14+
provideRouter(routes),
1115
],
1216
};
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { ChangeDetectionStrategy, Component } from '@angular/core';
2+
import { RouterOutlet } from '@angular/router';
3+
4+
@Component({
5+
selector: 'app-root',
6+
template: `
7+
<router-outlet />
8+
`,
9+
changeDetection: ChangeDetectionStrategy.OnPush,
10+
imports: [RouterOutlet],
11+
})
12+
export class App {}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { ChangeDetectionStrategy, Component, inject } from '@angular/core';
2+
import { Router, RouterLink, RouterOutlet } from '@angular/router';
3+
import { UserProfileService } from './user-profile.service';
4+
5+
@Component({
6+
selector: 'app-dashboard',
7+
template: `
8+
<div class="mb-4 flex gap-2">
9+
<button
10+
class="rounded border border-black bg-white px-4 py-2 text-black hover:bg-gray-200"
11+
routerLink="/profile">
12+
Profile Page
13+
</button>
14+
<button
15+
class="rounded border border-black bg-white px-4 py-2 text-black hover:bg-gray-200"
16+
(click)="navigate()">
17+
User Page
18+
</button>
19+
</div>
20+
<router-outlet />
21+
`,
22+
changeDetection: ChangeDetectionStrategy.OnPush,
23+
imports: [RouterOutlet, RouterLink],
24+
})
25+
export class Dashboard {
26+
private router = inject(Router);
27+
private userProfile = inject(UserProfileService);
28+
29+
navigate() {
30+
this.userProfile.getProfile().subscribe((profile) => {
31+
void this.router.navigate(['/', profile]);
32+
});
33+
}
34+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import {
2+
ChangeDetectionStrategy,
3+
Component,
4+
inject,
5+
signal,
6+
} from '@angular/core';
7+
import { RouterLink } from '@angular/router';
8+
import { UserProfileService } from './user-profile.service';
9+
10+
@Component({
11+
selector: 'app-profile-page',
12+
template: `
13+
<button
14+
class="mb-4 rounded bg-gray-400 px-4 py-2 text-white"
15+
routerLink="/">
16+
Back
17+
</button>
18+
<div class="mb-4 flex gap-2">
19+
<button
20+
class="rounded border border-black bg-white px-4 py-2 text-black hover:bg-gray-200"
21+
(click)="chooseProfile('admin')">
22+
Choose Admin Profile
23+
</button>
24+
<button
25+
class="rounded border border-black bg-white px-4 py-2 text-black hover:bg-gray-200"
26+
(click)="chooseProfile('user')">
27+
Choose User Profile
28+
</button>
29+
</div>
30+
<div class="text-lg font-semibold">
31+
Current profile: {{ selectedProfile() }}
32+
</div>
33+
`,
34+
changeDetection: ChangeDetectionStrategy.OnPush,
35+
imports: [RouterLink],
36+
})
37+
export class ProfilePage {
38+
private userProfile = inject(UserProfileService);
39+
selectedProfile = signal<'admin' | 'user'>('admin');
40+
41+
chooseProfile(profile: 'admin' | 'user') {
42+
this.userProfile.setProfile(profile);
43+
this.selectedProfile.set(profile);
44+
}
45+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { Routes } from '@angular/router';
2+
import { AdminPage } from './admin-page';
3+
import { App } from './app';
4+
import { Dashboard } from './dashboard';
5+
import { ProfilePage } from './profile-page';
6+
import { UserPage } from './user-page';
7+
8+
export const routes: Routes = [
9+
{
10+
path: '',
11+
component: App,
12+
children: [
13+
{ path: '', pathMatch: 'full', component: Dashboard },
14+
{ path: 'profile', component: ProfilePage },
15+
{ path: 'admin', component: AdminPage },
16+
{ path: 'user', component: UserPage },
17+
],
18+
},
19+
{ path: '**', redirectTo: '', pathMatch: 'full' },
20+
];
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { ChangeDetectionStrategy, Component } from '@angular/core';
2+
import { RouterLink } from '@angular/router';
3+
4+
@Component({
5+
selector: 'app-page-b',
6+
template: `
7+
<button
8+
class="mb-4 rounded bg-gray-400 px-4 py-2 text-white"
9+
routerLink="/">
10+
Back
11+
</button>
12+
<h2>User page</h2>
13+
`,
14+
changeDetection: ChangeDetectionStrategy.OnPush,
15+
imports: [RouterLink],
16+
})
17+
export class UserPage {}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { Injectable, signal } from '@angular/core';
2+
import { Observable, of } from 'rxjs';
3+
import { delay } from 'rxjs/operators';
4+
5+
@Injectable({ providedIn: 'root' })
6+
export class UserProfileService {
7+
private profile = signal<'admin' | 'user'>('admin');
8+
9+
setProfile(profile: 'admin' | 'user') {
10+
this.profile.set(profile);
11+
}
12+
13+
getProfile(): Observable<'admin' | 'user'> {
14+
return of(this.profile()).pipe(delay(300));
15+
}
16+
}

0 commit comments

Comments
 (0)