feat: add header component with navigation and cart functionality
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
<app-header></app-header>
|
||||
|
||||
<main class="app-content">
|
||||
<router-outlet></router-outlet>
|
||||
</main>
|
||||
|
||||
+2
-1
@@ -1,9 +1,10 @@
|
||||
import { Component, signal } from '@angular/core';
|
||||
import { RouterOutlet } from '@angular/router';
|
||||
import {Header} from './layout/header/header';
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
imports: [RouterOutlet],
|
||||
imports: [RouterOutlet, Header],
|
||||
templateUrl: './app.html',
|
||||
styleUrl: './app.scss'
|
||||
})
|
||||
|
||||
@@ -20,6 +20,8 @@ export class CartService {
|
||||
const response = await firstValueFrom(
|
||||
this.http.post<AddCartResponse>(`${API_BASE_URL}/cart`, request)
|
||||
);
|
||||
|
||||
//Note: can return always 1 because server has not SameSite=None. This will be taken as a third cookie, and the browser can ignore it.
|
||||
this.setCount(response.count);
|
||||
return response.count;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
<mat-toolbar color="primary" class="app-header">
|
||||
<a routerLink="/" class="app-header__brand">
|
||||
<mat-icon>smartphone</mat-icon>
|
||||
<span>Mobile Store</span>
|
||||
</a>
|
||||
|
||||
<nav class="app-header__breadcrumb" aria-label="breadcrumb">
|
||||
<a routerLink="/">Home</a>
|
||||
@if (isProductDetail()) {
|
||||
<span> / Product Detail</span>
|
||||
}
|
||||
</nav>
|
||||
|
||||
<span class="app-header__spacer"></span>
|
||||
|
||||
<button
|
||||
mat-icon-button
|
||||
[matBadge]="cartCount()"
|
||||
[matBadgeHidden]="cartCount() === 0"
|
||||
matBadgeColor="accent"
|
||||
aria-label="Cart"
|
||||
>
|
||||
<mat-icon>shopping_cart</mat-icon>
|
||||
</button>
|
||||
</mat-toolbar>
|
||||
@@ -0,0 +1,32 @@
|
||||
.app-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1.5rem;
|
||||
|
||||
&__brand {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
font-weight: 600;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
&__breadcrumb {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.25rem;
|
||||
font-size: 0.9rem;
|
||||
opacity: 0.9;
|
||||
|
||||
a {
|
||||
color: inherit;
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
||||
&__spacer {
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { Header } from './header';
|
||||
|
||||
describe('Header', () => {
|
||||
let component: Header;
|
||||
let fixture: ComponentFixture<Header>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [Header]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(Header);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,33 @@
|
||||
import { Component, inject } from '@angular/core';
|
||||
import { toSignal } from '@angular/core/rxjs-interop';
|
||||
import { NavigationEnd, Router, RouterLink } from '@angular/router';
|
||||
import { filter, map } from 'rxjs';
|
||||
import { MatToolbarModule } from '@angular/material/toolbar';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatBadgeModule } from '@angular/material/badge';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
|
||||
import { CartService } from '../../core/services/cart.service';
|
||||
|
||||
// App Header
|
||||
@Component({
|
||||
selector: 'app-header',
|
||||
imports: [RouterLink, MatToolbarModule, MatIconModule, MatBadgeModule, MatButtonModule],
|
||||
templateUrl: './header.html',
|
||||
styleUrl: './header.scss'
|
||||
})
|
||||
export class Header {
|
||||
private readonly router = inject(Router);
|
||||
private readonly cart = inject(CartService);
|
||||
|
||||
// Products count in the cart, for the badge in the header.
|
||||
readonly cartCount = this.cart.count;
|
||||
|
||||
readonly isProductDetail = toSignal(
|
||||
this.router.events.pipe(
|
||||
filter((event): event is NavigationEnd => event instanceof NavigationEnd),
|
||||
map((event) => event.urlAfterRedirects.startsWith('/product/'))
|
||||
),
|
||||
{ initialValue: this.router.url.startsWith('/product/') }
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user