diff --git a/src/app/app.component.css b/src/app/app.component.css
deleted file mode 100644
index e69de29..0000000
diff --git a/src/app/app.component.html b/src/app/app.component.html
index dfa99d7..315760b 100644
--- a/src/app/app.component.html
+++ b/src/app/app.component.html
@@ -1,13 +1,3 @@
-
+
Shopping Cart
-
-
-
+
diff --git a/src/app/app.component.spec.ts b/src/app/app.component.spec.ts
deleted file mode 100644
index a0b86fd..0000000
--- a/src/app/app.component.spec.ts
+++ /dev/null
@@ -1,29 +0,0 @@
-import { TestBed } from '@angular/core/testing';
-import { AppComponent } from './app.component';
-
-describe('AppComponent', () => {
- beforeEach(async () => {
- await TestBed.configureTestingModule({
- imports: [AppComponent],
- }).compileComponents();
- });
-
- it('should create the app', () => {
- const fixture = TestBed.createComponent(AppComponent);
- const app = fixture.componentInstance;
- expect(app).toBeTruthy();
- });
-
- it(`should have the 'angular-workshop' title`, () => {
- const fixture = TestBed.createComponent(AppComponent);
- const app = fixture.componentInstance;
- expect(app.title).toEqual('angular-workshop');
- });
-
- it('should render title', () => {
- const fixture = TestBed.createComponent(AppComponent);
- fixture.detectChanges();
- const compiled = fixture.nativeElement as HTMLElement;
- expect(compiled.querySelector('h1')?.textContent).toContain('Hello, angular-workshop');
- });
-});
diff --git a/src/app/app.component.ts b/src/app/app.component.ts
index 72d6e84..a53788e 100644
--- a/src/app/app.component.ts
+++ b/src/app/app.component.ts
@@ -1,37 +1,12 @@
-import { Component, signal, ChangeDetectionStrategy, inject, WritableSignal } from '@angular/core';
-import { UserService } from './user.service';
-import { NgFor, NgIf } from '@angular/common';
-
+import { Component, ChangeDetectionStrategy } from '@angular/core';
+import { ProductOverviewComponent } from "./product-overview/product-overview.component";
@Component({
selector: 'app-root',
standalone: true,
- imports: [NgFor, NgIf],
+ imports: [ProductOverviewComponent],
templateUrl: './app.component.html',
- styleUrl: './app.component.css',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class AppComponent {
title = 'angular-workshop';
-
- private userService = inject(UserService);
-
- selectedUserId = signal(null);
-
- users: WritableSignal = signal([]);
-
- todos: WritableSignal = signal([]);
-
- ngOnInit() {
- this.userService.getUsers().subscribe(users => this.users.set(users));
-
- if(this.selectedUserId() !== null) {
- this.userService.getTodos(this.selectedUserId()!).subscribe(todos => this.todos.set(todos));
- }
- }
-
- onUserSelect(event: Event) {
- const userId = Number((event.target as HTMLSelectElement).value);
- this.selectedUserId.set(userId);
- this.userService.getTodos(userId).subscribe(todos => this.todos.set(todos));
- }
}
diff --git a/src/app/app.config.ts b/src/app/app.config.ts
index ea48cd5..2e965d5 100644
--- a/src/app/app.config.ts
+++ b/src/app/app.config.ts
@@ -7,7 +7,6 @@ import { provideHttpClient, withFetch } from '@angular/common/http';
export const appConfig: ApplicationConfig = {
providers: [
provideZoneChangeDetection({ eventCoalescing: true }),
-
provideRouter(routes),
provideHttpClient(withFetch())
]
diff --git a/src/app/product-overview/product-overview.component.html b/src/app/product-overview/product-overview.component.html
new file mode 100644
index 0000000..44359ad
--- /dev/null
+++ b/src/app/product-overview/product-overview.component.html
@@ -0,0 +1,6 @@
+
+
+
+
+
Total: {{ total() | currency : 'EUR' : 'symbol' : '1.2-2' }}
+
diff --git a/src/app/product-overview/product-overview.component.ts b/src/app/product-overview/product-overview.component.ts
new file mode 100644
index 0000000..945ee16
--- /dev/null
+++ b/src/app/product-overview/product-overview.component.ts
@@ -0,0 +1,28 @@
+import { Component, ChangeDetectionStrategy, signal, computed } from '@angular/core';
+import { ProductSelectionComponent, Product } from '../product-selection/product-selection.component';
+import { ProductQuantityComponent } from '../product-quantity/product-quantity.component';
+import { CurrencyPipe } from '@angular/common';
+@Component({
+ selector: 'app-product-overview',
+ standalone: true,
+ imports: [ProductSelectionComponent, ProductQuantityComponent, CurrencyPipe],
+ templateUrl: './product-overview.component.html',
+ changeDetection: ChangeDetectionStrategy.OnPush,
+})
+export class ProductOverviewComponent {
+ selectedProduct = signal(null);
+ quantity = signal(1);
+
+ total = computed(() => {
+ const product = this.selectedProduct();
+ return product ? product.price * this.quantity() : 0;
+ });
+
+ onProductChange(product: Product) {
+ this.selectedProduct.set(product);
+ }
+
+ onQuantityChange(quantity: number) {
+ this.quantity.set(quantity);
+ }
+}
diff --git a/src/app/product-quantity/product-quantity.component.html b/src/app/product-quantity/product-quantity.component.html
new file mode 100644
index 0000000..aa78cfc
--- /dev/null
+++ b/src/app/product-quantity/product-quantity.component.html
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/app/product-quantity/product-quantity.component.ts b/src/app/product-quantity/product-quantity.component.ts
new file mode 100644
index 0000000..4c3d07b
--- /dev/null
+++ b/src/app/product-quantity/product-quantity.component.ts
@@ -0,0 +1,33 @@
+import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core';
+import { Product } from '../product-selection/product-selection.component';
+import { FormsModule } from '@angular/forms';
+import { ProductService } from '../services/product.service';
+
+@Component({
+ selector: 'app-product-quantity',
+ standalone: true,
+ imports: [FormsModule],
+ templateUrl: './product-quantity.component.html',
+ changeDetection: ChangeDetectionStrategy.OnPush,
+})
+export class ProductQuantityComponent {
+ @Input({ required: true }) set product(value: Product | null) {
+ this.productService.setProduct(value);
+ }
+ get product(): Product | null {
+ return this.productService.getProduct();
+ }
+
+ @Output() quantityChange = new EventEmitter();
+
+ constructor(private productService: ProductService) {}
+
+ get quantity(): number {
+ return this.productService.getQuantity();
+ }
+
+ onQuantityChange(value: number) {
+ this.productService.setQuantity(value);
+ this.quantityChange.emit(value);
+ }
+}
diff --git a/src/app/product-selection/product-selection.component.html b/src/app/product-selection/product-selection.component.html
new file mode 100644
index 0000000..46f83fd
--- /dev/null
+++ b/src/app/product-selection/product-selection.component.html
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/src/app/product-selection/product-selection.component.ts b/src/app/product-selection/product-selection.component.ts
new file mode 100644
index 0000000..dde4d94
--- /dev/null
+++ b/src/app/product-selection/product-selection.component.ts
@@ -0,0 +1,58 @@
+import { NgFor } from '@angular/common';
+import { CurrencyPipe } from '@angular/common';
+import { FormsModule } from '@angular/forms';
+import { Component, ChangeDetectionStrategy, Output, EventEmitter, signal, inject, computed, OnInit } from '@angular/core';
+import { ProductService } from '../services/product.service';
+
+export type Product = {
+ id: number;
+ title: string;
+ price: number;
+ description: string;
+ category: string;
+ image: string;
+ rating: {
+ rate: number;
+ count: number;
+ }
+}
+
+@Component({
+ selector: 'app-product-selection',
+ standalone: true,
+ imports: [NgFor, CurrencyPipe, FormsModule],
+ templateUrl: './product-selection.component.html',
+ changeDetection: ChangeDetectionStrategy.OnPush,
+})
+
+export class ProductSelectionComponent implements OnInit {
+ products = signal([]);
+
+ selectedProductId = signal(null);
+
+ selectedProduct = computed(() => {
+ const id = this.selectedProductId();
+ return id ? this.products().find(p => p.id === id) || null : null;
+ });
+
+ quantity = signal(1);
+
+ @Output() quantityChange = new EventEmitter();
+ @Output() productChange = new EventEmitter();
+
+ private productService = inject(ProductService);
+
+ ngOnInit() {
+ this.productService.getProducts().subscribe(products => this.products.set(products));
+ }
+
+ onProductChange(productId: number) {
+ this.selectedProductId.set(productId);
+ const product = this.selectedProduct();
+ if (product) {
+ this.productChange.emit(product);
+ this.quantity.set(1);
+ this.quantityChange.emit(1);
+ }
+ }
+}
diff --git a/src/app/services/product.service.ts b/src/app/services/product.service.ts
new file mode 100644
index 0000000..e4df12d
--- /dev/null
+++ b/src/app/services/product.service.ts
@@ -0,0 +1,43 @@
+import { Injectable, signal, WritableSignal } from '@angular/core';
+import { HttpClient } from '@angular/common/http';
+import { Observable, shareReplay } from 'rxjs';
+import { Product } from '../product-selection/product-selection.component';
+
+@Injectable({
+ providedIn: 'root'
+})
+export class ProductService {
+ private productSignal = signal(null);
+ private quantitySignal = signal(1);
+
+ constructor(private http: HttpClient) {}
+
+ // Local state management
+ setProduct(product: Product | null): void {
+ this.productSignal.set(product);
+ if (product) {
+ this.quantitySignal.set(1);
+ }
+ }
+
+ getProduct(): Product | null {
+ return this.productSignal();
+ }
+
+ setQuantity(quantity: number): void {
+ this.quantitySignal.set(quantity);
+ }
+
+ getQuantity(): number {
+ return this.quantitySignal();
+ }
+
+ // API calls
+ getProducts(): Observable {
+ return this.http.get('https://fakestoreapi.com/products');
+ }
+
+ getProductById(id: number): Observable {
+ return this.http.get(`https://fakestoreapi.com/products/${id}`);
+ }
+}
\ No newline at end of file
diff --git a/src/app/services/user.service.ts b/src/app/services/user.service.ts
new file mode 100644
index 0000000..feab9d5
--- /dev/null
+++ b/src/app/services/user.service.ts
@@ -0,0 +1,10 @@
+import { Injectable } from '@angular/core';
+import { HttpClient } from '@angular/common/http';
+import { Observable } from 'rxjs';
+
+@Injectable({
+ providedIn: 'root'
+})
+export class UserService {
+ constructor(private http: HttpClient) {}
+}
\ No newline at end of file
diff --git a/src/app/user.service.ts b/src/app/user.service.ts
deleted file mode 100644
index 08b31a5..0000000
--- a/src/app/user.service.ts
+++ /dev/null
@@ -1,26 +0,0 @@
-import { Injectable } from '@angular/core';
-import { HttpClient } from '@angular/common/http';
-import { Observable, shareReplay } from 'rxjs';
-
-@Injectable({
- providedIn: 'root'
-})
-export class UserService {
- constructor(private http: HttpClient) {}
-
- getUsers(): Observable {
- return this.http.get('https://jsonplaceholder.typicode.com/users')
- }
-
- getUser(id: number): Observable {
- return this.http.get(`https://jsonplaceholder.typicode.com/users/${id}`)
- }
-
- getTodos(userId: number): Observable {
- return this.http.get(`https://jsonplaceholder.typicode.com/todos?userId=${userId}`)
- }
-
- getTodo(id: number): Observable {
- return this.http.get(`https://jsonplaceholder.typicode.com/todos/${id}`)
- }
-}