ez claps 01-04
This commit is contained in:
parent
6219a98f9a
commit
0641a268b8
4
.vscode/extensions.json
vendored
4
.vscode/extensions.json
vendored
@ -1,4 +0,0 @@
|
||||
{
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=827846
|
||||
"recommendations": ["angular.ng-template"]
|
||||
}
|
20
.vscode/launch.json
vendored
20
.vscode/launch.json
vendored
@ -1,20 +0,0 @@
|
||||
{
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "ng serve",
|
||||
"type": "chrome",
|
||||
"request": "launch",
|
||||
"preLaunchTask": "npm: start",
|
||||
"url": "http://localhost:4200/"
|
||||
},
|
||||
{
|
||||
"name": "ng test",
|
||||
"type": "chrome",
|
||||
"request": "launch",
|
||||
"preLaunchTask": "npm: test",
|
||||
"url": "http://localhost:9876/debug.html"
|
||||
}
|
||||
]
|
||||
}
|
42
.vscode/tasks.json
vendored
42
.vscode/tasks.json
vendored
@ -1,42 +0,0 @@
|
||||
{
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?LinkId=733558
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"type": "npm",
|
||||
"script": "start",
|
||||
"isBackground": true,
|
||||
"problemMatcher": {
|
||||
"owner": "typescript",
|
||||
"pattern": "$tsc",
|
||||
"background": {
|
||||
"activeOnStart": true,
|
||||
"beginsPattern": {
|
||||
"regexp": "(.*?)"
|
||||
},
|
||||
"endsPattern": {
|
||||
"regexp": "bundle generation complete"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "npm",
|
||||
"script": "test",
|
||||
"isBackground": true,
|
||||
"problemMatcher": {
|
||||
"owner": "typescript",
|
||||
"pattern": "$tsc",
|
||||
"background": {
|
||||
"activeOnStart": true,
|
||||
"beginsPattern": {
|
||||
"regexp": "(.*?)"
|
||||
},
|
||||
"endsPattern": {
|
||||
"regexp": "bundle generation complete"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
@ -24,7 +24,8 @@
|
||||
{
|
||||
"glob": "**/*",
|
||||
"input": "public"
|
||||
}
|
||||
},
|
||||
"src/assets"
|
||||
],
|
||||
"styles": [
|
||||
"src/styles.css"
|
||||
|
276
package-lock.json
generated
276
package-lock.json
generated
@ -450,6 +450,34 @@
|
||||
"typescript": ">=5.4 <5.6"
|
||||
}
|
||||
},
|
||||
"node_modules/@angular/compiler-cli/node_modules/chokidar": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.1.tgz",
|
||||
"integrity": "sha512-n8enUVCED/KVRQlab1hr3MVpcVMvxtZjmEa956u+4YijlmQED223XMSYj2tLuKvr4jcCTzNNMpQDUer72MMmzA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"readdirp": "^4.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 14.16.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
}
|
||||
},
|
||||
"node_modules/@angular/compiler-cli/node_modules/readdirp": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.0.2.tgz",
|
||||
"integrity": "sha512-yDMz9g+VaZkqBYS/ozoBJwaBhTbZo3UNYQHNRw1D3UFQB8oHB4uS/tAODO+ZLjGWmUbKnIlOWO+aaIiAxrUWHA==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">= 14.16.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "individual",
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
}
|
||||
},
|
||||
"node_modules/@angular/core": {
|
||||
"version": "18.2.8",
|
||||
"resolved": "https://registry.npmjs.org/@angular/core/-/core-18.2.8.tgz",
|
||||
@ -5531,19 +5559,39 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/chokidar": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.1.tgz",
|
||||
"integrity": "sha512-n8enUVCED/KVRQlab1hr3MVpcVMvxtZjmEa956u+4YijlmQED223XMSYj2tLuKvr4jcCTzNNMpQDUer72MMmzA==",
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
|
||||
"integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"readdirp": "^4.0.1"
|
||||
"anymatch": "~3.1.2",
|
||||
"braces": "~3.0.2",
|
||||
"glob-parent": "~5.1.2",
|
||||
"is-binary-path": "~2.1.0",
|
||||
"is-glob": "~4.0.1",
|
||||
"normalize-path": "~3.0.0",
|
||||
"readdirp": "~3.6.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 14.16.0"
|
||||
"node": ">= 8.10.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"fsevents": "~2.3.2"
|
||||
}
|
||||
},
|
||||
"node_modules/chokidar/node_modules/glob-parent": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
|
||||
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"is-glob": "^4.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/chownr": {
|
||||
@ -8773,31 +8821,6 @@
|
||||
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/karma/node_modules/chokidar": {
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
|
||||
"integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"anymatch": "~3.1.2",
|
||||
"braces": "~3.0.2",
|
||||
"glob-parent": "~5.1.2",
|
||||
"is-binary-path": "~2.1.0",
|
||||
"is-glob": "~4.0.1",
|
||||
"normalize-path": "~3.0.0",
|
||||
"readdirp": "~3.6.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 8.10.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"fsevents": "~2.3.2"
|
||||
}
|
||||
},
|
||||
"node_modules/karma/node_modules/cliui": {
|
||||
"version": "7.0.4",
|
||||
"resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
|
||||
@ -8837,19 +8860,6 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/karma/node_modules/glob-parent": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
|
||||
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"is-glob": "^4.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/karma/node_modules/is-fullwidth-code-point": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
|
||||
@ -8860,32 +8870,6 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/karma/node_modules/picomatch": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
|
||||
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8.6"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/jonschlinkert"
|
||||
}
|
||||
},
|
||||
"node_modules/karma/node_modules/readdirp": {
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
|
||||
"integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"picomatch": "^2.2.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/karma/node_modules/source-map": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
||||
@ -11410,17 +11394,27 @@
|
||||
}
|
||||
},
|
||||
"node_modules/readdirp": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.0.2.tgz",
|
||||
"integrity": "sha512-yDMz9g+VaZkqBYS/ozoBJwaBhTbZo3UNYQHNRw1D3UFQB8oHB4uS/tAODO+ZLjGWmUbKnIlOWO+aaIiAxrUWHA==",
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
|
||||
"integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"picomatch": "^2.2.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 14.16.0"
|
||||
"node": ">=8.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/readdirp/node_modules/picomatch": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
|
||||
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=8.6"
|
||||
},
|
||||
"funding": {
|
||||
"type": "individual",
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
"url": "https://github.com/sponsors/jonschlinkert"
|
||||
}
|
||||
},
|
||||
"node_modules/reflect-metadata": {
|
||||
@ -11853,70 +11847,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/sass/node_modules/chokidar": {
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
|
||||
"integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"anymatch": "~3.1.2",
|
||||
"braces": "~3.0.2",
|
||||
"glob-parent": "~5.1.2",
|
||||
"is-binary-path": "~2.1.0",
|
||||
"is-glob": "~4.0.1",
|
||||
"normalize-path": "~3.0.0",
|
||||
"readdirp": "~3.6.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 8.10.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"fsevents": "~2.3.2"
|
||||
}
|
||||
},
|
||||
"node_modules/sass/node_modules/glob-parent": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
|
||||
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"is-glob": "^4.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/sass/node_modules/picomatch": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
|
||||
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8.6"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/jonschlinkert"
|
||||
}
|
||||
},
|
||||
"node_modules/sass/node_modules/readdirp": {
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
|
||||
"integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"picomatch": "^2.2.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/sax": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz",
|
||||
@ -14102,31 +14032,6 @@
|
||||
"balanced-match": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/webpack-dev-server/node_modules/chokidar": {
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
|
||||
"integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"anymatch": "~3.1.2",
|
||||
"braces": "~3.0.2",
|
||||
"glob-parent": "~5.1.2",
|
||||
"is-binary-path": "~2.1.0",
|
||||
"is-glob": "~4.0.1",
|
||||
"normalize-path": "~3.0.0",
|
||||
"readdirp": "~3.6.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 8.10.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"fsevents": "~2.3.2"
|
||||
}
|
||||
},
|
||||
"node_modules/webpack-dev-server/node_modules/glob": {
|
||||
"version": "10.4.5",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz",
|
||||
@ -14148,19 +14053,6 @@
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/webpack-dev-server/node_modules/glob-parent": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
|
||||
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"is-glob": "^4.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/webpack-dev-server/node_modules/http-proxy-middleware": {
|
||||
"version": "2.0.7",
|
||||
"resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.7.tgz",
|
||||
@ -14202,32 +14094,6 @@
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/webpack-dev-server/node_modules/picomatch": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
|
||||
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8.6"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/jonschlinkert"
|
||||
}
|
||||
},
|
||||
"node_modules/webpack-dev-server/node_modules/readdirp": {
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
|
||||
"integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"picomatch": "^2.2.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/webpack-dev-server/node_modules/rimraf": {
|
||||
"version": "5.0.10",
|
||||
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.10.tgz",
|
||||
|
@ -1,7 +1,12 @@
|
||||
import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core';
|
||||
import { provideRouter } from '@angular/router';
|
||||
import { routes } from './app.routes';
|
||||
import {provideHttpClient} from '@angular/common/http';
|
||||
|
||||
export const appConfig: ApplicationConfig = {
|
||||
providers: [provideZoneChangeDetection({ eventCoalescing: true }), provideRouter(routes)]
|
||||
providers: [
|
||||
provideZoneChangeDetection({ eventCoalescing: true }),
|
||||
provideRouter(routes),
|
||||
provideHttpClient(),
|
||||
]
|
||||
};
|
||||
|
@ -1,6 +1,12 @@
|
||||
import { Routes } from '@angular/router';
|
||||
import {ProductListComponent} from './product-list/product-list.component';
|
||||
import {ProductListComponent} from './product/product-list/product-list.component';
|
||||
import {ProductDetailComponent} from './product/product-detail/product-detail.component';
|
||||
import {CartComponent} from './product/cart/cart.component';
|
||||
import {ShippingComponent} from './product/shipping/shipping.component';
|
||||
|
||||
export const routes: Routes = [
|
||||
{ path: '', component: ProductListComponent },
|
||||
{ path: 'products/:productId', component: ProductDetailComponent },
|
||||
{ path: 'cart', component: CartComponent },
|
||||
{ path: 'shipping', component: ShippingComponent },
|
||||
];
|
||||
|
@ -1 +0,0 @@
|
||||
<h2>Products</h2>
|
@ -1,17 +0,0 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { products } from '../products';
|
||||
|
||||
@Component({
|
||||
selector: 'app-product-list',
|
||||
standalone: true,
|
||||
imports: [],
|
||||
templateUrl: './product-list.component.html',
|
||||
styleUrl: './product-list.component.css'
|
||||
})
|
||||
export class ProductListComponent {
|
||||
products = [...products];
|
||||
|
||||
share() {
|
||||
window.alert('The product has been shared!');
|
||||
}
|
||||
}
|
25
src/app/product/cart/cart.component.html
Normal file
25
src/app/product/cart/cart.component.html
Normal file
@ -0,0 +1,25 @@
|
||||
<a [routerLink]="['/shipping']">Shipping Prices</a>
|
||||
|
||||
<ng-container *ngFor="let product of _items">
|
||||
<h3>{{ product.name }}</h3>
|
||||
<p>{{ product.price | currency }}</p>
|
||||
<hr>
|
||||
</ng-container>
|
||||
|
||||
<form [formGroup]="checkoutForm" (ngSubmit)="onSubmit()">
|
||||
<div>
|
||||
<label for="name">
|
||||
Name
|
||||
</label>
|
||||
<input id="name" type="text" formControlName="name">
|
||||
|
||||
</div>
|
||||
<div>
|
||||
<label for="address">
|
||||
Address
|
||||
</label>
|
||||
<input id="address" type="text" formControlName="address">
|
||||
|
||||
</div>
|
||||
<button class="button" type="submit">Purchase</button>
|
||||
</form>
|
23
src/app/product/cart/cart.component.spec.ts
Normal file
23
src/app/product/cart/cart.component.spec.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { CartComponent } from './cart.component';
|
||||
|
||||
describe('CartComponent', () => {
|
||||
let component: CartComponent;
|
||||
let fixture: ComponentFixture<CartComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [CartComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(CartComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
44
src/app/product/cart/cart.component.ts
Normal file
44
src/app/product/cart/cart.component.ts
Normal file
@ -0,0 +1,44 @@
|
||||
import {Component, OnInit} from '@angular/core';
|
||||
import {CartService} from '../service/cart.service';
|
||||
import {CurrencyPipe, NgForOf} from '@angular/common';
|
||||
import {Product} from '../products';
|
||||
import {RouterLink} from '@angular/router';
|
||||
import {FormBuilder, FormGroup, ReactiveFormsModule} from '@angular/forms';
|
||||
|
||||
@Component({
|
||||
selector: 'app-cart',
|
||||
standalone: true,
|
||||
imports: [
|
||||
NgForOf,
|
||||
CurrencyPipe,
|
||||
RouterLink,
|
||||
ReactiveFormsModule
|
||||
],
|
||||
templateUrl: './cart.component.html',
|
||||
styleUrl: './cart.component.css'
|
||||
})
|
||||
export class CartComponent implements OnInit {
|
||||
protected _items: Product[] = [];
|
||||
|
||||
protected checkoutForm!: FormGroup;
|
||||
|
||||
constructor(
|
||||
private _cartService: CartService,
|
||||
private _formBuilder: FormBuilder
|
||||
) {
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this._items = this._cartService.items;
|
||||
this.checkoutForm = this._formBuilder.group({
|
||||
name: '',
|
||||
address: ''
|
||||
});
|
||||
}
|
||||
|
||||
onSubmit(): void {
|
||||
this._items = this._cartService.clearCart();
|
||||
console.warn('Your order has been submitted', this.checkoutForm.value);
|
||||
this.checkoutForm.reset();
|
||||
}
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
<p *ngIf="product && product.price > 700">
|
||||
<button type="button" (click)="notify.emit()">Notify Me</button>
|
||||
</p>
|
@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { ProductAlertsComponent } from './product-alerts.component';
|
||||
|
||||
describe('ProductAlertsComponent', () => {
|
||||
let component: ProductAlertsComponent;
|
||||
let fixture: ComponentFixture<ProductAlertsComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [ProductAlertsComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(ProductAlertsComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
18
src/app/product/product-alerts/product-alerts.component.ts
Normal file
18
src/app/product/product-alerts/product-alerts.component.ts
Normal file
@ -0,0 +1,18 @@
|
||||
import {Component, EventEmitter, Input, Output} from '@angular/core';
|
||||
import {Product} from '../products';
|
||||
import {NgIf} from '@angular/common';
|
||||
|
||||
@Component({
|
||||
selector: 'app-product-alerts',
|
||||
standalone: true,
|
||||
imports: [
|
||||
NgIf
|
||||
],
|
||||
templateUrl: './product-alerts.component.html',
|
||||
styleUrl: './product-alerts.component.css'
|
||||
})
|
||||
export class ProductAlertsComponent {
|
||||
@Input() product: Product | undefined;
|
||||
|
||||
@Output() notify: EventEmitter<null> = new EventEmitter();
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
<ng-container *ngIf="_product">
|
||||
<p>{{_product.name}}</p>
|
||||
<p>{{_product.description}}</p>
|
||||
<p>Price: {{_product.price | currency }}</p>
|
||||
<button (click)="addToCart(_product)">Add to Cart</button>
|
||||
</ng-container>
|
@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { ProductDetailComponent } from './product-detail.component';
|
||||
|
||||
describe('ProductDetailComponent', () => {
|
||||
let component: ProductDetailComponent;
|
||||
let fixture: ComponentFixture<ProductDetailComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [ProductDetailComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(ProductDetailComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
39
src/app/product/product-detail/product-detail.component.ts
Normal file
39
src/app/product/product-detail/product-detail.component.ts
Normal file
@ -0,0 +1,39 @@
|
||||
import {Component, OnInit} from '@angular/core';
|
||||
import {Product, products} from '../products';
|
||||
import {CurrencyPipe, NgIf} from '@angular/common';
|
||||
import {ActivatedRoute} from '@angular/router';
|
||||
import {CartService} from '../service/cart.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-product-detail',
|
||||
standalone: true,
|
||||
imports: [
|
||||
NgIf,
|
||||
CurrencyPipe
|
||||
],
|
||||
templateUrl: './product-detail.component.html',
|
||||
styleUrl: './product-detail.component.css'
|
||||
})
|
||||
export class ProductDetailComponent implements OnInit{
|
||||
_product: Product | undefined;
|
||||
constructor(
|
||||
private route: ActivatedRoute,
|
||||
private cartService: CartService
|
||||
) {
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
const routeParams = this.route.snapshot.paramMap;
|
||||
const productIdFromRoute = Number(routeParams.get('productId'));
|
||||
|
||||
this._product = products.find(product => product.id === productIdFromRoute);
|
||||
}
|
||||
|
||||
addToCart(product: Product) {
|
||||
console.log(product)
|
||||
this.cartService.addToCart(product);
|
||||
|
||||
window.alert('Your product has been added to the cart!');
|
||||
|
||||
}
|
||||
}
|
17
src/app/product/product-list/product-list.component.html
Normal file
17
src/app/product/product-list/product-list.component.html
Normal file
@ -0,0 +1,17 @@
|
||||
<h2>Products</h2>
|
||||
<ng-container *ngFor="let product of products">
|
||||
<h3>
|
||||
<a
|
||||
[title]="product.name + 'details'"
|
||||
[routerLink]="['/products', product.id]"
|
||||
>
|
||||
{{ product.name }}
|
||||
</a>
|
||||
</h3>
|
||||
<p *ngIf="product.description">
|
||||
{{ product.description }}
|
||||
</p>
|
||||
<button (click)="share()">Share</button>
|
||||
<app-product-alerts [product]="product" (notify)="onNotify()" />
|
||||
<hr>
|
||||
</ng-container>
|
32
src/app/product/product-list/product-list.component.ts
Normal file
32
src/app/product/product-list/product-list.component.ts
Normal file
@ -0,0 +1,32 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { products } from '../products';
|
||||
import {ProductDetailComponent} from '../product-detail/product-detail.component';
|
||||
import {NgForOf, NgIf} from '@angular/common';
|
||||
import {ProductAlertsComponent} from '../product-alerts/product-alerts.component';
|
||||
import {RouterLink} from '@angular/router';
|
||||
|
||||
@Component({
|
||||
selector: 'app-product-list',
|
||||
standalone: true,
|
||||
imports: [
|
||||
ProductDetailComponent,
|
||||
NgForOf,
|
||||
NgIf,
|
||||
ProductAlertsComponent,
|
||||
RouterLink
|
||||
],
|
||||
templateUrl: './product-list.component.html',
|
||||
styleUrl: './product-list.component.css'
|
||||
})
|
||||
export class ProductListComponent {
|
||||
products = [...products];
|
||||
|
||||
share() {
|
||||
window.alert('The product has been shared!');
|
||||
}
|
||||
|
||||
|
||||
onNotify() {
|
||||
window.alert('You will be notified when the product goes on sale');
|
||||
}
|
||||
}
|
@ -5,7 +5,7 @@ export interface Product {
|
||||
description: string;
|
||||
}
|
||||
|
||||
export const products = [
|
||||
export const products: Product[] = [
|
||||
{
|
||||
id: 1,
|
||||
name: 'Phone XL',
|
16
src/app/product/service/cart.service.spec.ts
Normal file
16
src/app/product/service/cart.service.spec.ts
Normal file
@ -0,0 +1,16 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
|
||||
import { CartService } from './cart.service';
|
||||
|
||||
describe('CartService', () => {
|
||||
let service: CartService;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({});
|
||||
service = TestBed.inject(CartService);
|
||||
});
|
||||
|
||||
it('should be created', () => {
|
||||
expect(service).toBeTruthy();
|
||||
});
|
||||
});
|
33
src/app/product/service/cart.service.ts
Normal file
33
src/app/product/service/cart.service.ts
Normal file
@ -0,0 +1,33 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import {Product} from '../products';
|
||||
import {HttpClient} from '@angular/common/http';
|
||||
import {Shipping} from '../shipping';
|
||||
import {Observable} from 'rxjs';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class CartService {
|
||||
private _items: Product[] = [];
|
||||
|
||||
constructor(private http: HttpClient) {
|
||||
}
|
||||
|
||||
public addToCart(product: Product): void {
|
||||
this._items.push(product);
|
||||
}
|
||||
|
||||
public get items() {
|
||||
return this._items;
|
||||
}
|
||||
|
||||
public clearCart(): Product[] {
|
||||
this._items = [];
|
||||
|
||||
return this._items;
|
||||
}
|
||||
|
||||
public getShippingPrices(): Observable<Shipping[]> {
|
||||
return this.http.get<Shipping[]>('/assets/shipping.json');
|
||||
}
|
||||
}
|
4
src/app/product/shipping.ts
Normal file
4
src/app/product/shipping.ts
Normal file
@ -0,0 +1,4 @@
|
||||
export interface Shipping {
|
||||
type: string;
|
||||
price: number;
|
||||
}
|
0
src/app/product/shipping/shipping.component.css
Normal file
0
src/app/product/shipping/shipping.component.css
Normal file
5
src/app/product/shipping/shipping.component.html
Normal file
5
src/app/product/shipping/shipping.component.html
Normal file
@ -0,0 +1,5 @@
|
||||
<h3>Shipping Prices</h3>
|
||||
<div class="shipping-item" *ngFor="let shipping of shippingCosts | async">
|
||||
<span>{{ shipping.type }}</span>
|
||||
<span>{{ shipping.price | currency }}</span>
|
||||
</div>
|
23
src/app/product/shipping/shipping.component.spec.ts
Normal file
23
src/app/product/shipping/shipping.component.spec.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { ShippingComponent } from './shipping.component';
|
||||
|
||||
describe('ShippingComponent', () => {
|
||||
let component: ShippingComponent;
|
||||
let fixture: ComponentFixture<ShippingComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [ShippingComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(ShippingComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
28
src/app/product/shipping/shipping.component.ts
Normal file
28
src/app/product/shipping/shipping.component.ts
Normal file
@ -0,0 +1,28 @@
|
||||
import {Component, OnInit} from '@angular/core';
|
||||
import {CartService} from '../service/cart.service';
|
||||
import {Observable} from 'rxjs';
|
||||
import {Shipping} from '../shipping';
|
||||
import {AsyncPipe, CurrencyPipe, NgForOf} from '@angular/common';
|
||||
|
||||
@Component({
|
||||
selector: 'app-shipping',
|
||||
standalone: true,
|
||||
imports: [
|
||||
AsyncPipe,
|
||||
CurrencyPipe,
|
||||
NgForOf
|
||||
],
|
||||
templateUrl: './shipping.component.html',
|
||||
styleUrl: './shipping.component.css'
|
||||
})
|
||||
export class ShippingComponent implements OnInit {
|
||||
protected shippingCosts!: Observable<Shipping[]>;
|
||||
|
||||
constructor(private cartService: CartService) {
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.shippingCosts = this.cartService.getShippingPrices();
|
||||
}
|
||||
|
||||
}
|
@ -1,5 +1,4 @@
|
||||
<a [routerLink]="['/']">
|
||||
<h1>My Store</h1>
|
||||
</a>
|
||||
|
||||
<a class="button fancy-button"><em class="material-icons">shopping_cart</em>Checkout</a>
|
||||
<a class="button fancy-button" [routerLink]="['/cart']"><em class="material-icons">shopping_cart</em>Checkout</a>
|
||||
|
Reference in New Issue
Block a user