├── .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 |
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 |
4 |
5 | @for (product of products(); track product.id) {
6 | @defer (on immediate) {
7 |
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 |
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 |
--------------------------------------------------------------------------------