├── .editorconfig ├── .gitignore ├── .vscode ├── extensions.json ├── launch.json └── tasks.json ├── README.md ├── angular.json ├── db.json ├── package-lock.json ├── package.json ├── proxy.config.json ├── src ├── app │ ├── app.component.html │ ├── app.component.scss │ ├── app.component.ts │ ├── app.config.ts │ ├── app.routes.ts │ ├── features │ │ ├── create │ │ │ ├── create.component.html │ │ │ ├── create.component.scss │ │ │ └── create.component.ts │ │ ├── edit │ │ │ ├── edit.component.html │ │ │ ├── edit.component.scss │ │ │ └── edit.component.ts │ │ └── list │ │ │ ├── components │ │ │ ├── card │ │ │ │ ├── card.component.html │ │ │ │ ├── card.component.scss │ │ │ │ └── card.component.ts │ │ │ └── no-items │ │ │ │ ├── no-items.component.html │ │ │ │ ├── no-items.component.scss │ │ │ │ └── no-items.component.ts │ │ │ ├── list.component.html │ │ │ ├── list.component.scss │ │ │ └── list.component.ts │ └── shared │ │ ├── components │ │ ├── back-to-list │ │ │ ├── back-to-list.component.html │ │ │ ├── back-to-list.component.scss │ │ │ └── back-to-list.component.ts │ │ ├── form │ │ │ ├── form.component.html │ │ │ ├── form.component.scss │ │ │ └── form.component.ts │ │ └── header │ │ │ ├── header.component.html │ │ │ ├── header.component.scss │ │ │ └── header.component.ts │ │ ├── interfaces │ │ ├── payload-product.interface.ts │ │ └── product.interface.ts │ │ ├── resolvers │ │ ├── get-product.resolver.ts │ │ └── get-products.resolver.ts │ │ └── services │ │ ├── confirmation-dialog.service.ts │ │ └── products.service.ts ├── assets │ └── .gitkeep ├── favicon.ico ├── index.html ├── main.ts └── styles.scss ├── tsconfig.app.json ├── tsconfig.json └── tsconfig.spec.json /.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see https://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.ts] 12 | quote_type = single 13 | 14 | [*.md] 15 | max_line_length = off 16 | trim_trailing_whitespace = false 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # Compiled output 4 | /dist 5 | /tmp 6 | /out-tsc 7 | /bazel-out 8 | 9 | # Node 10 | /node_modules 11 | npm-debug.log 12 | yarn-error.log 13 | 14 | # IDEs and editors 15 | .idea/ 16 | .project 17 | .classpath 18 | .c9/ 19 | *.launch 20 | .settings/ 21 | *.sublime-workspace 22 | 23 | # Visual Studio Code 24 | .vscode/* 25 | !.vscode/settings.json 26 | !.vscode/tasks.json 27 | !.vscode/launch.json 28 | !.vscode/extensions.json 29 | .history/* 30 | 31 | # Miscellaneous 32 | /.angular/cache 33 | .sass-cache/ 34 | /connect.lock 35 | /coverage 36 | /libpeerconnection.log 37 | testem.log 38 | /typings 39 | 40 | # System files 41 | .DS_Store 42 | Thumbs.db 43 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=827846 3 | "recommendations": ["angular.ng-template"] 4 | } 5 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 3 | "version": "0.2.0", 4 | "configurations": [ 5 | { 6 | "name": "ng serve", 7 | "type": "chrome", 8 | "request": "launch", 9 | "preLaunchTask": "npm: start", 10 | "url": "http://localhost:4200/" 11 | }, 12 | { 13 | "name": "ng test", 14 | "type": "chrome", 15 | "request": "launch", 16 | "preLaunchTask": "npm: test", 17 | "url": "http://localhost:9876/debug.html" 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // For more information, visit: https://go.microsoft.com/fwlink/?LinkId=733558 3 | "version": "2.0.0", 4 | "tasks": [ 5 | { 6 | "type": "npm", 7 | "script": "start", 8 | "isBackground": true, 9 | "problemMatcher": { 10 | "owner": "typescript", 11 | "pattern": "$tsc", 12 | "background": { 13 | "activeOnStart": true, 14 | "beginsPattern": { 15 | "regexp": "(.*?)" 16 | }, 17 | "endsPattern": { 18 | "regexp": "bundle generation complete" 19 | } 20 | } 21 | } 22 | }, 23 | { 24 | "type": "npm", 25 | "script": "test", 26 | "isBackground": true, 27 | "problemMatcher": { 28 | "owner": "typescript", 29 | "pattern": "$tsc", 30 | "background": { 31 | "activeOnStart": true, 32 | "beginsPattern": { 33 | "regexp": "(.*?)" 34 | }, 35 | "endsPattern": { 36 | "regexp": "bundle generation complete" 37 | } 38 | } 39 | } 40 | } 41 | ] 42 | } 43 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Mini-Curso Angular 17 2 | 3 | Nesse projeto foi criado um gerenciador de cadastro de produtos, onde é possível listar, criar, editar e remover produtos. 4 | 5 | ## O que foi usado 6 | 7 | - Angular 17 8 | - Angular Material 17 9 | - Json Server como Backend 10 | 11 | ## Comandos 12 | 13 | #### Iniciar servidor local 14 | 15 | O comando abaixo vai iniciar o servidor do Angular 16 | 17 | ``` 18 | npm start 19 | ``` 20 | 21 | E para iniciar o servidor de JSON Server, execute: 22 | 23 | ``` 24 | npx json-server db.json 25 | ``` 26 | -------------------------------------------------------------------------------- /angular.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json", 3 | "version": 1, 4 | "newProjectRoot": "projects", 5 | "projects": { 6 | "product-store": { 7 | "projectType": "application", 8 | "schematics": { 9 | "@schematics/angular:component": { 10 | "style": "scss", 11 | "skipTests": true 12 | }, 13 | "@schematics/angular:class": { 14 | "skipTests": true 15 | }, 16 | "@schematics/angular:directive": { 17 | "skipTests": true 18 | }, 19 | "@schematics/angular:guard": { 20 | "skipTests": true 21 | }, 22 | "@schematics/angular:interceptor": { 23 | "skipTests": true 24 | }, 25 | "@schematics/angular:pipe": { 26 | "skipTests": true 27 | }, 28 | "@schematics/angular:resolver": { 29 | "skipTests": true 30 | }, 31 | "@schematics/angular:service": { 32 | "skipTests": true 33 | } 34 | }, 35 | "root": "", 36 | "sourceRoot": "src", 37 | "prefix": "app", 38 | "architect": { 39 | "build": { 40 | "builder": "@angular-devkit/build-angular:application", 41 | "options": { 42 | "outputPath": "dist/product-store", 43 | "index": "src/index.html", 44 | "browser": "src/main.ts", 45 | "polyfills": [ 46 | "zone.js" 47 | ], 48 | "tsConfig": "tsconfig.app.json", 49 | "inlineStyleLanguage": "scss", 50 | "assets": [ 51 | "src/favicon.ico", 52 | "src/assets" 53 | ], 54 | "styles": [ 55 | "@angular/material/prebuilt-themes/deeppurple-amber.css", 56 | "src/styles.scss" 57 | ], 58 | "scripts": [] 59 | }, 60 | "configurations": { 61 | "production": { 62 | "budgets": [ 63 | { 64 | "type": "initial", 65 | "maximumWarning": "500kb", 66 | "maximumError": "1mb" 67 | }, 68 | { 69 | "type": "anyComponentStyle", 70 | "maximumWarning": "2kb", 71 | "maximumError": "4kb" 72 | } 73 | ], 74 | "outputHashing": "all" 75 | }, 76 | "development": { 77 | "optimization": false, 78 | "extractLicenses": false, 79 | "sourceMap": true 80 | } 81 | }, 82 | "defaultConfiguration": "production" 83 | }, 84 | "serve": { 85 | "builder": "@angular-devkit/build-angular:dev-server", 86 | "configurations": { 87 | "production": { 88 | "buildTarget": "product-store:build:production" 89 | }, 90 | "development": { 91 | "buildTarget": "product-store:build:development" 92 | } 93 | }, 94 | "options": { 95 | "proxyConfig": "proxy.config.json" 96 | }, 97 | "defaultConfiguration": "development" 98 | }, 99 | "extract-i18n": { 100 | "builder": "@angular-devkit/build-angular:extract-i18n", 101 | "options": { 102 | "buildTarget": "product-store:build" 103 | } 104 | }, 105 | "test": { 106 | "builder": "@angular-devkit/build-angular:karma", 107 | "options": { 108 | "polyfills": [ 109 | "zone.js", 110 | "zone.js/testing" 111 | ], 112 | "tsConfig": "tsconfig.spec.json", 113 | "inlineStyleLanguage": "scss", 114 | "assets": [ 115 | "src/favicon.ico", 116 | "src/assets" 117 | ], 118 | "styles": [ 119 | "@angular/material/prebuilt-themes/deeppurple-amber.css", 120 | "src/styles.scss" 121 | ], 122 | "scripts": [] 123 | } 124 | } 125 | } 126 | } 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /db.json: -------------------------------------------------------------------------------- 1 | { 2 | "products": [ 3 | { 4 | "id": "75e2", 5 | "title": "Mochila" 6 | }, 7 | { 8 | "id": "39e1", 9 | "title": "Camiseta" 10 | } 11 | ] 12 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "product-store", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "ng": "ng", 6 | "start": "ng serve", 7 | "build": "ng build", 8 | "watch": "ng build --watch --configuration development", 9 | "test": "ng test" 10 | }, 11 | "private": true, 12 | "dependencies": { 13 | "@angular/animations": "^17.1.0", 14 | "@angular/cdk": "^17.1.2", 15 | "@angular/common": "^17.1.0", 16 | "@angular/compiler": "^17.1.0", 17 | "@angular/core": "^17.1.0", 18 | "@angular/forms": "^17.1.0", 19 | "@angular/material": "^17.1.2", 20 | "@angular/platform-browser": "^17.1.0", 21 | "@angular/platform-browser-dynamic": "^17.1.0", 22 | "@angular/router": "^17.1.0", 23 | "rxjs": "~7.8.0", 24 | "tslib": "^2.3.0", 25 | "zone.js": "~0.14.3" 26 | }, 27 | "devDependencies": { 28 | "@angular-devkit/build-angular": "^17.1.2", 29 | "@angular/cli": "^17.1.2", 30 | "@angular/compiler-cli": "^17.1.0", 31 | "@types/jasmine": "~5.1.0", 32 | "jasmine-core": "~5.1.0", 33 | "json-server": "^1.0.0-alpha.23", 34 | "karma": "~6.4.0", 35 | "karma-chrome-launcher": "~3.2.0", 36 | "karma-coverage": "~2.2.0", 37 | "karma-jasmine": "~5.1.0", 38 | "karma-jasmine-html-reporter": "~2.1.0", 39 | "typescript": "~5.3.2" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /proxy.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "/api": { 3 | "target": "http://localhost:3000/", 4 | "pathRewrite": { 5 | "^/api": "" 6 | } 7 | } 8 | } -------------------------------------------------------------------------------- /src/app/app.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 |
5 | 6 |
7 |
8 | -------------------------------------------------------------------------------- /src/app/app.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-dimension/mini-curso-angular-17/3508851d839db4267d2c98852193dbe95d089be8/src/app/app.component.scss -------------------------------------------------------------------------------- /src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { RouterOutlet } from '@angular/router'; 3 | import { HeaderComponent } from './shared/components/header/header.component'; 4 | 5 | @Component({ 6 | selector: 'app-root', 7 | standalone: true, 8 | imports: [RouterOutlet, HeaderComponent], 9 | templateUrl: './app.component.html', 10 | styleUrl: './app.component.scss' 11 | }) 12 | export class AppComponent { } 13 | -------------------------------------------------------------------------------- /src/app/app.config.ts: -------------------------------------------------------------------------------- 1 | import { ApplicationConfig, ValueProvider } from '@angular/core'; 2 | import { provideRouter } from '@angular/router'; 3 | 4 | import { routes } from './app.routes'; 5 | import { provideAnimationsAsync } from '@angular/platform-browser/animations/async'; 6 | import { provideHttpClient } from '@angular/common/http'; 7 | import { 8 | MAT_SNACK_BAR_DEFAULT_OPTIONS, 9 | MatSnackBarConfig, 10 | } from '@angular/material/snack-bar'; 11 | 12 | const SNACK_BAR_CONFIG: ValueProvider = { 13 | provide: MAT_SNACK_BAR_DEFAULT_OPTIONS, 14 | useValue: { 15 | duration: 3000, 16 | horizontalPosition: 'right', 17 | verticalPosition: 'top', 18 | } as MatSnackBarConfig, 19 | }; 20 | 21 | export const appConfig: ApplicationConfig = { 22 | providers: [ 23 | provideRouter(routes), 24 | provideAnimationsAsync(), 25 | provideHttpClient(), 26 | SNACK_BAR_CONFIG 27 | ], 28 | }; 29 | -------------------------------------------------------------------------------- /src/app/app.routes.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Routes, 3 | } from '@angular/router'; 4 | import { ListComponent } from './features/list/list.component'; 5 | import { getProducts } from './shared/resolvers/get-products.resolver'; 6 | import { getProduct } from './shared/resolvers/get-product.resolver'; 7 | 8 | export const routes: Routes = [ 9 | { 10 | path: '', 11 | resolve: { 12 | products: getProducts 13 | }, 14 | component: ListComponent, 15 | }, 16 | { 17 | path: 'create-product', 18 | loadComponent: () => 19 | import('./features/create/create.component').then( 20 | (m) => m.CreateComponent 21 | ), 22 | }, 23 | { 24 | path: 'edit-product/:id', 25 | resolve: { 26 | product: getProduct, 27 | }, 28 | loadComponent: () => 29 | import('./features/edit/edit.component').then((m) => m.EditComponent), 30 | }, 31 | ]; 32 | -------------------------------------------------------------------------------- /src/app/features/create/create.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/app/features/create/create.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-dimension/mini-curso-angular-17/3508851d839db4267d2c98852193dbe95d089be8/src/app/features/create/create.component.scss -------------------------------------------------------------------------------- /src/app/features/create/create.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, inject } from '@angular/core'; 2 | import { MatSnackBar } from '@angular/material/snack-bar'; 3 | import { ProductsService } from '../../shared/services/products.service'; 4 | import { Router, RouterLink } from '@angular/router'; 5 | import { FormComponent } from '../../shared/components/form/form.component'; 6 | import { Product } from '../../shared/interfaces/product.interface'; 7 | import { BackToListComponent } from '../../shared/components/back-to-list/back-to-list.component'; 8 | 9 | @Component({ 10 | selector: 'app-create', 11 | standalone: true, 12 | imports: [FormComponent, BackToListComponent], 13 | templateUrl: './create.component.html', 14 | styleUrl: './create.component.scss', 15 | }) 16 | export class CreateComponent { 17 | productsService = inject(ProductsService); 18 | matSnackBar = inject(MatSnackBar); 19 | router = inject(Router); 20 | 21 | onSubmit(product: Product) { 22 | this.productsService.post(product).subscribe(() => { 23 | this.matSnackBar.open('Produto criado com sucesso!', 'Ok'); 24 | 25 | this.router.navigateByUrl('/'); 26 | }); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/app/features/edit/edit.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/app/features/edit/edit.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-dimension/mini-curso-angular-17/3508851d839db4267d2c98852193dbe95d089be8/src/app/features/edit/edit.component.scss -------------------------------------------------------------------------------- /src/app/features/edit/edit.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, inject } from '@angular/core'; 2 | import { ProductsService } from '../../shared/services/products.service'; 3 | import { MatSnackBar } from '@angular/material/snack-bar'; 4 | import { ActivatedRoute, Router } from '@angular/router'; 5 | import { Product } from '../../shared/interfaces/product.interface'; 6 | import { FormComponent } from '../../shared/components/form/form.component'; 7 | import { BackToListComponent } from '../../shared/components/back-to-list/back-to-list.component'; 8 | 9 | @Component({ 10 | selector: 'app-edit', 11 | standalone: true, 12 | imports: [FormComponent, BackToListComponent], 13 | templateUrl: './edit.component.html', 14 | styleUrl: './edit.component.scss', 15 | }) 16 | export class EditComponent { 17 | productsService = inject(ProductsService); 18 | matSnackBar = inject(MatSnackBar); 19 | router = inject(Router); 20 | 21 | product: Product = inject(ActivatedRoute).snapshot.data['product']; 22 | 23 | onSubmit(product: Product) { 24 | this.productsService.put(this.product.id, product).subscribe(() => { 25 | this.matSnackBar.open('Produto editado com sucesso!', 'Ok'); 26 | 27 | this.router.navigateByUrl('/'); 28 | }); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/app/features/list/components/card/card.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | {{ productTitle() }} 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/app/features/list/components/card/card.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-dimension/mini-curso-angular-17/3508851d839db4267d2c98852193dbe95d089be8/src/app/features/list/components/card/card.component.scss -------------------------------------------------------------------------------- /src/app/features/list/components/card/card.component.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Component, 3 | EventEmitter, 4 | Output, 5 | computed, 6 | input, 7 | } from '@angular/core'; 8 | import { MatButtonModule } from '@angular/material/button'; 9 | import { MatCardModule } from '@angular/material/card'; 10 | import { Product } from '../../../../shared/interfaces/product.interface'; 11 | 12 | @Component({ 13 | selector: 'app-card', 14 | standalone: true, 15 | imports: [MatCardModule, MatButtonModule], 16 | templateUrl: './card.component.html', 17 | styleUrl: './card.component.scss', 18 | }) 19 | export class CardComponent { 20 | product = input.required(); 21 | 22 | @Output() edit = new EventEmitter(); 23 | @Output() delete = new EventEmitter(); 24 | 25 | productTitle = computed(() => this.product().title); 26 | 27 | onEdit() { 28 | this.edit.emit(); 29 | } 30 | 31 | onDelete() { 32 | this.delete.emit(); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/app/features/list/components/no-items/no-items.component.html: -------------------------------------------------------------------------------- 1 | 2 | Nenhum produto cadastrado :( 3 | 4 | -------------------------------------------------------------------------------- /src/app/features/list/components/no-items/no-items.component.scss: -------------------------------------------------------------------------------- 1 | .centralize-message { 2 | text-align: center; 3 | } -------------------------------------------------------------------------------- /src/app/features/list/components/no-items/no-items.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { MatCardModule } from '@angular/material/card'; 3 | 4 | @Component({ 5 | selector: 'app-no-items', 6 | standalone: true, 7 | imports: [MatCardModule], 8 | templateUrl: './no-items.component.html', 9 | styleUrl: './no-items.component.scss' 10 | }) 11 | export class NoItemsComponent { 12 | 13 | } 14 | -------------------------------------------------------------------------------- /src/app/features/list/list.component.html: -------------------------------------------------------------------------------- 1 |
2 | Criar produto 3 |
4 | 5 | @for (product of products(); track product.id) { 6 | @defer (on immediate) { 7 |
8 | 12 | 13 |
14 | } 15 | } 16 | @empty { 17 | @defer (on immediate) { 18 | 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/app/features/list/list.component.scss: -------------------------------------------------------------------------------- 1 | .action-container { 2 | display: flex; 3 | justify-content: flex-end; 4 | padding: 16px 0; 5 | } 6 | 7 | .item-container { 8 | display: block; 9 | margin-bottom: 16px; 10 | } -------------------------------------------------------------------------------- /src/app/features/list/list.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, inject, signal } from '@angular/core'; 2 | import { ProductsService } from '../../shared/services/products.service'; 3 | import { Product } from '../../shared/interfaces/product.interface'; 4 | import { MatButtonModule } from '@angular/material/button'; 5 | import { CardComponent } from './components/card/card.component'; 6 | import { ActivatedRoute, Router, RouterLink } from '@angular/router'; 7 | import { filter } from 'rxjs'; 8 | import { ConfirmationDialogService } from '../../shared/services/confirmation-dialog.service'; 9 | import { NoItemsComponent } from './components/no-items/no-items.component'; 10 | 11 | @Component({ 12 | selector: 'app-list', 13 | standalone: true, 14 | imports: [CardComponent, RouterLink, MatButtonModule, NoItemsComponent], 15 | templateUrl: './list.component.html', 16 | styleUrl: './list.component.scss', 17 | }) 18 | export class ListComponent { 19 | products = signal( 20 | inject(ActivatedRoute).snapshot.data['products'] 21 | ); 22 | 23 | productsService = inject(ProductsService); 24 | router = inject(Router); 25 | confirmationDialogService = inject(ConfirmationDialogService); 26 | 27 | onEdit(product: Product) { 28 | this.router.navigate(['/edit-product', product.id]); 29 | } 30 | 31 | onDelete(product: Product) { 32 | this.confirmationDialogService 33 | .openDialog() 34 | .pipe(filter((answer) => answer === true)) 35 | .subscribe(() => { 36 | this.productsService.delete(product.id).subscribe(() => { 37 | this.productsService.getAll().subscribe((products) => { 38 | this.products.set(products); 39 | }); 40 | }); 41 | }); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/app/shared/components/back-to-list/back-to-list.component.html: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /src/app/shared/components/back-to-list/back-to-list.component.scss: -------------------------------------------------------------------------------- 1 | .link-container { 2 | margin: 8px 0; 3 | } -------------------------------------------------------------------------------- /src/app/shared/components/back-to-list/back-to-list.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { MatButtonModule } from '@angular/material/button'; 3 | import { RouterLink } from '@angular/router'; 4 | 5 | @Component({ 6 | selector: 'app-back-to-list', 7 | standalone: true, 8 | imports: [RouterLink, MatButtonModule], 9 | templateUrl: './back-to-list.component.html', 10 | styleUrl: './back-to-list.component.scss' 11 | }) 12 | export class BackToListComponent { 13 | 14 | } 15 | -------------------------------------------------------------------------------- /src/app/shared/components/form/form.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 | Input 5 | 11 | 12 | 13 | 14 |
15 |
16 | -------------------------------------------------------------------------------- /src/app/shared/components/form/form.component.scss: -------------------------------------------------------------------------------- 1 | .form-container { 2 | display: flex; 3 | justify-content: center; 4 | } 5 | 6 | .form-content-container { 7 | width: 100%; 8 | display: flex; 9 | flex-direction: column; 10 | } -------------------------------------------------------------------------------- /src/app/shared/components/form/form.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, EventEmitter, Output, input } from '@angular/core'; 2 | import { 3 | FormGroup, 4 | FormControl, 5 | Validators, 6 | ReactiveFormsModule, 7 | } from '@angular/forms'; 8 | import { Product } from '../../interfaces/product.interface'; 9 | import { MatButtonModule } from '@angular/material/button'; 10 | import { MatFormFieldModule } from '@angular/material/form-field'; 11 | import { MatInputModule } from '@angular/material/input'; 12 | 13 | @Component({ 14 | selector: 'app-form', 15 | standalone: true, 16 | imports: [ 17 | ReactiveFormsModule, 18 | MatFormFieldModule, 19 | MatInputModule, 20 | MatButtonModule, 21 | ], 22 | templateUrl: './form.component.html', 23 | styleUrl: './form.component.scss', 24 | }) 25 | export class FormComponent { 26 | product = input(null); 27 | 28 | form!: FormGroup; 29 | 30 | @Output() done = new EventEmitter(); 31 | 32 | ngOnInit(): void { 33 | this.form = new FormGroup({ 34 | title: new FormControl(this.product()?.title ?? '', { 35 | nonNullable: true, 36 | validators: Validators.required, 37 | }), 38 | }); 39 | } 40 | 41 | onSubmit() { 42 | const product = this.form.value as Product; 43 | this.done.emit(product); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/app/shared/components/header/header.component.html: -------------------------------------------------------------------------------- 1 | 2 |
3 |
4 |
Gerenciados de Produtos
5 |
6 |
7 |
8 | -------------------------------------------------------------------------------- /src/app/shared/components/header/header.component.scss: -------------------------------------------------------------------------------- 1 | .title { 2 | text-align: center; 3 | } -------------------------------------------------------------------------------- /src/app/shared/components/header/header.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { MatToolbarModule } from "@angular/material/toolbar"; 3 | 4 | @Component({ 5 | selector: 'app-header', 6 | standalone: true, 7 | imports: [MatToolbarModule], 8 | templateUrl: './header.component.html', 9 | styleUrl: './header.component.scss' 10 | }) 11 | export class HeaderComponent { 12 | 13 | } 14 | -------------------------------------------------------------------------------- /src/app/shared/interfaces/payload-product.interface.ts: -------------------------------------------------------------------------------- 1 | import { Product } from "./product.interface"; 2 | 3 | export type ProductPayload = Omit; -------------------------------------------------------------------------------- /src/app/shared/interfaces/product.interface.ts: -------------------------------------------------------------------------------- 1 | export interface Product { 2 | id: string; 3 | title: string 4 | } -------------------------------------------------------------------------------- /src/app/shared/resolvers/get-product.resolver.ts: -------------------------------------------------------------------------------- 1 | import { inject } from '@angular/core'; 2 | import { ActivatedRouteSnapshot } from '@angular/router'; 3 | import { ProductsService } from '../services/products.service'; 4 | 5 | export const getProduct = (route: ActivatedRouteSnapshot) => { 6 | const productsService = inject(ProductsService); 7 | return productsService.get(route.paramMap.get('id') as string); 8 | }; 9 | -------------------------------------------------------------------------------- /src/app/shared/resolvers/get-products.resolver.ts: -------------------------------------------------------------------------------- 1 | import { inject } from '@angular/core'; 2 | import { ProductsService } from '../services/products.service'; 3 | 4 | export const getProducts = () => { 5 | const productsService = inject(ProductsService); 6 | return productsService.getAll(); 7 | }; 8 | -------------------------------------------------------------------------------- /src/app/shared/services/confirmation-dialog.service.ts: -------------------------------------------------------------------------------- 1 | import { Component, Injectable, inject } from '@angular/core'; 2 | import { MatButtonModule } from '@angular/material/button'; 3 | import { 4 | MatDialog, 5 | MatDialogModule, 6 | MatDialogRef, 7 | } from '@angular/material/dialog'; 8 | import { Observable } from 'rxjs'; 9 | import { filter } from 'rxjs/operators'; 10 | 11 | @Component({ 12 | selector: 'app-confirmation-dialog', 13 | template: ` 14 |

Deletar produto

15 | 16 | Tem certeza que quer deletar esse produto? 17 | 18 | 19 | 20 | 28 | 29 | `, 30 | standalone: true, 31 | imports: [MatButtonModule, MatDialogModule], 32 | }) 33 | export class ConfirmationDialogComponent { 34 | matDialogRef = inject(MatDialogRef); 35 | 36 | onNo() { 37 | this.matDialogRef.close(false); 38 | } 39 | 40 | onYes() { 41 | this.matDialogRef.close(true); 42 | } 43 | } 44 | 45 | @Injectable({ 46 | providedIn: 'root', 47 | }) 48 | export class ConfirmationDialogService { 49 | matDialog = inject(MatDialog); 50 | 51 | constructor() {} 52 | 53 | openDialog(): Observable { 54 | return this.matDialog.open(ConfirmationDialogComponent).afterClosed(); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/app/shared/services/products.service.ts: -------------------------------------------------------------------------------- 1 | import { HttpClient } from '@angular/common/http'; 2 | import { Injectable, inject } from '@angular/core'; 3 | import { Product } from '../interfaces/product.interface'; 4 | import { ProductPayload } from '../interfaces/payload-product.interface'; 5 | 6 | @Injectable({ 7 | providedIn: 'root', 8 | }) 9 | export class ProductsService { 10 | httpClient = inject(HttpClient); 11 | 12 | getAll() { 13 | return this.httpClient.get('/api/products'); 14 | } 15 | 16 | get(id: string) { 17 | return this.httpClient.get(`/api/products/${id}`); 18 | } 19 | 20 | post(payload: ProductPayload) { 21 | return this.httpClient.post('/api/products', payload); 22 | } 23 | 24 | put(id: string, payload: ProductPayload) { 25 | return this.httpClient.put(`/api/products/${id}`, payload); 26 | } 27 | 28 | delete(id: string) { 29 | return this.httpClient.delete(`/api/products/${id}`); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-dimension/mini-curso-angular-17/3508851d839db4267d2c98852193dbe95d089be8/src/assets/.gitkeep -------------------------------------------------------------------------------- /src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-dimension/mini-curso-angular-17/3508851d839db4267d2c98852193dbe95d089be8/src/favicon.ico -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | ProductStore 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import { bootstrapApplication } from '@angular/platform-browser'; 2 | import { appConfig } from './app/app.config'; 3 | import { AppComponent } from './app/app.component'; 4 | 5 | bootstrapApplication(AppComponent, appConfig) 6 | .catch((err) => console.error(err)); 7 | -------------------------------------------------------------------------------- /src/styles.scss: -------------------------------------------------------------------------------- 1 | /* You can add global styles to this file, and also import other style files */ 2 | html, 3 | body { 4 | height: 100%; 5 | } 6 | body { 7 | margin: 0; 8 | font-family: Roboto, "Helvetica Neue", sans-serif; 9 | } 10 | 11 | .container { 12 | display: flex; 13 | justify-content: center; 14 | width: 100%; 15 | } 16 | 17 | .content-container { 18 | max-width: 450px; 19 | width: 100%; 20 | } 21 | -------------------------------------------------------------------------------- /tsconfig.app.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "extends": "./tsconfig.json", 4 | "compilerOptions": { 5 | "outDir": "./out-tsc/app", 6 | "types": [] 7 | }, 8 | "files": [ 9 | "src/main.ts" 10 | ], 11 | "include": [ 12 | "src/**/*.d.ts" 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "compileOnSave": false, 4 | "compilerOptions": { 5 | "outDir": "./dist/out-tsc", 6 | "forceConsistentCasingInFileNames": true, 7 | "strict": true, 8 | "noImplicitOverride": true, 9 | "noPropertyAccessFromIndexSignature": true, 10 | "noImplicitReturns": true, 11 | "noFallthroughCasesInSwitch": true, 12 | "skipLibCheck": true, 13 | "esModuleInterop": true, 14 | "sourceMap": true, 15 | "declaration": false, 16 | "experimentalDecorators": true, 17 | "moduleResolution": "node", 18 | "importHelpers": true, 19 | "target": "ES2022", 20 | "module": "ES2022", 21 | "useDefineForClassFields": false, 22 | "lib": [ 23 | "ES2022", 24 | "dom" 25 | ] 26 | }, 27 | "angularCompilerOptions": { 28 | "enableI18nLegacyMessageIdFormat": false, 29 | "strictInjectionParameters": true, 30 | "strictInputAccessModifiers": true, 31 | "strictTemplates": true 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "extends": "./tsconfig.json", 4 | "compilerOptions": { 5 | "outDir": "./out-tsc/spec", 6 | "types": [ 7 | "jasmine" 8 | ] 9 | }, 10 | "include": [ 11 | "src/**/*.spec.ts", 12 | "src/**/*.d.ts" 13 | ] 14 | } 15 | --------------------------------------------------------------------------------