├── .editorconfig ├── .gitignore ├── LICENSE ├── README.md ├── angular.json ├── browserslist ├── db.json ├── e2e ├── protractor.conf.js ├── src │ ├── app.e2e-spec.ts │ └── app.po.ts └── tsconfig.e2e.json ├── package-lock.json ├── package.json ├── src ├── app │ ├── app.component.html │ ├── app.component.scss │ ├── app.component.ts │ ├── app.module.ts │ ├── app.routing.module.ts │ ├── core │ │ ├── config-params.service.ts │ │ └── filmes.service.ts │ ├── filmes │ │ ├── cadastro-filmes │ │ │ ├── cadastro-filmes.component.html │ │ │ ├── cadastro-filmes.component.scss │ │ │ └── cadastro-filmes.component.ts │ │ ├── filmes.module.ts │ │ ├── listagem-filmes │ │ │ ├── listagem-filmes.component.html │ │ │ ├── listagem-filmes.component.scss │ │ │ └── listagem-filmes.component.ts │ │ └── visualizar-filmes │ │ │ ├── visualizar-filmes.component.css │ │ │ ├── visualizar-filmes.component.html │ │ │ └── visualizar-filmes.component.ts │ └── shared │ │ ├── components │ │ ├── alerta │ │ │ ├── alerta.component.css │ │ │ ├── alerta.component.html │ │ │ └── alerta.component.ts │ │ ├── campos │ │ │ ├── campos.module.ts │ │ │ ├── input-date │ │ │ │ ├── input-date.component.css │ │ │ │ ├── input-date.component.html │ │ │ │ └── input-date.component.ts │ │ │ ├── input-number │ │ │ │ ├── input-number.component.css │ │ │ │ ├── input-number.component.html │ │ │ │ └── input-number.component.ts │ │ │ ├── input-select │ │ │ │ ├── input-select.component.css │ │ │ │ ├── input-select.component.html │ │ │ │ └── input-select.component.ts │ │ │ ├── input-text │ │ │ │ ├── input-text.component.css │ │ │ │ ├── input-text.component.html │ │ │ │ └── input-text.component.ts │ │ │ ├── input-textarea │ │ │ │ ├── input-textarea.component.css │ │ │ │ ├── input-textarea.component.html │ │ │ │ └── input-textarea.component.ts │ │ │ └── validar-campos.service.ts │ │ ├── rodape │ │ │ ├── rodape.component.html │ │ │ ├── rodape.component.scss │ │ │ └── rodape.component.ts │ │ └── topo │ │ │ ├── topo.component.html │ │ │ ├── topo.component.scss │ │ │ └── topo.component.ts │ │ ├── material │ │ ├── material.module.spec.ts │ │ └── material.module.ts │ │ └── models │ │ ├── alerta.ts │ │ ├── campo-generico.ts │ │ ├── config-prams.ts │ │ └── filme.ts ├── assets │ ├── .gitkeep │ ├── images │ │ └── angular-material-post.png │ └── styles │ │ ├── reset.scss │ │ ├── styles.scss │ │ └── themes.scss ├── environments │ ├── environment.prod.ts │ └── environment.ts ├── favicon.ico ├── index.html ├── karma.conf.js ├── main.ts ├── polyfills.ts ├── test.ts ├── tsconfig.app.json ├── tsconfig.spec.json └── tslint.json ├── tsconfig.json └── tslint.json /.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see http://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 | [*.md] 12 | max_line_length = off 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /.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 | 8 | # dependencies 9 | /node_modules 10 | 11 | # IDEs and editors 12 | /.idea 13 | .project 14 | .classpath 15 | .c9/ 16 | *.launch 17 | .settings/ 18 | *.sublime-workspace 19 | 20 | # IDE - VSCode 21 | .vscode/* 22 | !.vscode/settings.json 23 | !.vscode/tasks.json 24 | !.vscode/launch.json 25 | !.vscode/extensions.json 26 | 27 | # misc 28 | /.sass-cache 29 | /connect.lock 30 | /coverage 31 | /libpeerconnection.log 32 | npm-debug.log 33 | yarn-error.log 34 | testem.log 35 | /typings 36 | 37 | # System Files 38 | .DS_Store 39 | Thumbs.db 40 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Renan Rafael Bertoldo 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Curso Intermediário de Angular - Digital Innovation One 2 | 3 | Esse curso foi feito para a plataforma [Digital Innovation One](https://digitalinnovation.one/) 4 | 5 | O curso consiste em um sistema de filmes, com a possibilidade de cadastros, edições, listagem e visualização dos filmes. 6 | 7 | ## Instalação 8 | 9 | 1. clone o repositório `git clone git@github.com:RenanRB/curso-angular.git` 10 | 2. Entre no projeto e instale as dependencias `npm install` 11 | 3. Caso você queira alguma aula específica lembre que voce pode pegar direto das nossas release fazendo download do zip ou clonando a partir do commit hash, URL: https://github.com/RenanRB/curso-angular/tags 12 | 13 | ## Ambiente Local 14 | 15 | Execute `ng serve` para que o projeto suba localmente. Acesse a url `http://localhost:4200/`. O projeto já está com reload automático conforme as alterações que você realizar no código 16 | 17 | ## Simulando o Back-end 18 | 19 | Execute `npm install -g json-server` para instalar globalmente o servidor json. Após a instalação entre na pasta do projeto e execute `json-server --watch db.json`, com isso um servidor será inicializado na url `http://localhost:3000/`, após a inicialização sera possível realizar requisições http. 20 | 21 | ## Gerando componente 22 | 23 | Execute `ng generate component nome-do-componente` para criar um novo componente. Você também pode usuar `ng generate directive|pipe|service|class|guard|interface|enum|module`. 24 | 25 | ## Build 26 | 27 | Execute `ng build` para gerar o compilado do projeto. O projeto vai ser criado dentro do diretório `dist/`. Adicionar `--prod` junto comando de build para gerar minificado e pronto para o ambiente de produção. 28 | 29 | -------------------------------------------------------------------------------- /angular.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json", 3 | "version": 1, 4 | "newProjectRoot": "projects", 5 | "projects": { 6 | "angular-material-base": { 7 | "root": "", 8 | "sourceRoot": "src", 9 | "projectType": "application", 10 | "prefix": "", 11 | "schematics": { 12 | "@schematics/angular:component": { 13 | "styleext": "scss" 14 | } 15 | }, 16 | "architect": { 17 | "build": { 18 | "builder": "@angular-devkit/build-angular:browser", 19 | "options": { 20 | "outputPath": "dist/angular-material-base", 21 | "index": "src/index.html", 22 | "main": "src/main.ts", 23 | "polyfills": "src/polyfills.ts", 24 | "tsConfig": "src/tsconfig.app.json", 25 | "assets": [ 26 | "src/favicon.ico", 27 | "src/assets" 28 | ], 29 | "styles": [ 30 | { 31 | "input": "node_modules/@angular/material/prebuilt-themes/deeppurple-amber.css" 32 | }, 33 | "src/assets/styles/styles.scss" 34 | ], 35 | "scripts": [] 36 | }, 37 | "configurations": { 38 | "production": { 39 | "fileReplacements": [ 40 | { 41 | "replace": "src/environments/environment.ts", 42 | "with": "src/environments/environment.prod.ts" 43 | } 44 | ], 45 | "optimization": true, 46 | "outputHashing": "all", 47 | "sourceMap": false, 48 | "extractCss": true, 49 | "namedChunks": false, 50 | "aot": true, 51 | "extractLicenses": true, 52 | "vendorChunk": false, 53 | "buildOptimizer": true 54 | } 55 | } 56 | }, 57 | "serve": { 58 | "builder": "@angular-devkit/build-angular:dev-server", 59 | "options": { 60 | "browserTarget": "angular-material-base:build" 61 | }, 62 | "configurations": { 63 | "production": { 64 | "browserTarget": "angular-material-base:build:production" 65 | } 66 | } 67 | }, 68 | "extract-i18n": { 69 | "builder": "@angular-devkit/build-angular:extract-i18n", 70 | "options": { 71 | "browserTarget": "angular-material-base:build" 72 | } 73 | }, 74 | "test": { 75 | "builder": "@angular-devkit/build-angular:karma", 76 | "options": { 77 | "main": "src/test.ts", 78 | "polyfills": "src/polyfills.ts", 79 | "tsConfig": "src/tsconfig.spec.json", 80 | "karmaConfig": "src/karma.conf.js", 81 | "styles": [ 82 | { 83 | "input": "node_modules/@angular/material/prebuilt-themes/indigo-pink.css" 84 | }, 85 | "src/styles.css" 86 | ], 87 | "scripts": [], 88 | "assets": [ 89 | "src/favicon.ico", 90 | "src/assets" 91 | ] 92 | } 93 | }, 94 | "lint": { 95 | "builder": "@angular-devkit/build-angular:tslint", 96 | "options": { 97 | "tsConfig": [ 98 | "src/tsconfig.app.json", 99 | "src/tsconfig.spec.json" 100 | ], 101 | "exclude": [ 102 | "**/node_modules/**" 103 | ] 104 | } 105 | } 106 | } 107 | }, 108 | "angular-material-base-e2e": { 109 | "root": "e2e/", 110 | "projectType": "application", 111 | "architect": { 112 | "e2e": { 113 | "builder": "@angular-devkit/build-angular:protractor", 114 | "options": { 115 | "protractorConfig": "e2e/protractor.conf.js", 116 | "devServerTarget": "angular-material-base:serve" 117 | }, 118 | "configurations": { 119 | "production": { 120 | "devServerTarget": "angular-material-base:serve:production" 121 | } 122 | } 123 | }, 124 | "lint": { 125 | "builder": "@angular-devkit/build-angular:tslint", 126 | "options": { 127 | "tsConfig": "e2e/tsconfig.e2e.json", 128 | "exclude": [ 129 | "**/node_modules/**" 130 | ] 131 | } 132 | } 133 | } 134 | } 135 | }, 136 | "defaultProject": "angular-material-base", 137 | "schematics": { 138 | "@schematics/angular:component": { 139 | "prefix": "", 140 | "styleext": "scss" 141 | }, 142 | "@schematics/angular:directive": { 143 | "prefix": "" 144 | } 145 | }, 146 | "cli": { 147 | "defaultCollection": "@ngrx/schematics" 148 | } 149 | } -------------------------------------------------------------------------------- /browserslist: -------------------------------------------------------------------------------- 1 | # This file is currently used by autoprefixer to adjust CSS to support the below specified browsers 2 | # For additional information regarding the format and rule options, please see: 3 | # https://github.com/browserslist/browserslist#queries 4 | # For IE 9-11 support, please uncomment the last line of the file and adjust as needed 5 | > 0.5% 6 | last 2 versions 7 | Firefox ESR 8 | not dead 9 | # IE 9-11 -------------------------------------------------------------------------------- /db.json: -------------------------------------------------------------------------------- 1 | { 2 | "filmes": [ 3 | { 4 | "id": 1, 5 | "titulo": "MIB: Homens de Preto", 6 | "urlFoto": "https://m.media-amazon.com/images/M/MV5BOTlhYTVkMDktYzIyNC00NzlkLTlmN2ItOGEyMWQ4OTA2NDdmXkEyXkFqcGdeQXVyNTAyODkwOQ@@._V1_UX182_CR0,0,182,268_AL_.jpg", 7 | "dtLancamento": "1997-07-11T03:00:00.000Z", 8 | "descricao": "A police officer joins a secret organization that polices and monitors extraterrestrial interactions on Earth.", 9 | "nota": 7.3, 10 | "urlIMDb": "https://www.imdb.com/title/tt0119654/", 11 | "genero": "Ação" 12 | }, 13 | { 14 | "id": 2, 15 | "titulo": "MIB: Homens de Preto II ", 16 | "urlFoto": "https://m.media-amazon.com/images/M/MV5BMTMxNDA0NTMxMV5BMl5BanBnXkFtZTYwMDE2NzY2._V1_UX182_CR0,0,182,268_AL_.jpg", 17 | "dtLancamento": "2012-07-19T03:00:00.000Z", 18 | "descricao": "Agent J is sent to find Agent K and restore his memory after the re-appearance of a case from K's past.", 19 | "nota": 6.2, 20 | "urlIMDb": "https://www.imdb.com/title/tt0120912", 21 | "genero": "Ação" 22 | }, 23 | { 24 | "id": 3, 25 | "titulo": "MIB: Homens de Preto III", 26 | "urlFoto": "https://m.media-amazon.com/images/M/MV5BMTU2NTYxODcwMF5BMl5BanBnXkFtZTcwNDk1NDY0Nw@@._V1_UX182_CR0,0,182,268_AL_.jpg", 27 | "dtLancamento": "2012-25-11T03:00:00.000Z", 28 | "descricao": "Agent J travels in time to M.I.B.'s early days in 1969 to stop an alien from assassinating his friend Agent K and changing history.", 29 | "nota": 6.8, 30 | "urlIMDb": "https://www.imdb.com/title/tt1409024/", 31 | "genero": "Ação" 32 | }, 33 | { 34 | "id": 4, 35 | "titulo": "MIB: Homens de Preto - Internacional", 36 | "urlFoto": "https://m.media-amazon.com/images/M/MV5BMDZkODI2ZGItYTY5Yi00MTA4LWExY2ItM2ZmNjczYjM0NDg1XkEyXkFqcGdeQXVyMzY0MTE3NzU@._V1_UX182_CR0,0,182,268_AL_.jpg", 37 | "dtLancamento": "2019-13-11T03:00:00.000Z", 38 | "descricao": "The Men in Black have always protected the Earth from the scum of the universe. In this new adventure, they tackle their biggest threat to date: a mole in the Men in Black organization.", 39 | "nota": 5.6, 40 | "urlIMDb": "https://www.imdb.com/title/tt2283336/", 41 | "genero": "Ação" 42 | }, 43 | { 44 | "id": 5, 45 | "titulo": "Coringa", 46 | "urlFoto": "https://m.media-amazon.com/images/M/MV5BNGVjNWI4ZGUtNzE0MS00YTJmLWE0ZDctN2ZiYTk2YmI3NTYyXkEyXkFqcGdeQXVyMTkxNjUyNQ@@._V1_UX182_CR0,0,182,268_AL_.jpg", 47 | "dtLancamento": "2019-10-03T03:00:00.000Z", 48 | "descricao": "In Gotham City, mentally troubled comedian Arthur Fleck is disregarded and mistreated by society. He then embarks on a downward spiral of revolution and bloody crime. This path brings him face-to-face with his alter-ego: the Joker.", 49 | "nota": 8.7, 50 | "urlIMDb": "https://www.imdb.com/title/tt7286456/", 51 | "genero": "Drama" 52 | }, 53 | { 54 | "id": 6, 55 | "titulo": "Vingadores: Ultimato", 56 | "urlFoto": "https://m.media-amazon.com/images/M/MV5BMTc5MDE2ODcwNV5BMl5BanBnXkFtZTgwMzI2NzQ2NzM@._V1_UX182_CR0,0,182,268_AL_.jpg", 57 | "dtLancamento": "2019-04-25T03:00:00.000Z", 58 | "descricao": "After the devastating events of Vingadores: Guerra Infinita (2018), the universe is in ruins. With the help of remaining allies, the Avengers assemble once more in order to reverse Thanos' actions and restore balance to the universe.", 59 | "nota": 8.5, 60 | "urlIMDb": "https://www.imdb.com/title/tt4154796/", 61 | "genero": "Aventura" 62 | }, 63 | { 64 | "id": 7, 65 | "titulo": "Capitã Marvel", 66 | "urlFoto": "https://m.media-amazon.com/images/M/MV5BMTE0YWFmOTMtYTU2ZS00ZTIxLWE3OTEtYTNiYzBkZjViZThiXkEyXkFqcGdeQXVyODMzMzQ4OTI@._V1_UX182_CR0,0,182,268_AL_.jpg", 67 | "dtLancamento": "2019-03-07T03:00:00.000Z", 68 | "descricao": "Carol Danvers becomes one of the universe's most powerful heroes when Earth is caught in the middle of a galactic war between two alien races.", 69 | "nota": 6.9, 70 | "urlIMDb": "https://www.imdb.com/title/tt4154664/", 71 | "genero": "Ação" 72 | }, 73 | { 74 | "id": 8, 75 | "titulo": "Shazam!", 76 | "urlFoto": "https://m.media-amazon.com/images/M/MV5BYTE0Yjc1NzUtMjFjMC00Y2I3LTg3NGYtNGRlMGJhYThjMTJmXkEyXkFqcGdeQXVyNTI4MzE4MDU@._V1_UX182_CR0,0,182,268_AL_.jpg", 77 | "dtLancamento": "2019-04-04T03:00:00.000Z", 78 | "descricao": "A newly fostered young boy in search of his mother instead finds unexpected super powers and soon gains a powerful enemy.", 79 | "nota": 7.1, 80 | "urlIMDb": "https://www.imdb.com/title/tt0448115/", 81 | "genero": "Comédia" 82 | }, 83 | { 84 | "id": 9, 85 | "titulo": "John Wick 3: Parabellum", 86 | "urlFoto": "https://m.media-amazon.com/images/M/MV5BMDg2YzI0ODctYjliMy00NTU0LTkxODYtYTNkNjQwMzVmOTcxXkEyXkFqcGdeQXVyNjg2NjQwMDQ@._V1_UX182_CR0,0,182,268_AL_.jpg", 87 | "dtLancamento": "2019-05-16T03:00:00.000Z", 88 | "descricao": "John Wick is on the run after killing a member of the international assassin's guild, and with a $14 million price tag on his head, he is the target of hit men and women everywhere.", 89 | "nota": 7.5, 90 | "urlIMDb": "https://www.imdb.com/title/tt6146586/", 91 | "genero": "Ação" 92 | }, 93 | { 94 | "titulo": "Mistério no Mediterrâneo", 95 | "urlFoto": "https://m.media-amazon.com/images/M/MV5BNTA2YTI5YjUtZWI4Zi00NWQ5LWFiYmEtOTBmNTUyNDAwNjllXkEyXkFqcGdeQXVyNjIzNzM4NzA@._V1_UX182_CR0,0,182,268_AL_.jpg", 96 | "dtLancamento": "2019-06-14T03:00:00.000Z", 97 | "descricao": "A New York cop and his wife go on a European vacation to reinvigorate the spark in their marriage, but end up getting framed and on the run for the death of an elderly billionaire.", 98 | "nota": 6, 99 | "urlIMDb": "https://www.imdb.com/title/tt1618434/", 100 | "genero": "Comédia", 101 | "id": 10 102 | }, 103 | { 104 | "titulo": "Star Wars: A Ascensão Skywalker", 105 | "urlFoto": "https://m.media-amazon.com/images/M/MV5BMDljNTQ5ODItZmQwMy00M2ExLTljOTQtZTVjNGE2NTg0NGIxXkEyXkFqcGdeQXVyODkzNTgxMDg@._V1_UX182_CR0,0,182,268_AL_.jpg", 106 | "dtLancamento": "2019-12-19T03:00:00.000Z", 107 | "descricao": "In Theaters Now\nAfter Palpatine mysteriously returns, the Resistance faces the First Order once more in the final chapter of the Skywalker saga.", 108 | "nota": 6.9, 109 | "urlIMDb": "https://www.imdb.com/title/tt2527338/?ref_=vi_md_ti", 110 | "genero": "Aventura", 111 | "id": 11 112 | } 113 | ] 114 | } -------------------------------------------------------------------------------- /e2e/protractor.conf.js: -------------------------------------------------------------------------------- 1 | // Protractor configuration file, see link for more information 2 | // https://github.com/angular/protractor/blob/master/lib/config.ts 3 | 4 | const { SpecReporter } = require('jasmine-spec-reporter'); 5 | 6 | exports.config = { 7 | allScriptsTimeout: 11000, 8 | specs: [ 9 | './src/**/*.e2e-spec.ts' 10 | ], 11 | capabilities: { 12 | 'browserName': 'chrome' 13 | }, 14 | directConnect: true, 15 | baseUrl: 'http://localhost:4200/', 16 | framework: 'jasmine', 17 | jasmineNodeOpts: { 18 | showColors: true, 19 | defaultTimeoutInterval: 30000, 20 | print: function() {} 21 | }, 22 | onPrepare() { 23 | require('ts-node').register({ 24 | project: require('path').join(__dirname, './tsconfig.e2e.json') 25 | }); 26 | jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } })); 27 | } 28 | }; -------------------------------------------------------------------------------- /e2e/src/app.e2e-spec.ts: -------------------------------------------------------------------------------- 1 | import { AppPage } from './app.po'; 2 | 3 | describe('workspace-project App', () => { 4 | let page: AppPage; 5 | 6 | beforeEach(() => { 7 | page = new AppPage(); 8 | }); 9 | 10 | it('should display welcome message', () => { 11 | page.navigateTo(); 12 | expect(page.getParagraphText()).toEqual('Welcome to angular-material-base!'); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /e2e/src/app.po.ts: -------------------------------------------------------------------------------- 1 | import { browser, by, element } from 'protractor'; 2 | 3 | export class AppPage { 4 | navigateTo() { 5 | return browser.get('/'); 6 | } 7 | 8 | getParagraphText() { 9 | return element(by.css('dio-root h1')).getText(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /e2e/tsconfig.e2e.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/app", 5 | "module": "commonjs", 6 | "target": "es5", 7 | "types": [ 8 | "jasmine", 9 | "jasminewd2", 10 | "node" 11 | ] 12 | } 13 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "filmes", 3 | "version": "0.0.0", 4 | "description": "Curso avançado angular", 5 | "scripts": { 6 | "ng": "ng", 7 | "start": "ng serve", 8 | "build": "ng build", 9 | "test": "ng test", 10 | "lint": "ng lint", 11 | "e2e": "ng e2e" 12 | }, 13 | "private": true, 14 | "dependencies": { 15 | "@angular/animations": "^8.2.7", 16 | "@angular/cdk": "^8.2.0", 17 | "@angular/common": "^8.2.7", 18 | "@angular/compiler": "^8.2.7", 19 | "@angular/core": "^8.2.7", 20 | "@angular/forms": "^8.2.7", 21 | "@angular/material": "^8.2.0", 22 | "@angular/platform-browser": "^8.2.7", 23 | "@angular/platform-browser-dynamic": "^8.2.7", 24 | "@angular/router": "^8.2.7", 25 | "@ngrx/effects": "^8.3.0", 26 | "@ngrx/entity": "^8.3.0", 27 | "@ngrx/schematics": "^8.5.2", 28 | "@ngrx/store": "^8.3.0", 29 | "core-js": "^3.2.1", 30 | "hammerjs": "^2.0.8", 31 | "ngx-infinite-scroll": "^8.0.1", 32 | "rxjs": "^6.0.0", 33 | "tslib": "^1.10.0", 34 | "zone.js": "~0.9.1" 35 | }, 36 | "devDependencies": { 37 | "@angular-devkit/build-angular": "~0.803.5", 38 | "@angular/cli": "~8.3.5", 39 | "@angular/compiler-cli": "^8.2.7", 40 | "@angular/language-service": "^8.2.7", 41 | "@schematics/angular": "^7.0.7", 42 | "@types/jasmine": "~3.4.0", 43 | "@types/jasminewd2": "~2.0.3", 44 | "@types/node": "~12.7.7", 45 | "codelyzer": "^5.0.1", 46 | "jasmine-core": "~3.5.0", 47 | "jasmine-spec-reporter": "~4.2.1", 48 | "karma": "~4.3.0", 49 | "karma-chrome-launcher": "~3.1.0", 50 | "karma-coverage-istanbul-reporter": "~2.1.0", 51 | "karma-jasmine": "~2.0.1", 52 | "karma-jasmine-html-reporter": "^1.4.2", 53 | "protractor": "~5.4.2", 54 | "ts-node": "~8.4.1", 55 | "tslint": "~5.20.0", 56 | "typescript": "~3.5" 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/app/app.component.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/app.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RenanRB/curso-angular/b4379b667acf18f2137ee6b28bb56ba5ff58735d/src/app/app.component.scss -------------------------------------------------------------------------------- /src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'dio-root', 5 | templateUrl: './app.component.html', 6 | styleUrls: ['./app.component.scss'] 7 | }) 8 | export class AppComponent { 9 | title = 'app'; 10 | } 11 | -------------------------------------------------------------------------------- /src/app/app.module.ts: -------------------------------------------------------------------------------- 1 | import { BrowserModule } from '@angular/platform-browser'; 2 | import { NgModule } from '@angular/core'; 3 | import { LayoutModule } from '@angular/cdk/layout'; 4 | import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; 5 | import { MAT_DATE_LOCALE } from '@angular/material/core'; 6 | import { HttpClientModule } from '@angular/common/http'; 7 | 8 | import { AppComponent } from './app.component'; 9 | import { AppRoutingModule } from './app.routing.module'; 10 | 11 | import { MaterialModule } from './shared/material/material.module'; 12 | import { TopoComponent } from './shared/components/topo/topo.component'; 13 | import { RodapeComponent } from './shared/components/rodape/rodape.component'; 14 | import { AlertaComponent } from './shared/components/alerta/alerta.component'; 15 | 16 | 17 | @NgModule({ 18 | declarations: [ 19 | AppComponent, 20 | TopoComponent, 21 | RodapeComponent, 22 | AlertaComponent, 23 | ], 24 | imports: [ 25 | BrowserModule, 26 | BrowserAnimationsModule, 27 | HttpClientModule, 28 | LayoutModule, 29 | MaterialModule, 30 | AppRoutingModule 31 | ], 32 | entryComponents: [AlertaComponent], 33 | providers: [{ provide: MAT_DATE_LOCALE, useValue: 'pt' }], 34 | bootstrap: [AppComponent] 35 | }) 36 | export class AppModule { } 37 | -------------------------------------------------------------------------------- /src/app/app.routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { Routes, RouterModule } from '@angular/router'; 3 | import { FilmesModule } from './filmes/filmes.module'; 4 | import { CadastroFilmesComponent } from './filmes/cadastro-filmes/cadastro-filmes.component'; 5 | import { ListagemFilmesComponent } from './filmes/listagem-filmes/listagem-filmes.component'; 6 | import { VisualizarFilmesComponent } from './filmes/visualizar-filmes/visualizar-filmes.component'; 7 | 8 | const routes: Routes = [ 9 | 10 | { 11 | path: '', 12 | redirectTo: 'filmes', 13 | pathMatch: 'full' 14 | }, 15 | { 16 | path: 'filmes', 17 | children: [ 18 | { 19 | path: '', 20 | component: ListagemFilmesComponent 21 | }, 22 | { 23 | path: 'cadastro', 24 | children: [ 25 | { 26 | path: '', 27 | component: CadastroFilmesComponent 28 | }, 29 | { 30 | path: ':id', 31 | component: CadastroFilmesComponent 32 | } 33 | ] 34 | }, 35 | { 36 | path: ':id', 37 | component: VisualizarFilmesComponent, 38 | pathMatch: 'full' 39 | } 40 | ] 41 | }, 42 | { path: '**', redirectTo: 'filmes' }, 43 | 44 | ]; 45 | 46 | @NgModule({ 47 | imports: [ 48 | RouterModule.forRoot(routes), 49 | FilmesModule 50 | ], 51 | exports: [RouterModule] 52 | }) 53 | export class AppRoutingModule { } 54 | -------------------------------------------------------------------------------- /src/app/core/config-params.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { HttpParams } from '@angular/common/http'; 3 | import { ConfigPrams } from '../shared/models/config-prams'; 4 | 5 | @Injectable({ 6 | providedIn: 'root' 7 | }) 8 | export class ConfigParamsService { 9 | 10 | constructor() { } 11 | 12 | configurarParametros(config: ConfigPrams): HttpParams { 13 | let httpParams = new HttpParams(); 14 | if (config.pagina) { 15 | httpParams = httpParams.set('_page', config.pagina.toString()); 16 | } 17 | if (config.limite) { 18 | httpParams = httpParams.set('_limit', config.limite.toString()); 19 | } 20 | if (config.pesquisa) { 21 | httpParams = httpParams.set('q', config.pesquisa); 22 | } 23 | if (config.campo) { 24 | httpParams = httpParams.set(config.campo.tipo, config.campo.valor.toString()); 25 | } 26 | httpParams = httpParams.set('_sort', 'id'); 27 | httpParams = httpParams.set('_order', 'desc'); 28 | 29 | return httpParams; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/app/core/filmes.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { HttpClient } from '@angular/common/http'; 3 | import { Observable } from 'rxjs'; 4 | import { Filme } from '../shared/models/filme'; 5 | import { ConfigPrams } from '../shared/models/config-prams'; 6 | import { ConfigParamsService } from './config-params.service'; 7 | 8 | const url = 'http://localhost:3000/filmes/'; 9 | 10 | @Injectable({ 11 | providedIn: 'root' 12 | }) 13 | export class FilmesService { 14 | 15 | constructor(private http: HttpClient, 16 | private configService: ConfigParamsService) { } 17 | 18 | salvar(filme: Filme): Observable { 19 | return this.http.post(url, filme); 20 | } 21 | 22 | editar(filme: Filme): Observable { 23 | return this.http.put(url + filme.id, filme); 24 | } 25 | 26 | listar(config: ConfigPrams): Observable { 27 | const configPrams = this.configService.configurarParametros(config); 28 | return this.http.get(url, {params: configPrams}); 29 | } 30 | 31 | visualizar(id: number): Observable { 32 | return this.http.get(url + id); 33 | } 34 | 35 | excluir(id: number): Observable { 36 | return this.http.delete(url + id); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/app/filmes/cadastro-filmes/cadastro-filmes.component.html: -------------------------------------------------------------------------------- 1 | Cadastrar Filme 2 |
3 | 4 |
5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 |
20 |
21 | -------------------------------------------------------------------------------- /src/app/filmes/cadastro-filmes/cadastro-filmes.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RenanRB/curso-angular/b4379b667acf18f2137ee6b28bb56ba5ff58735d/src/app/filmes/cadastro-filmes/cadastro-filmes.component.scss -------------------------------------------------------------------------------- /src/app/filmes/cadastro-filmes/cadastro-filmes.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { FormBuilder, FormGroup, Validators } from '@angular/forms'; 3 | import { MatDialog } from '@angular/material/dialog'; 4 | import { Router, ActivatedRoute } from '@angular/router'; 5 | import { ValidarCamposService } from 'src/app/shared/components/campos/validar-campos.service'; 6 | import { Filme } from 'src/app/shared/models/filme'; 7 | import { FilmesService } from 'src/app/core/filmes.service'; 8 | import { AlertaComponent } from 'src/app/shared/components/alerta/alerta.component'; 9 | import { Alerta } from 'src/app/shared/models/alerta'; 10 | 11 | @Component({ 12 | selector: 'dio-cadastro-filmes', 13 | templateUrl: './cadastro-filmes.component.html', 14 | styleUrls: ['./cadastro-filmes.component.scss'] 15 | }) 16 | export class CadastroFilmesComponent implements OnInit { 17 | 18 | id: number; 19 | cadastro: FormGroup; 20 | generos: Array; 21 | 22 | constructor(public validacao: ValidarCamposService, 23 | public dialog: MatDialog, 24 | private fb: FormBuilder, 25 | private filmeService: FilmesService, 26 | private router: Router, 27 | private activatedRoute: ActivatedRoute) { } 28 | 29 | get f() { 30 | return this.cadastro.controls; 31 | } 32 | 33 | ngOnInit(): void { 34 | this.id = this.activatedRoute.snapshot.params['id']; 35 | if (this.id) { 36 | this.filmeService.visualizar(this.id) 37 | .subscribe((filme: Filme) => this.criarFormulario(filme)); 38 | } else { 39 | this.criarFormulario(this.criarFilmeEmBranco()); 40 | } 41 | 42 | this.generos = ['Ação', 'Romance', 'Aventura', 'Terror', 'Ficção cientifica', 'Comédia', 'Aventura', 'Drama']; 43 | 44 | } 45 | 46 | submit(): void { 47 | this.cadastro.markAllAsTouched(); 48 | if (this.cadastro.invalid) { 49 | return; 50 | } 51 | 52 | const filme = this.cadastro.getRawValue() as Filme; 53 | if (this.id) { 54 | filme.id = this.id; 55 | this.editar(filme); 56 | } else { 57 | this.salvar(filme); 58 | } 59 | } 60 | 61 | reiniciarForm(): void { 62 | this.cadastro.reset(); 63 | } 64 | 65 | private criarFormulario(filme: Filme): void { 66 | this.cadastro = this.fb.group({ 67 | titulo: [filme.titulo, [Validators.required, Validators.minLength(2), Validators.maxLength(256)]], 68 | urlFoto: [filme.urlFoto, [Validators.minLength(10)]], 69 | dtLancamento: [filme.dtLancamento, [Validators.required]], 70 | descricao: [filme.descricao], 71 | nota: [filme.nota, [Validators.required, Validators.min(0), Validators.max(10)]], 72 | urlIMDb: [filme.urlIMDb, [Validators.minLength(10)]], 73 | genero: [filme.genero, [Validators.required]] 74 | }); 75 | } 76 | 77 | private criarFilmeEmBranco(): Filme { 78 | return { 79 | id: null, 80 | titulo: null, 81 | dtLancamento: null, 82 | urlFoto: null, 83 | descricao: null, 84 | nota: null, 85 | urlImdb: null, 86 | genero: null 87 | } as Filme; 88 | } 89 | 90 | private salvar(filme: Filme): void { 91 | this.filmeService.salvar(filme).subscribe(() => { 92 | const config = { 93 | data: { 94 | btnSucesso: 'Ir para a listagem', 95 | btnCancelar: 'Cadastrar um novo filme', 96 | corBtnCancelar: 'primary', 97 | possuirBtnFechar: true 98 | } as Alerta 99 | }; 100 | const dialogRef = this.dialog.open(AlertaComponent, config); 101 | dialogRef.afterClosed().subscribe((opcao: boolean) => { 102 | if (opcao) { 103 | this.router.navigateByUrl('filmes'); 104 | } else { 105 | this.reiniciarForm(); 106 | } 107 | }); 108 | }, 109 | () => { 110 | const config = { 111 | data: { 112 | titulo: 'Erro ao salvar o registro!', 113 | descricao: 'Não conseguimos salvar seu registro, favor tentar novamente mais tarde', 114 | corBtnSucesso: 'warn', 115 | btnSucesso: 'Fechar' 116 | } as Alerta 117 | }; 118 | this.dialog.open(AlertaComponent, config); 119 | }); 120 | } 121 | 122 | private editar(filme: Filme): void { 123 | this.filmeService.editar(filme).subscribe(() => { 124 | const config = { 125 | data: { 126 | descricao: 'Seu registro foi atualizado com sucesso!', 127 | btnSucesso: 'Ir para a listagem', 128 | } as Alerta 129 | }; 130 | const dialogRef = this.dialog.open(AlertaComponent, config); 131 | dialogRef.afterClosed().subscribe(() => this.router.navigateByUrl('filmes')); 132 | }, 133 | () => { 134 | const config = { 135 | data: { 136 | titulo: 'Erro ao editar o registro!', 137 | descricao: 'Não conseguimos editar seu registro, favor tentar novamente mais tarde', 138 | corBtnSucesso: 'warn', 139 | btnSucesso: 'Fechar' 140 | } as Alerta 141 | }; 142 | this.dialog.open(AlertaComponent, config); 143 | }); 144 | } 145 | 146 | } 147 | -------------------------------------------------------------------------------- /src/app/filmes/filmes.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { ReactiveFormsModule, FormsModule } from '@angular/forms'; 4 | 5 | import { InfiniteScrollModule } from 'ngx-infinite-scroll'; 6 | 7 | import { CadastroFilmesComponent } from './cadastro-filmes/cadastro-filmes.component'; 8 | import { MaterialModule } from '../shared/material/material.module'; 9 | import { ListagemFilmesComponent } from './listagem-filmes/listagem-filmes.component'; 10 | import { CamposModule } from '../shared/components/campos/campos.module'; 11 | import { VisualizarFilmesComponent } from './visualizar-filmes/visualizar-filmes.component'; 12 | 13 | @NgModule({ 14 | imports: [ 15 | CommonModule, 16 | MaterialModule, 17 | ReactiveFormsModule, 18 | FormsModule, 19 | CamposModule, 20 | InfiniteScrollModule 21 | ], 22 | declarations: [CadastroFilmesComponent, ListagemFilmesComponent, VisualizarFilmesComponent] 23 | }) 24 | export class FilmesModule { } 25 | -------------------------------------------------------------------------------- /src/app/filmes/listagem-filmes/listagem-filmes.component.html: -------------------------------------------------------------------------------- 1 | Filmes Cadastrados 2 | 3 | 4 |
5 |
6 |
7 | 10 | 11 |
12 |
13 |
14 |
15 | 19 | 20 |
21 |
22 |
23 |
24 | 25 |
29 | 30 | 31 |
32 | {{filme.titulo}} 33 | {{filme.genero}} 34 |
35 | 36 | 37 |

38 | {{filme.descricao || 'Nenhuma descrição informada'}} 39 |

40 |
41 | 42 | 43 | 44 |
45 |
46 | 47 | 48 |

Nenhum registro encontrado!

49 |
50 | -------------------------------------------------------------------------------- /src/app/filmes/listagem-filmes/listagem-filmes.component.scss: -------------------------------------------------------------------------------- 1 | .home-content { 2 | display: flex; 3 | flex-wrap: wrap; 4 | width: calc(100% - 20px); 5 | justify-content: space-between; 6 | padding: 10px; 7 | .home-card { 8 | margin-bottom: 10px; 9 | 10 | } 11 | } 12 | 13 | .filtro-listagem { 14 | width: calc(100% - 50px); 15 | } 16 | 17 | .mat-card-image { 18 | width: 182px; 19 | height: 268px; 20 | margin: auto; 21 | display: block; 22 | } 23 | 24 | @media screen and (min-width: 525px) { 25 | .home-content { 26 | .home-card { 27 | width: calc(50% - 52px); 28 | } 29 | } 30 | } 31 | 32 | @media screen and (min-width: 800px) { 33 | .home-content { 34 | .home-card { 35 | width: calc(33.333% - 52px); 36 | } 37 | } 38 | } 39 | 40 | @media screen and (min-width: 1300px) { 41 | .home-content { 42 | .home-card { 43 | width: calc(25% - 52px); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/app/filmes/listagem-filmes/listagem-filmes.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { FormGroup, FormBuilder } from '@angular/forms'; 3 | import { Router } from '@angular/router'; 4 | import { debounceTime } from 'rxjs/operators'; 5 | import { FilmesService } from 'src/app/core/filmes.service'; 6 | import { Filme } from 'src/app/shared/models/filme'; 7 | import { ConfigPrams } from 'src/app/shared/models/config-prams'; 8 | 9 | @Component({ 10 | selector: 'dio-listagem-filmes', 11 | templateUrl: './listagem-filmes.component.html', 12 | styleUrls: ['./listagem-filmes.component.scss'] 13 | }) 14 | export class ListagemFilmesComponent implements OnInit { 15 | readonly semFoto = 'https://www.termoparts.com.br/wp-content/uploads/2017/10/no-image.jpg'; 16 | 17 | config: ConfigPrams = { 18 | pagina: 0, 19 | limite: 4 20 | }; 21 | filmes: Filme[] = []; 22 | filtrosListagem: FormGroup; 23 | generos: Array; 24 | 25 | constructor(private filmesService: FilmesService, 26 | private fb: FormBuilder, 27 | private router: Router) { } 28 | 29 | ngOnInit(): void { 30 | this.filtrosListagem = this.fb.group({ 31 | texto: [''], 32 | genero: [''] 33 | }); 34 | 35 | this.filtrosListagem.get('texto').valueChanges 36 | .pipe(debounceTime(400)) 37 | .subscribe((val: string) => { 38 | this.config.pesquisa = val; 39 | this.resetarConsulta(); 40 | }); 41 | 42 | this.filtrosListagem.get('genero').valueChanges.subscribe((val: string) => { 43 | this.config.campo = {tipo: 'genero', valor: val}; 44 | this.resetarConsulta(); 45 | }); 46 | 47 | this.generos = ['Ação', 'Romance', 'Aventura', 'Terror', 'Ficção cientifica', 'Comédia', 'Aventura', 'Drama']; 48 | 49 | this.listarFilmes(); 50 | } 51 | 52 | onScroll(): void { 53 | this.listarFilmes(); 54 | } 55 | 56 | abrir(id: number): void { 57 | this.router.navigateByUrl('/filmes/' + id); 58 | } 59 | 60 | private listarFilmes(): void { 61 | this.config.pagina++; 62 | this.filmesService.listar(this.config) 63 | .subscribe((filmes: Filme[]) => this.filmes.push(...filmes)); 64 | } 65 | 66 | private resetarConsulta(): void { 67 | this.config.pagina = 0; 68 | this.filmes = []; 69 | this.listarFilmes(); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/app/filmes/visualizar-filmes/visualizar-filmes.component.css: -------------------------------------------------------------------------------- 1 | .mat-card-image { 2 | width: 182px; 3 | height: 268px; 4 | margin: auto; 5 | display: block; 6 | } 7 | -------------------------------------------------------------------------------- /src/app/filmes/visualizar-filmes/visualizar-filmes.component.html: -------------------------------------------------------------------------------- 1 | 2 | Título: {{filme.titulo}} 3 | Data de lançamento: {{filme.dtLancamento | date: 'dd/MM/yyyy' }} 4 | Gênero: {{filme.genero}} 5 | Descrição: {{filme.descricao}} 6 | Nota: {{filme.nota}} 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |

Nenhum registro encontrado!

18 |
19 | -------------------------------------------------------------------------------- /src/app/filmes/visualizar-filmes/visualizar-filmes.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { ActivatedRoute, Router } from '@angular/router'; 3 | import { MatDialog } from '@angular/material/dialog'; 4 | import { FilmesService } from 'src/app/core/filmes.service'; 5 | import { Filme } from 'src/app/shared/models/filme'; 6 | import { Alerta } from 'src/app/shared/models/alerta'; 7 | import { AlertaComponent } from 'src/app/shared/components/alerta/alerta.component'; 8 | 9 | @Component({ 10 | selector: 'dio-visualizar-filmes', 11 | templateUrl: './visualizar-filmes.component.html', 12 | styleUrls: ['./visualizar-filmes.component.css'] 13 | }) 14 | export class VisualizarFilmesComponent implements OnInit { 15 | readonly semFoto = 'https://www.termoparts.com.br/wp-content/uploads/2017/10/no-image.jpg'; 16 | filme: Filme; 17 | id: number; 18 | 19 | constructor(public dialog: MatDialog, 20 | private activatedRoute: ActivatedRoute, 21 | private router: Router, 22 | private filmesService: FilmesService) { } 23 | 24 | ngOnInit() { 25 | this.id = this.activatedRoute.snapshot.params['id']; 26 | this.visualizar(); 27 | } 28 | 29 | editar(): void { 30 | this.router.navigateByUrl('/filmes/cadastro/' + this.id); 31 | } 32 | 33 | excluir(): void { 34 | const config = { 35 | data: { 36 | titulo: 'Você tem certeza que deseja excluir?', 37 | descricao: 'Caso você tenha certceza que deseja excluir, clique no botão OK', 38 | corBtnCancelar: 'primary', 39 | corBtnSucesso: 'warn', 40 | possuirBtnFechar: true 41 | } as Alerta 42 | }; 43 | const dialogRef = this.dialog.open(AlertaComponent, config); 44 | dialogRef.afterClosed().subscribe((opcao: boolean) => { 45 | if (opcao) { 46 | this.filmesService.excluir(this.id) 47 | .subscribe(() => this.router.navigateByUrl('/filmes')); 48 | } 49 | }); 50 | } 51 | 52 | private visualizar(): void { 53 | this.filmesService.visualizar(this.id).subscribe((filme: Filme) => this.filme = filme); 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /src/app/shared/components/alerta/alerta.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RenanRB/curso-angular/b4379b667acf18f2137ee6b28bb56ba5ff58735d/src/app/shared/components/alerta/alerta.component.css -------------------------------------------------------------------------------- /src/app/shared/components/alerta/alerta.component.html: -------------------------------------------------------------------------------- 1 |

{{alerta.titulo}}

2 |
3 |

{{alerta.descricao}}

4 |
5 |
6 | 12 | 18 |
19 | -------------------------------------------------------------------------------- /src/app/shared/components/alerta/alerta.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit, Inject } from '@angular/core'; 2 | import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; 3 | import { Alerta } from '../../models/alerta'; 4 | 5 | @Component({ 6 | selector: 'dio-alerta', 7 | templateUrl: './alerta.component.html', 8 | styleUrls: ['./alerta.component.css'] 9 | }) 10 | export class AlertaComponent implements OnInit { 11 | 12 | alerta = { 13 | titulo: 'Sucesso!', 14 | descricao: 'Seu registro foi cadastrado com sucesso!', 15 | btnSucesso: 'OK', 16 | btnCancelar: 'Cancelar', 17 | corBtnSucesso: 'accent', 18 | corBtnCancelar: 'warn', 19 | possuirBtnFechar: false 20 | } as Alerta; 21 | 22 | constructor(public dialogRef: MatDialogRef, 23 | @Inject(MAT_DIALOG_DATA) public data: Alerta) { } 24 | 25 | ngOnInit() { 26 | if (this.data) { 27 | this.alerta.titulo = this.data.titulo || this.alerta.titulo; 28 | this.alerta.descricao = this.data.descricao || this.alerta.descricao; 29 | this.alerta.btnSucesso = this.data.btnSucesso || this.alerta.btnSucesso; 30 | this.alerta.btnCancelar = this.data.btnCancelar || this.alerta.btnCancelar; 31 | this.alerta.corBtnSucesso = this.data.corBtnSucesso || this.alerta.corBtnSucesso; 32 | this.alerta.corBtnCancelar = this.data.corBtnCancelar || this.alerta.corBtnCancelar; 33 | this.alerta.possuirBtnFechar = this.data.possuirBtnFechar || this.alerta.possuirBtnFechar; 34 | } 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/app/shared/components/campos/campos.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { InputTextComponent } from './input-text/input-text.component'; 4 | import { InputNumberComponent } from './input-number/input-number.component'; 5 | import { InputDateComponent } from './input-date/input-date.component'; 6 | import { InputTextareaComponent } from './input-textarea/input-textarea.component'; 7 | import { InputSelectComponent } from './input-select/input-select.component'; 8 | import { MaterialModule } from '../../material/material.module'; 9 | import { ReactiveFormsModule, FormsModule } from '@angular/forms'; 10 | 11 | @NgModule({ 12 | declarations: [ 13 | InputTextComponent, 14 | InputNumberComponent, 15 | InputDateComponent, 16 | InputTextareaComponent, 17 | InputSelectComponent 18 | ], 19 | imports: [ 20 | CommonModule, 21 | MaterialModule, 22 | ReactiveFormsModule, 23 | FormsModule 24 | ], 25 | exports: [ 26 | InputTextComponent, 27 | InputNumberComponent, 28 | InputDateComponent, 29 | InputTextareaComponent, 30 | InputSelectComponent 31 | ] 32 | }) 33 | export class CamposModule { } 34 | -------------------------------------------------------------------------------- /src/app/shared/components/campos/input-date/input-date.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RenanRB/curso-angular/b4379b667acf18f2137ee6b28bb56ba5ff58735d/src/app/shared/components/campos/input-date/input-date.component.css -------------------------------------------------------------------------------- /src/app/shared/components/campos/input-date/input-date.component.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 10 | 11 | 12 | Campo obrigatório 13 | 14 |
15 | -------------------------------------------------------------------------------- /src/app/shared/components/campos/input-date/input-date.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input } from '@angular/core'; 2 | import { FormGroup, AbstractControl } from '@angular/forms'; 3 | import { ValidarCamposService } from '../validar-campos.service'; 4 | 5 | @Component({ 6 | selector: 'dio-input-date', 7 | templateUrl: './input-date.component.html', 8 | styleUrls: ['./input-date.component.css'] 9 | }) 10 | export class InputDateComponent { 11 | 12 | @Input() titulo: string; 13 | @Input() formGroup: FormGroup; 14 | @Input() controlName: string; 15 | 16 | constructor(public validacao: ValidarCamposService) { } 17 | 18 | get formControl(): AbstractControl { 19 | return this.formGroup.controls[this.controlName]; 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /src/app/shared/components/campos/input-number/input-number.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RenanRB/curso-angular/b4379b667acf18f2137ee6b28bb56ba5ff58735d/src/app/shared/components/campos/input-number/input-number.component.css -------------------------------------------------------------------------------- /src/app/shared/components/campos/input-number/input-number.component.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 11 | Campo obrigatório 12 | 13 | Campo precisa ter no mínimo o valor {{validacao.lengthValidar(formControl, 'min')}} 14 | 15 | 16 | Campo pode ter no máximo o valor {{validacao.lengthValidar(formControl, 'max')}} 17 | 18 | 19 |
20 | -------------------------------------------------------------------------------- /src/app/shared/components/campos/input-number/input-number.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input } from '@angular/core'; 2 | import { FormGroup, AbstractControl } from '@angular/forms'; 3 | import { ValidarCamposService } from '../validar-campos.service'; 4 | 5 | @Component({ 6 | selector: 'dio-input-number', 7 | templateUrl: './input-number.component.html', 8 | styleUrls: ['./input-number.component.css'] 9 | }) 10 | export class InputNumberComponent { 11 | 12 | @Input() titulo: string; 13 | @Input() formGroup: FormGroup; 14 | @Input() controlName: string; 15 | @Input() minimo = 0; 16 | @Input() maximo = 10; 17 | @Input() step = 1; 18 | 19 | constructor(public validacao: ValidarCamposService) { } 20 | 21 | get formControl(): AbstractControl { 22 | return this.formGroup.controls[this.controlName]; 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/app/shared/components/campos/input-select/input-select.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RenanRB/curso-angular/b4379b667acf18f2137ee6b28bb56ba5ff58735d/src/app/shared/components/campos/input-select/input-select.component.css -------------------------------------------------------------------------------- /src/app/shared/components/campos/input-select/input-select.component.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | {{opcao}} 5 | 6 | Campo obrigatório 7 | 8 |
9 | -------------------------------------------------------------------------------- /src/app/shared/components/campos/input-select/input-select.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input } from '@angular/core'; 2 | import { FormGroup, AbstractControl } from '@angular/forms'; 3 | import { ValidarCamposService } from '../validar-campos.service'; 4 | 5 | @Component({ 6 | selector: 'dio-input-select', 7 | templateUrl: './input-select.component.html', 8 | styleUrls: ['./input-select.component.css'] 9 | }) 10 | export class InputSelectComponent { 11 | 12 | @Input() titulo: string; 13 | @Input() formGroup: FormGroup; 14 | @Input() controlName: string; 15 | @Input() opcoes: Array; 16 | 17 | constructor(public validacao: ValidarCamposService) { } 18 | 19 | get formControl(): AbstractControl { 20 | return this.formGroup.controls[this.controlName]; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/app/shared/components/campos/input-text/input-text.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RenanRB/curso-angular/b4379b667acf18f2137ee6b28bb56ba5ff58735d/src/app/shared/components/campos/input-text/input-text.component.css -------------------------------------------------------------------------------- /src/app/shared/components/campos/input-text/input-text.component.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 7 | Campo obrigatório 8 | 9 | Campo precisa ter no mínimo {{validacao.lengthValidar(formControl, 'minlength')}} caracteres 10 | 11 | 12 | Campo pode ter no máximo {{validacao.lengthValidar(formControl, 'maxlength')}} caracteres 13 | 14 | 15 |
16 | -------------------------------------------------------------------------------- /src/app/shared/components/campos/input-text/input-text.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input } from '@angular/core'; 2 | import { FormGroup, AbstractControl } from '@angular/forms'; 3 | import { ValidarCamposService } from '../validar-campos.service'; 4 | 5 | @Component({ 6 | selector: 'dio-input-text', 7 | templateUrl: './input-text.component.html', 8 | styleUrls: ['./input-text.component.css'] 9 | }) 10 | export class InputTextComponent { 11 | 12 | @Input() titulo: string; 13 | @Input() formGroup: FormGroup; 14 | @Input() controlName: string; 15 | 16 | constructor(public validacao: ValidarCamposService) { } 17 | 18 | get formControl(): AbstractControl { 19 | return this.formGroup.controls[this.controlName]; 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /src/app/shared/components/campos/input-textarea/input-textarea.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RenanRB/curso-angular/b4379b667acf18f2137ee6b28bb56ba5ff58735d/src/app/shared/components/campos/input-textarea/input-textarea.component.css -------------------------------------------------------------------------------- /src/app/shared/components/campos/input-textarea/input-textarea.component.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 8 | Campo obrigatório 9 | 10 | Campo precisa ter no mínimo {{validacao.lengthValidar(formControl, 'minlength')}} caracteres 11 | 12 | 13 | Campo pode ter no máximo {{validacao.lengthValidar(formControl, 'maxlength')}} caracteres 14 | 15 | 16 |
17 | -------------------------------------------------------------------------------- /src/app/shared/components/campos/input-textarea/input-textarea.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input } from '@angular/core'; 2 | import { FormGroup, AbstractControl } from '@angular/forms'; 3 | import { ValidarCamposService } from '../validar-campos.service'; 4 | 5 | @Component({ 6 | selector: 'dio-input-textarea', 7 | templateUrl: './input-textarea.component.html', 8 | styleUrls: ['./input-textarea.component.css'] 9 | }) 10 | export class InputTextareaComponent { 11 | 12 | @Input() titulo: string; 13 | @Input() formGroup: FormGroup; 14 | @Input() controlName: string; 15 | 16 | constructor(public validacao: ValidarCamposService) { } 17 | 18 | get formControl(): AbstractControl { 19 | return this.formGroup.controls[this.controlName]; 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /src/app/shared/components/campos/validar-campos.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { AbstractControl } from '@angular/forms'; 3 | 4 | @Injectable({ 5 | providedIn: 'root' 6 | }) 7 | export class ValidarCamposService { 8 | 9 | constructor() { } 10 | 11 | hasErrorValidar(control: AbstractControl, errorName: string): boolean { 12 | if ((control.dirty || control.touched) && this.hasError(control, errorName)) { 13 | return true; 14 | } 15 | return false; 16 | } 17 | 18 | hasError(control: AbstractControl, errorName: string): boolean { 19 | return control.hasError(errorName); 20 | } 21 | 22 | lengthValidar(control: AbstractControl, errorName: string): number { 23 | const error = control.errors[errorName]; 24 | return error.requiredLength || error.min || error.max || 0; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/app/shared/components/rodape/rodape.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |

Rodapé do site

4 |
5 |
-------------------------------------------------------------------------------- /src/app/shared/components/rodape/rodape.component.scss: -------------------------------------------------------------------------------- 1 | .primary { 2 | color: white; 3 | height: 46px; 4 | display: flex; 5 | align-items: center; 6 | justify-content: space-around; 7 | } -------------------------------------------------------------------------------- /src/app/shared/components/rodape/rodape.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'dio-rodape', 5 | templateUrl: './rodape.component.html', 6 | styleUrls: ['./rodape.component.scss'] 7 | }) 8 | export class RodapeComponent implements OnInit { 9 | 10 | constructor() { } 11 | 12 | ngOnInit() { 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/app/shared/components/topo/topo.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Menu 4 | 5 | Inicial 6 | 7 | Cadastrar novo Filme 8 | 9 | 10 | 11 | 12 | 13 | 16 | Minha Lista de Filmes 17 | 20 | 21 | 22 |
23 | 24 |
25 | 26 | 27 |
28 | 29 |
30 | 31 | 32 | 33 | 37 | 41 | 42 | -------------------------------------------------------------------------------- /src/app/shared/components/topo/topo.component.scss: -------------------------------------------------------------------------------- 1 | .sidenav-container { 2 | height: 100%; 3 | } 4 | 5 | .sidenav { 6 | width: 200px; 7 | box-shadow: 3px 0 6px rgba(0,0,0,.24); 8 | } 9 | 10 | .app-content { 11 | min-height: calc(100vh - 110px); 12 | } 13 | 14 | .app-title { 15 | text-transform: uppercase; 16 | padding-left: 26px; 17 | } 18 | 19 | .header-toolbar { 20 | justify-content: space-between; 21 | } -------------------------------------------------------------------------------- /src/app/shared/components/topo/topo.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit, ViewChild } from '@angular/core'; 2 | import { MatSidenav } from '@angular/material/sidenav'; 3 | 4 | @Component({ 5 | selector: 'dio-topo', 6 | templateUrl: './topo.component.html', 7 | styleUrls: ['./topo.component.scss'] 8 | }) 9 | export class TopoComponent implements OnInit { 10 | @ViewChild('sidenav', {static: false}) sidenav: MatSidenav; 11 | 12 | 13 | constructor() { } 14 | 15 | ngOnInit() { 16 | } 17 | 18 | 19 | openSideNav() { 20 | this.sidenav.open(); 21 | } 22 | 23 | closeSideNav() { 24 | this.sidenav.close(); 25 | } 26 | 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/app/shared/material/material.module.spec.ts: -------------------------------------------------------------------------------- 1 | import { MaterialModule } from './material.module'; 2 | 3 | describe('MaterialModule', () => { 4 | let materialModule: MaterialModule; 5 | 6 | beforeEach(() => { 7 | materialModule = new MaterialModule(); 8 | }); 9 | 10 | it('should create an instance', () => { 11 | expect(materialModule).toBeTruthy(); 12 | }); 13 | }); 14 | -------------------------------------------------------------------------------- /src/app/shared/material/material.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { MatButtonModule } from '@angular/material/button'; 3 | import { MatSidenavModule } from '@angular/material/sidenav'; 4 | import { MatCheckboxModule } from '@angular/material/checkbox'; 5 | import { MatIconModule } from '@angular/material/icon'; 6 | import { MatListModule } from '@angular/material/list'; 7 | import { MatCardModule } from '@angular/material/card'; 8 | import { MatExpansionModule } from '@angular/material/expansion'; 9 | import { MatRadioModule } from '@angular/material/radio'; 10 | import { MatInputModule } from '@angular/material/input'; 11 | import { MatTabsModule } from '@angular/material/tabs'; 12 | import { MatSnackBarModule } from '@angular/material/snack-bar'; 13 | import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; 14 | import { MatBottomSheetModule } from '@angular/material/bottom-sheet'; 15 | import { MatBadgeModule } from '@angular/material/badge'; 16 | import { MatGridListModule } from '@angular/material/grid-list'; 17 | import { MatDialogModule } from '@angular/material/dialog'; 18 | import { MatSelectModule } from '@angular/material/select'; 19 | import { MatChipsModule } from '@angular/material/chips'; 20 | import { MatFormFieldModule } from '@angular/material/form-field'; 21 | import { MatPaginatorModule } from '@angular/material/paginator'; 22 | import { MatSlideToggleModule } from '@angular/material/slide-toggle'; 23 | import { MatSortModule } from '@angular/material/sort'; 24 | import { MatMenuModule } from '@angular/material/menu'; 25 | import { MatTooltipModule } from '@angular/material/tooltip'; 26 | import { MatToolbarModule } from '@angular/material/toolbar'; 27 | import { MatTableModule } from '@angular/material/table'; 28 | import { MatDatepickerModule } from '@angular/material/datepicker'; 29 | import { MatNativeDateModule } from '@angular/material/core'; 30 | 31 | 32 | @NgModule({ 33 | imports: [ 34 | MatIconModule, 35 | MatButtonModule, 36 | MatCheckboxModule, 37 | MatSidenavModule, 38 | MatListModule, 39 | MatTabsModule, 40 | MatCardModule, 41 | MatListModule, 42 | MatCardModule, 43 | MatExpansionModule, 44 | MatRadioModule, 45 | MatInputModule, 46 | MatSnackBarModule, 47 | MatProgressSpinnerModule, 48 | MatBottomSheetModule, 49 | MatBadgeModule, 50 | MatGridListModule, 51 | MatDialogModule, 52 | MatSelectModule, 53 | MatChipsModule, 54 | MatSlideToggleModule, 55 | MatMenuModule, 56 | MatTooltipModule, 57 | MatToolbarModule, 58 | MatTableModule, 59 | MatPaginatorModule, 60 | MatSortModule, 61 | MatFormFieldModule, 62 | MatDatepickerModule, 63 | MatNativeDateModule 64 | ], 65 | exports: [ 66 | MatIconModule, 67 | MatButtonModule, 68 | MatCheckboxModule, 69 | MatSidenavModule, 70 | MatListModule, 71 | MatTabsModule, 72 | MatCardModule, 73 | MatListModule, 74 | MatCardModule, 75 | MatExpansionModule, 76 | MatRadioModule, 77 | MatInputModule, 78 | MatSnackBarModule, 79 | MatProgressSpinnerModule, 80 | MatBottomSheetModule, 81 | MatBadgeModule, 82 | MatGridListModule, 83 | MatDialogModule, 84 | MatSelectModule, 85 | MatChipsModule, 86 | MatSlideToggleModule, 87 | MatMenuModule, 88 | MatTooltipModule, 89 | MatToolbarModule, 90 | MatTableModule, 91 | MatPaginatorModule, 92 | MatSortModule, 93 | MatFormFieldModule, 94 | MatDatepickerModule, 95 | MatNativeDateModule 96 | ] 97 | }) 98 | export class MaterialModule { 99 | } 100 | -------------------------------------------------------------------------------- /src/app/shared/models/alerta.ts: -------------------------------------------------------------------------------- 1 | export interface Alerta { 2 | titulo?: string; 3 | descricao?: string; 4 | btnSucesso?: string; 5 | btnCancelar?: string; 6 | corBtnSucesso?: string; 7 | corBtnCancelar?: string; 8 | possuirBtnFechar?: boolean; 9 | } 10 | -------------------------------------------------------------------------------- /src/app/shared/models/campo-generico.ts: -------------------------------------------------------------------------------- 1 | export interface CampoGenerico { 2 | tipo: string; 3 | valor: any; 4 | } 5 | -------------------------------------------------------------------------------- /src/app/shared/models/config-prams.ts: -------------------------------------------------------------------------------- 1 | import { CampoGenerico } from './campo-generico'; 2 | 3 | export interface ConfigPrams { 4 | pagina?: number; 5 | limite?: number; 6 | pesquisa?: string; 7 | campo?: CampoGenerico; 8 | } 9 | -------------------------------------------------------------------------------- /src/app/shared/models/filme.ts: -------------------------------------------------------------------------------- 1 | export interface Filme { 2 | id?: number; 3 | titulo: string; 4 | urlFoto?: string; 5 | dtLancamento: Date; 6 | descricao?: string; 7 | nota: number; 8 | urlIMDb?: string; 9 | genero: string; 10 | } 11 | -------------------------------------------------------------------------------- /src/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RenanRB/curso-angular/b4379b667acf18f2137ee6b28bb56ba5ff58735d/src/assets/.gitkeep -------------------------------------------------------------------------------- /src/assets/images/angular-material-post.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RenanRB/curso-angular/b4379b667acf18f2137ee6b28bb56ba5ff58735d/src/assets/images/angular-material-post.png -------------------------------------------------------------------------------- /src/assets/styles/reset.scss: -------------------------------------------------------------------------------- 1 | body, div, dl, dt, dd, ul, ol, li, h1, h2, h3, h4, h5, h6, pre, 2 | form, fieldset, input, p, blockquote, table, th, td, embed, object { 3 | padding: 0; 4 | margin: 0; 5 | } 6 | table { 7 | border-collapse: collapse; 8 | border-spacing: 0; 9 | } 10 | fieldset, img, abbr { 11 | border: 0; 12 | } 13 | address, caption, cite, code, dfn, em, 14 | h1, h2, h3, h4, h5, h6, strong, th, var { 15 | font-weight: normal; 16 | font-style: normal; 17 | } 18 | ul { 19 | list-style: none; 20 | } 21 | caption, th { 22 | text-align: left; 23 | } 24 | h1, h2, h3, h4, h5, h6 { 25 | font-size: 1.0em; 26 | } 27 | q:before, q:after { 28 | content: ''; 29 | } 30 | a, ins { 31 | text-decoration: none; 32 | } -------------------------------------------------------------------------------- /src/assets/styles/styles.scss: -------------------------------------------------------------------------------- 1 | @import 'reset'; 2 | @import 'themes'; 3 | 4 | body { 5 | margin: 0; 6 | } 7 | 8 | .center { 9 | margin: 10px auto; 10 | } 11 | 12 | .width50 { 13 | width: 50%; 14 | } 15 | 16 | .width75 { 17 | width: 75%; 18 | } 19 | 20 | .main-div { 21 | display: flex; 22 | justify-content: center; 23 | align-items: center; 24 | } 25 | 26 | .full-width { 27 | width: 100%; 28 | } 29 | 30 | .float-left { 31 | float: left; 32 | } 33 | 34 | .margin10 { 35 | margin: 10px; 36 | } 37 | 38 | .padding10 { 39 | padding: 10px; 40 | } 41 | 42 | .quebrar-linha { 43 | white-space: pre-line; 44 | } 45 | -------------------------------------------------------------------------------- /src/assets/styles/themes.scss: -------------------------------------------------------------------------------- 1 | // define 3 theme color 2 | // mat-palette accepts $palette-name, main, lighter and darker variants 3 | 4 | @import '~@angular/material/theming'; 5 | 6 | $default-theme-primary: mat-palette($mat-cyan, 500, 100, 900); 7 | $default-theme-accent: mat-palette($mat-orange, 800, 300, 200); 8 | $default-theme-warn: mat-palette($mat-red, A700); 9 | 10 | // create theme (use mat-dark-theme for themes with dark backgrounds) 11 | $default-theme: mat-dark-theme( 12 | $default-theme-primary, 13 | $default-theme-accent, 14 | $default-theme-warn 15 | ); 16 | 17 | @include angular-material-theme($default-theme); 18 | 19 | 20 | footer { 21 | background-color: mat-color($default-theme-primary); 22 | } -------------------------------------------------------------------------------- /src/environments/environment.prod.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: true 3 | }; 4 | -------------------------------------------------------------------------------- /src/environments/environment.ts: -------------------------------------------------------------------------------- 1 | // This file can be replaced during build by using the `fileReplacements` array. 2 | // `ng build ---prod` replaces `environment.ts` with `environment.prod.ts`. 3 | // The list of file replacements can be found in `angular.json`. 4 | 5 | export const environment = { 6 | production: false 7 | }; 8 | 9 | /* 10 | * In development mode, to ignore zone related error stack frames such as 11 | * `zone.run`, `zoneDelegate.invokeTask` for easier debugging, you can 12 | * import the following file, but please comment it out in production mode 13 | * because it will have performance impact when throw error 14 | */ 15 | // import 'zone.js/dist/zone-error'; // Included with Angular CLI. 16 | -------------------------------------------------------------------------------- /src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RenanRB/curso-angular/b4379b667acf18f2137ee6b28bb56ba5ff58735d/src/favicon.ico -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Meus Filmes 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration file, see link for more information 2 | // https://karma-runner.github.io/1.0/config/configuration-file.html 3 | 4 | module.exports = function (config) { 5 | config.set({ 6 | basePath: '', 7 | frameworks: ['jasmine', '@angular-devkit/build-angular'], 8 | plugins: [ 9 | require('karma-jasmine'), 10 | require('karma-chrome-launcher'), 11 | require('karma-jasmine-html-reporter'), 12 | require('karma-coverage-istanbul-reporter'), 13 | require('@angular-devkit/build-angular/plugins/karma') 14 | ], 15 | client: { 16 | clearContext: false // leave Jasmine Spec Runner output visible in browser 17 | }, 18 | coverageIstanbulReporter: { 19 | dir: require('path').join(__dirname, '../coverage'), 20 | reports: ['html', 'lcovonly'], 21 | fixWebpackSourcePaths: true 22 | }, 23 | reporters: ['progress', 'kjhtml'], 24 | port: 9876, 25 | colors: true, 26 | logLevel: config.LOG_INFO, 27 | autoWatch: true, 28 | browsers: ['Chrome'], 29 | singleRun: false 30 | }); 31 | }; -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import { enableProdMode } from '@angular/core'; 2 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 3 | 4 | import { AppModule } from './app/app.module'; 5 | import { environment } from './environments/environment'; 6 | 7 | import 'hammerjs'; 8 | 9 | if (environment.production) { 10 | enableProdMode(); 11 | } 12 | 13 | platformBrowserDynamic().bootstrapModule(AppModule) 14 | .catch(err => console.log(err)); 15 | -------------------------------------------------------------------------------- /src/polyfills.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Web Animations `@angular/platform-browser/animations` 3 | * Only required if AnimationBuilder is used within the application and using IE/Edge or Safari. 4 | * Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0). 5 | **/ 6 | // import 'web-animations-js'; // Run `npm install --save web-animations-js`. 7 | 8 | /** 9 | * By default, zone.js will patch all possible macroTask and DomEvents 10 | * user can disable parts of macroTask/DomEvents patch by setting following flags 11 | */ 12 | 13 | // (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame 14 | // (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick 15 | // (window as any).__zone_symbol__BLACK_LISTED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames 16 | 17 | /* 18 | * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js 19 | * with the following flag, it will bypass `zone.js` patch for IE/Edge 20 | */ 21 | // (window as any).__Zone_enable_cross_context_check = true; 22 | 23 | /*************************************************************************************************** 24 | * Zone JS is required by default for Angular itself. 25 | */ 26 | import 'zone.js/dist/zone'; // Included with Angular CLI. 27 | 28 | 29 | 30 | /*************************************************************************************************** 31 | * APPLICATION IMPORTS 32 | */ 33 | -------------------------------------------------------------------------------- /src/test.ts: -------------------------------------------------------------------------------- 1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files 2 | 3 | import 'zone.js/dist/zone-testing'; 4 | import { getTestBed } from '@angular/core/testing'; 5 | import { 6 | BrowserDynamicTestingModule, 7 | platformBrowserDynamicTesting 8 | } from '@angular/platform-browser-dynamic/testing'; 9 | 10 | declare const require: any; 11 | 12 | // First, initialize the Angular testing environment. 13 | getTestBed().initTestEnvironment( 14 | BrowserDynamicTestingModule, 15 | platformBrowserDynamicTesting() 16 | ); 17 | // Then we find all the tests. 18 | const context = require.context('./', true, /\.spec\.ts$/); 19 | // And load the modules. 20 | context.keys().map(context); 21 | -------------------------------------------------------------------------------- /src/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/app", 5 | "types": [] 6 | }, 7 | "exclude": [ 8 | "src/test.ts", 9 | "**/*.spec.ts" 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /src/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/spec", 5 | "types": [ 6 | "jasmine", 7 | "node" 8 | ] 9 | }, 10 | "files": [ 11 | "test.ts", 12 | "polyfills.ts" 13 | ], 14 | "include": [ 15 | "**/*.spec.ts", 16 | "**/*.d.ts" 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /src/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tslint.json", 3 | "rules": { 4 | "directive-selector": [ 5 | true, 6 | "attribute", 7 | "dio", 8 | "camelCase" 9 | ], 10 | "component-selector": [ 11 | true, 12 | "element", 13 | "dio", 14 | "kebab-case" 15 | ] 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "baseUrl": "./", 5 | "downlevelIteration": true, 6 | "importHelpers": true, 7 | "module": "esnext", 8 | "outDir": "./dist/out-tsc", 9 | "sourceMap": true, 10 | "declaration": false, 11 | "moduleResolution": "node", 12 | "emitDecoratorMetadata": true, 13 | "experimentalDecorators": true, 14 | "target": "es2015", 15 | "typeRoots": [ 16 | "node_modules/@types" 17 | ], 18 | "lib": [ 19 | "es2017", 20 | "dom" 21 | ] 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rulesDirectory": [ 3 | "node_modules/codelyzer" 4 | ], 5 | "rules": { 6 | "arrow-return-shorthand": true, 7 | "callable-types": true, 8 | "class-name": true, 9 | "comment-format": [ 10 | true, 11 | "check-space" 12 | ], 13 | "curly": true, 14 | "deprecation": { 15 | "severity": "warn" 16 | }, 17 | "eofline": true, 18 | "forin": true, 19 | "import-blacklist": [ 20 | true, 21 | "rxjs/Rx" 22 | ], 23 | "import-spacing": true, 24 | "indent": [ 25 | true, 26 | "spaces" 27 | ], 28 | "interface-over-type-literal": true, 29 | "label-position": true, 30 | "max-line-length": [ 31 | true, 32 | 140 33 | ], 34 | "member-access": false, 35 | "member-ordering": [ 36 | true, 37 | { 38 | "order": [ 39 | "static-field", 40 | "instance-field", 41 | "static-method", 42 | "instance-method" 43 | ] 44 | } 45 | ], 46 | "no-arg": true, 47 | "no-bitwise": true, 48 | "no-console": [ 49 | true, 50 | "debug", 51 | "info", 52 | "time", 53 | "timeEnd", 54 | "trace" 55 | ], 56 | "no-construct": true, 57 | "no-debugger": true, 58 | "no-duplicate-super": true, 59 | "no-empty": false, 60 | "no-empty-interface": true, 61 | "no-eval": true, 62 | "no-inferrable-types": [ 63 | true, 64 | "ignore-params" 65 | ], 66 | "no-misused-new": true, 67 | "no-non-null-assertion": true, 68 | "no-shadowed-variable": true, 69 | "no-string-literal": false, 70 | "no-string-throw": true, 71 | "no-switch-case-fall-through": true, 72 | "no-trailing-whitespace": true, 73 | "no-unnecessary-initializer": true, 74 | "no-unused-expression": true, 75 | "no-use-before-declare": true, 76 | "no-var-keyword": true, 77 | "object-literal-sort-keys": false, 78 | "one-line": [ 79 | true, 80 | "check-open-brace", 81 | "check-catch", 82 | "check-else", 83 | "check-whitespace" 84 | ], 85 | "prefer-const": true, 86 | "quotemark": [ 87 | true, 88 | "single" 89 | ], 90 | "radix": true, 91 | "semicolon": [ 92 | true, 93 | "always" 94 | ], 95 | "triple-equals": [ 96 | true, 97 | "allow-null-check" 98 | ], 99 | "typedef-whitespace": [ 100 | true, 101 | { 102 | "call-signature": "nospace", 103 | "index-signature": "nospace", 104 | "parameter": "nospace", 105 | "property-declaration": "nospace", 106 | "variable-declaration": "nospace" 107 | } 108 | ], 109 | "unified-signatures": true, 110 | "variable-name": false, 111 | "whitespace": [ 112 | true, 113 | "check-branch", 114 | "check-decl", 115 | "check-operator", 116 | "check-separator", 117 | "check-type" 118 | ], 119 | "no-output-on-prefix": true, 120 | "no-inputs-metadata-property": true, 121 | "no-outputs-metadata-property": true, 122 | "no-host-metadata-property": true, 123 | "no-input-rename": true, 124 | "no-output-rename": true, 125 | "use-lifecycle-interface": true, 126 | "use-pipe-transform-interface": true, 127 | "component-class-suffix": true, 128 | "directive-class-suffix": true 129 | } 130 | } 131 | --------------------------------------------------------------------------------