├── .editorconfig ├── .gitignore ├── LICENSE ├── NOTICE ├── README.md ├── angular.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 │ │ ├── app.component.html │ │ └── app.component.ts │ ├── app.module.ts │ └── components │ │ ├── add │ │ ├── add.component.html │ │ └── add.component.ts │ │ ├── list │ │ ├── list.component.html │ │ └── list.component.ts │ │ └── search │ │ ├── search.component.html │ │ └── search.component.ts ├── assets │ ├── .gitkeep │ └── data.json ├── browserslist ├── environments │ ├── environment.prod.ts │ └── environment.ts ├── favicon.ico ├── index.html ├── karma.conf.js ├── main.ts ├── polyfills.ts ├── styles.css ├── test.ts ├── tsconfig.app.json ├── tsconfig.spec.json └── tslint.json ├── tsconfig.json └── tslint.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 | [*.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 | # profiling files 12 | chrome-profiler-events.json 13 | speed-measure-plugin.json 14 | 15 | # IDEs and editors 16 | /.idea 17 | .project 18 | .classpath 19 | .c9/ 20 | *.launch 21 | .settings/ 22 | *.sublime-workspace 23 | 24 | # IDE - VSCode 25 | .vscode/* 26 | !.vscode/settings.json 27 | !.vscode/tasks.json 28 | !.vscode/launch.json 29 | !.vscode/extensions.json 30 | 31 | # misc 32 | /.sass-cache 33 | /connect.lock 34 | /coverage 35 | /libpeerconnection.log 36 | npm-debug.log 37 | yarn-error.log 38 | testem.log 39 | /typings 40 | 41 | # System Files 42 | .DS_Store 43 | Thumbs.db 44 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | BSD 2-CLAUSE LICENSE 3 | 4 | Copyright 2017 LinkedIn Corporation. 5 | All Rights Reserved. 6 | 7 | Redistribution and use in source and binary forms, with or without 8 | modification, are permitted provided that the following conditions are 9 | met: 10 | 11 | 1. Redistributions of source code must retain the above copyright 12 | notice, this list of conditions and the following disclaimer. 13 | 14 | 2. Redistributions in binary form must reproduce the above copyright 15 | notice, this list of conditions and the following disclaimer in the 16 | documentation and/or other materials provided with the 17 | distribution. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | Copyright 2017 LinkedIn Corporation 2 | All Rights Reserved. 3 | 4 | Licensed under the BSD 2-Clause License (the "License"). 5 | See LICENSE in the project root for license information. 6 | 7 | 8 | This product includes: 9 | 10 | * N/A 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Building a Web Interface with Angular 2 | 3 | This is the repository for my course Building a Web Interface with Angular. The full course is available on LinkedIn Learning. 4 | 5 | ## Instructions 6 | 7 | This repository has branches for each of the videos in the course. You can use the branch pop up menu in github to switch to a specific branch and take a look at the course at that stage. Or you can simply add `/tree/BRANCH_NAME` to the URL to go to the branch you want to peek at. 8 | 9 | ## Branches 10 | 11 | The branches are structured so that they correspond to the videos in the course. So, for example if I name a branch `02_03` then that branch corresponds to the second chapter and the third video in that chapter. The extra letter at the end of the name corresponds to the state of the branch. A `b` means that this is how the code looks at the beginning of the video, an `e` means that is how the code looked at the end of the video. The `master` branch usually has the final state of the code when I finish the course. 12 | 13 | ## Installing 14 | 15 | 1. Make sure you have these installed 16 | - [node.js](http://nodejs.org/) 17 | - [git](http://git-scm.com/) 18 | - [angular cli](https://cli.angular.io/) 19 | 2. Clone this repository into your local machine using the terminal (mac) or Gitbash (PC) `> git clone CLONEURL` 20 | 3. CD to the folder `cd FOLDERNAME` 21 | 4. Run `npm install` to install the project dependencies 22 | 5. Run `ng serve` to start live preview server 23 | 24 | ## Downloading All Branches 25 | 26 | For more advanced users, you can also download all of the branches for this repository. 27 | 28 | 1. `mkdir NAME` 29 | 1. `cd NAME` 30 | 1. `git clone --bare CLONEURL .git` (make sure you add extra .git) 31 | 1. `git config --bool core.bare false` 32 | 1. `git reset --hard` 33 | 1. Run `npm install` to install the project dependencies 34 | 1. Run `ng serve` to start live preview server 35 | 36 | For advanced instructions of how to work with this and other courses with github repos, check out the course: [Learning Git and Github](https://linkedin-learning.pxf.io/c/1252977/449670/8005?subId1=githubrepo&u=https%3A%2F%2Fwww.linkedin.com%2Flearning%2Flearning-git-and-github): 37 | 38 | ## More Stuff 39 | 40 | Check out some of my other courses on [LinkedIn Learning](https://linkedin-learning.pxf.io/c/1252977/449670/8005?subId1=githubrepo&u=https%3A%2F%2Fwww.linkedin.com%2Flearning%2Finstructors%2Fray-villalobos). You can follow me on [LinkedIn](https://www.linkedin.com/in/planetoftheweb/), read [my blog](http://raybo.org), [follow me on twitter](http://twitter.com/planetoftheweb), or check out my [youtube channel](http://youtube.com/planetoftheweb). 41 | -------------------------------------------------------------------------------- /angular.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json", 3 | "version": 1, 4 | "newProjectRoot": "projects", 5 | "projects": { 6 | "angularinterface": { 7 | "root": "", 8 | "sourceRoot": "src", 9 | "projectType": "application", 10 | "prefix": "app", 11 | "schematics": {}, 12 | "architect": { 13 | "build": { 14 | "builder": "@angular-devkit/build-angular:browser", 15 | "options": { 16 | "outputPath": "dist/angularinterface", 17 | "index": "src/index.html", 18 | "main": "src/main.ts", 19 | "polyfills": "src/polyfills.ts", 20 | "tsConfig": "src/tsconfig.app.json", 21 | "assets": ["src/favicon.ico", "src/assets"], 22 | "styles": [ 23 | "node_modules/bootstrap/dist/css/bootstrap.css", 24 | "src/styles.css" 25 | ], 26 | "scripts": [ 27 | "node_modules/jquery/dist/jquery.js", 28 | "node_modules/popper.js/dist/umd/popper.js", 29 | "node_modules/bootstrap/dist/js/bootstrap.js" 30 | ] 31 | }, 32 | "configurations": { 33 | "production": { 34 | "fileReplacements": [ 35 | { 36 | "replace": "src/environments/environment.ts", 37 | "with": "src/environments/environment.prod.ts" 38 | } 39 | ], 40 | "optimization": true, 41 | "outputHashing": "all", 42 | "sourceMap": false, 43 | "extractCss": true, 44 | "namedChunks": false, 45 | "aot": true, 46 | "extractLicenses": true, 47 | "vendorChunk": false, 48 | "buildOptimizer": true, 49 | "budgets": [ 50 | { 51 | "type": "initial", 52 | "maximumWarning": "2mb", 53 | "maximumError": "5mb" 54 | } 55 | ] 56 | } 57 | } 58 | }, 59 | "serve": { 60 | "builder": "@angular-devkit/build-angular:dev-server", 61 | "options": { 62 | "browserTarget": "angularinterface:build" 63 | }, 64 | "configurations": { 65 | "production": { 66 | "browserTarget": "angularinterface:build:production" 67 | } 68 | } 69 | }, 70 | "extract-i18n": { 71 | "builder": "@angular-devkit/build-angular:extract-i18n", 72 | "options": { 73 | "browserTarget": "angularinterface:build" 74 | } 75 | }, 76 | "test": { 77 | "builder": "@angular-devkit/build-angular:karma", 78 | "options": { 79 | "main": "src/test.ts", 80 | "polyfills": "src/polyfills.ts", 81 | "tsConfig": "src/tsconfig.spec.json", 82 | "karmaConfig": "src/karma.conf.js", 83 | "styles": ["src/styles.css"], 84 | "scripts": [], 85 | "assets": ["src/favicon.ico", "src/assets"] 86 | } 87 | }, 88 | "lint": { 89 | "builder": "@angular-devkit/build-angular:tslint", 90 | "options": { 91 | "tsConfig": [ 92 | "src/tsconfig.app.json", 93 | "src/tsconfig.spec.json" 94 | ], 95 | "exclude": ["**/node_modules/**"] 96 | } 97 | } 98 | } 99 | }, 100 | "angularinterface-e2e": { 101 | "root": "e2e/", 102 | "projectType": "application", 103 | "prefix": "", 104 | "architect": { 105 | "e2e": { 106 | "builder": "@angular-devkit/build-angular:protractor", 107 | "options": { 108 | "protractorConfig": "e2e/protractor.conf.js", 109 | "devServerTarget": "angularinterface:serve" 110 | }, 111 | "configurations": { 112 | "production": { 113 | "devServerTarget": "angularinterface:serve:production" 114 | } 115 | } 116 | }, 117 | "lint": { 118 | "builder": "@angular-devkit/build-angular:tslint", 119 | "options": { 120 | "tsConfig": "e2e/tsconfig.e2e.json", 121 | "exclude": ["**/node_modules/**"] 122 | } 123 | } 124 | } 125 | } 126 | }, 127 | "defaultProject": "angularinterface" 128 | } 129 | -------------------------------------------------------------------------------- /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.getTitleText()).toEqual('Welcome to angularinterface!'); 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 | getTitleText() { 9 | return element(by.css('app-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": "angularinterface", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "ng": "ng", 6 | "start": "ng serve", 7 | "build": "ng build", 8 | "test": "ng test", 9 | "lint": "ng lint", 10 | "e2e": "ng e2e" 11 | }, 12 | "private": true, 13 | "dependencies": { 14 | "@angular/animations": "~7.1.0", 15 | "@angular/common": "~7.1.0", 16 | "@angular/compiler": "~7.1.0", 17 | "@angular/core": "~7.1.0", 18 | "@angular/forms": "~7.1.0", 19 | "@angular/platform-browser": "~7.1.0", 20 | "@angular/platform-browser-dynamic": "~7.1.0", 21 | "@angular/router": "~7.1.0", 22 | "core-js": "^2.5.4", 23 | "lodash": "^4.17.11", 24 | "rxjs": "~6.3.3", 25 | "zone.js": "~0.8.26" 26 | }, 27 | "devDependencies": { 28 | "@angular-devkit/build-angular": "~0.11.0", 29 | "@angular/cli": "~7.1.0", 30 | "@angular/compiler-cli": "~7.1.0", 31 | "@angular/language-service": "~7.1.0", 32 | "@fortawesome/angular-fontawesome": "^0.3.0", 33 | "@fortawesome/fontawesome-svg-core": "^1.2.8", 34 | "@fortawesome/free-solid-svg-icons": "^5.5.0", 35 | "@types/jasmine": "~2.8.8", 36 | "@types/jasminewd2": "~2.0.3", 37 | "@types/node": "~8.9.4", 38 | "bootstrap": "^4.1.3", 39 | "codelyzer": "~4.5.0", 40 | "jasmine-core": "~2.99.1", 41 | "jasmine-spec-reporter": "~4.2.1", 42 | "jquery": "^3.3.1", 43 | "karma": "~3.1.1", 44 | "karma-chrome-launcher": "~2.2.0", 45 | "karma-coverage-istanbul-reporter": "~2.0.1", 46 | "karma-jasmine": "~1.1.2", 47 | "karma-jasmine-html-reporter": "^0.2.2", 48 | "popper.js": "^1.14.6", 49 | "protractor": "~5.4.0", 50 | "ts-node": "~7.0.0", 51 | "tslib": "^1.9.3", 52 | "tslint": "~5.11.0", 53 | "typescript": "~3.1.6" 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/app/app-component/app.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 | 6 |
7 | 9 | 15 | 20 |
21 | 22 |
23 |
24 |
25 |
-------------------------------------------------------------------------------- /src/app/app-component/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { HttpClient } from '@angular/common/http'; 3 | import { library } from '@fortawesome/fontawesome-svg-core'; 4 | import { faTimes, faPlus } from '@fortawesome/free-solid-svg-icons'; 5 | import { without, findIndex } from 'lodash'; 6 | 7 | library.add(faTimes, faPlus); 8 | 9 | @Component({ 10 | selector: 'app-root', 11 | templateUrl: './app.component.html' 12 | }) 13 | export class AppComponent implements OnInit { 14 | theList: object[]; 15 | modifiedList: object[]; 16 | orderBy: string; 17 | orderType: string; 18 | lastIndex: number; 19 | 20 | addApt(theApt: any) { 21 | theApt.aptId = this.lastIndex; 22 | this.theList.unshift(theApt); 23 | this.modifiedList.unshift(theApt); 24 | this.lastIndex++; 25 | } 26 | 27 | deleteApt(theApt: object) { 28 | this.theList = without(this.theList, theApt); 29 | this.modifiedList = without(this.theList, theApt); 30 | } 31 | 32 | updateApt(aptInfo) { 33 | let aptIndex: number; 34 | let modifiedIndex: number; 35 | 36 | aptIndex = findIndex(this.theList, { 37 | aptId: aptInfo.theApt.aptId 38 | }); 39 | modifiedIndex = findIndex(this.modifiedList, { 40 | aptId: aptInfo.theApt.aptId 41 | }); 42 | 43 | this.theList[aptIndex][aptInfo.labelName] = aptInfo.newValue; 44 | this.modifiedList[modifiedIndex][aptInfo.labelName] = 45 | aptInfo.newValue; 46 | } 47 | 48 | searchApt(theQuery: string) { 49 | this.modifiedList = this.theList.filter(eachItem => { 50 | return ( 51 | eachItem['petName'] 52 | .toLowerCase() 53 | .includes(theQuery.toLowerCase()) || 54 | eachItem['ownerName'] 55 | .toLowerCase() 56 | .includes(theQuery.toLowerCase()) || 57 | eachItem['aptNotes'] 58 | .toLowerCase() 59 | .includes(theQuery.toLowerCase()) 60 | ); 61 | }); 62 | this.sortItems(); 63 | } 64 | 65 | sortItems() { 66 | let order: number; 67 | if (this.orderType === 'asc') { 68 | order = 1; 69 | } else { 70 | order = -1; 71 | } 72 | 73 | this.modifiedList.sort((a, b) => { 74 | if ( 75 | a[this.orderBy].toLowerCase() < b[this.orderBy].toLowerCase() 76 | ) { 77 | return -1 * order; 78 | } 79 | if ( 80 | a[this.orderBy].toLowerCase() > b[this.orderBy].toLowerCase() 81 | ) { 82 | return 1 * order; 83 | } 84 | }); 85 | } 86 | 87 | orderApt(orderObj) { 88 | this.orderBy = orderObj.orderBy; 89 | this.orderType = orderObj.orderType; 90 | 91 | this.sortItems(); 92 | } 93 | 94 | constructor(private http: HttpClient) { 95 | this.orderBy = 'petName'; 96 | this.orderType = 'asc'; 97 | } 98 | 99 | ngOnInit(): void { 100 | this.lastIndex = 0; 101 | this.http.get('../assets/data.json').subscribe(data => { 102 | this.theList = data.map((item: any) => { 103 | item.aptId = this.lastIndex++; 104 | return item; 105 | }); 106 | this.modifiedList = data; 107 | this.sortItems(); 108 | }); 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /src/app/app.module.ts: -------------------------------------------------------------------------------- 1 | import { BrowserModule } from '@angular/platform-browser'; 2 | import { NgModule } from '@angular/core'; 3 | import { HttpClientModule } from '@angular/common/http'; 4 | import { FormsModule } from '@angular/forms'; 5 | import { FontAwesomeModule } from '@fortawesome/angular-fontawesome'; 6 | 7 | import { AppComponent } from './app-component/app.component'; 8 | import { AddComponent } from './components/add/add.component'; 9 | import { SearchComponent } from './components/search/search.component'; 10 | import { ListComponent } from './components/list/list.component'; 11 | 12 | @NgModule({ 13 | declarations: [ 14 | AppComponent, 15 | AddComponent, 16 | SearchComponent, 17 | ListComponent 18 | ], 19 | imports: [ 20 | BrowserModule, 21 | HttpClientModule, 22 | FormsModule, 23 | FontAwesomeModule 24 | ], 25 | providers: [], 26 | bootstrap: [AppComponent] 27 | }) 28 | export class AppModule {} 29 | -------------------------------------------------------------------------------- /src/app/components/add/add.component.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
5 | Add Appointment 6 |
7 | 8 |
9 | 10 |
14 | 15 |
16 | 17 |
18 | 19 |
20 |
21 | 22 |
23 | 25 |
26 | 28 |
29 |
30 | 31 |
32 | 33 |
34 | 35 |
36 | 37 |
38 | 39 |
40 |
41 | 42 |
43 | 44 |
45 | 47 |
48 |
49 | 50 |
51 |
52 | 54 |
55 |
56 | 57 |
58 |
59 |
-------------------------------------------------------------------------------- /src/app/components/add/add.component.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Component, 3 | OnInit, 4 | Output, 5 | EventEmitter 6 | } from '@angular/core'; 7 | 8 | @Component({ 9 | selector: 'app-add', 10 | templateUrl: './add.component.html' 11 | }) 12 | export class AddComponent implements OnInit { 13 | showForm: boolean; 14 | 15 | @Output() addEvt = new EventEmitter(); 16 | 17 | toggleAptDisplay() { 18 | this.showForm = !this.showForm; 19 | } 20 | 21 | handleAdd(formInfo: any) { 22 | const tempItem: object = { 23 | petName: formInfo.petName, 24 | ownerName: formInfo.ownerName, 25 | aptDate: formInfo.aptDate + ' ' + formInfo.aptTime, 26 | aptNotes: formInfo.aptNotes 27 | }; 28 | this.addEvt.emit(tempItem); 29 | this.showForm = !this.showForm; 30 | } 31 | 32 | constructor() { 33 | this.showForm = true; 34 | } 35 | 36 | ngOnInit() {} 37 | } 38 | -------------------------------------------------------------------------------- /src/app/components/list/list.component.html: -------------------------------------------------------------------------------- 1 |
2 |
4 |
5 | 9 |
10 | 11 |
12 |
13 | {{item.petName}} 15 | {{item.aptDate | date: 'M-d h:mm a'}} 16 |
17 | 18 |
19 | Owner: 20 | {{item.ownerName}} 22 |
23 |
{{item.aptNotes}}
25 |
26 |
27 |
-------------------------------------------------------------------------------- /src/app/components/list/list.component.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Component, 3 | Input, 4 | Output, 5 | EventEmitter 6 | } from '@angular/core'; 7 | 8 | @Component({ 9 | selector: 'app-list', 10 | templateUrl: './list.component.html' 11 | }) 12 | export class ListComponent { 13 | @Input() aptList; 14 | @Output() deleteEvt = new EventEmitter(); 15 | @Output() updateEvt = new EventEmitter(); 16 | 17 | handleDelete(theApt: object) { 18 | this.deleteEvt.emit(theApt); 19 | } 20 | 21 | handleUpdate(theApt: object, labelName: string, newValue: string) { 22 | this.updateEvt.emit({ 23 | theApt: theApt, 24 | labelName: labelName, 25 | newValue: newValue 26 | }); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/app/components/search/search.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | 8 |
9 | 12 | 13 | 31 |
32 |
33 |
34 |
-------------------------------------------------------------------------------- /src/app/components/search/search.component.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Component, 3 | OnInit, 4 | Input, 5 | Output, 6 | EventEmitter 7 | } from '@angular/core'; 8 | 9 | @Component({ 10 | selector: 'app-search', 11 | templateUrl: './search.component.html' 12 | }) 13 | export class SearchComponent implements OnInit { 14 | @Input() orderBy; 15 | @Input() orderType; 16 | @Output() queryEvt = new EventEmitter(); 17 | @Output() orderEvt = new EventEmitter(); 18 | 19 | handleQuery(query: string) { 20 | this.queryEvt.emit(query); 21 | } 22 | 23 | handleSort(orderItems) { 24 | this.orderBy = orderItems.orderBy; 25 | this.orderType = orderItems.orderType; 26 | this.orderEvt.emit(orderItems); 27 | } 28 | 29 | constructor() {} 30 | 31 | ngOnInit() {} 32 | } 33 | -------------------------------------------------------------------------------- /src/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/planetoftheweb/angularinterface/5cc64856a59d9f9ba9813f4227ba17362f241b61/src/assets/.gitkeep -------------------------------------------------------------------------------- /src/assets/data.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "petName": "Pepe", 4 | "ownerName": "Reggie Tupp", 5 | "aptNotes": "It's time for this rabbit's post spaying surgery checkup", 6 | "aptDate": "2018-11-28 13:30" 7 | }, 8 | { 9 | "petName": "Rio", 10 | "ownerName": "Philip Ransu", 11 | "aptNotes": "Rio is up for his next round of vaccinations", 12 | "aptDate": "2018-11-28 10:15" 13 | }, 14 | { 15 | "petName": "Scooter", 16 | "ownerName": "Zachary Heilyn", 17 | "aptNotes": "Scooter has been pawing at his ear and may have an ear infection", 18 | "aptDate": "2018-11-28 14:45" 19 | }, 20 | { 21 | "petName": "Nadalee", 22 | "ownerName": "Krystle Valerija", 23 | "aptNotes": "This dog is coming in for his monthly nail trim and grooming", 24 | "aptDate": "2018-11-28 16:00" 25 | }, 26 | { 27 | "petName": "Scout", 28 | "ownerName": "Nicolette Bardeau", 29 | "aptNotes": "This dog is coming in for his annual checkup and vaccinations", 30 | "aptDate": "2018-11-28 9:00" 31 | }, 32 | { 33 | "petName": "Zera", 34 | "ownerName": "Austin Finnagan", 35 | "aptNotes": "This iguana's is showing signs of dementia associated with his old age", 36 | "aptDate": "2018-11-29 13:15" 37 | }, 38 | { 39 | "petName": "Oddball", 40 | "ownerName": "Howie Cadell", 41 | "aptNotes": "Oddball has a hard lump on right front foot", 42 | "aptDate": "2018-11-29 10:00" 43 | }, 44 | { 45 | "petName": "Millie", 46 | "ownerName": "Freya Terray", 47 | "aptNotes": "MIllie has exhibited signs of an upset stomach and is not eating regularly", 48 | "aptDate": "2018-11-29 11:45" 49 | }, 50 | { 51 | "petName": "Fluffy", 52 | "ownerName": "Tracy Westbay", 53 | "aptNotes": "Fluffy has some matted hair that needs to be groomed", 54 | "aptDate": "2018-11-29 14:30" 55 | }, 56 | { 57 | "petName": "Chyna", 58 | "ownerName": "Sandie Gobnet", 59 | "aptNotes": "This turtle is coming in for a checkup and to be tested for Salmonella", 60 | "aptDate": "2018-11-29 16:00" 61 | }, 62 | { 63 | "petName": "Wesley", 64 | "ownerName": "Nathan Cayden", 65 | "aptNotes": "This dog is returning for his next heartworm treatment visit", 66 | "aptDate": "2018-11-29 8:30" 67 | }, 68 | { 69 | "petName": "Pax", 70 | "ownerName": "Sarah Greer", 71 | "aptNotes": "This senior dog has been sluggish and showing lethargic behavior", 72 | "aptDate": "2018-11-30 10:15" 73 | }, 74 | { 75 | "petName": "Squiggles", 76 | "ownerName": "Madisyn Roope", 77 | "aptNotes": "Squiggles is due for her annual checkup and vaccinations", 78 | "aptDate": "2018-11-30 11:30" 79 | }, 80 | { 81 | "petName": "Lucky", 82 | "ownerName": "Lisa Choy-Wu", 83 | "aptNotes": "This cat has tartar buildup and her owner would like his teeth cleaned", 84 | "aptDate": "2018-11-30 14:30" 85 | }, 86 | { 87 | "petName": "Bailey", 88 | "ownerName": "Leslie Richardson", 89 | "aptNotes": "This cat is suffering from hotspots and dermatitis", 90 | "aptDate": "2018-11-30 15:45" 91 | }, 92 | { 93 | "petName": "Kiko", 94 | "ownerName": "Kathlyn Zlata", 95 | "aptNotes": "Kiko has been exhibiting excessive thirst and weight loss for the past few weeks", 96 | "aptDate": "2018-11-30 9:00" 97 | }, 98 | { 99 | "petName": "Felix", 100 | "ownerName": "Francine Benet", 101 | "aptNotes": "Felix's mom is coming in to follow up on lab work results", 102 | "aptDate": "2018-12-1 13:00" 103 | }, 104 | { 105 | "petName": "Sami", 106 | "ownerName": "Maggie Rickland", 107 | "aptNotes": "Sami has had some changes in his bathroom in habits", 108 | "aptDate": "2018-12-1 10:00" 109 | }, 110 | { 111 | "petName": "Cosmo", 112 | "ownerName": "Jennifer Dawson", 113 | "aptNotes": "Cosmo's mom would like us to check for arthritic conditions and do a routine checkup", 114 | "aptDate": "2018-12-1 11:30" 115 | }, 116 | { 117 | "petName": "Casper", 118 | "ownerName": "Dalania Devitto", 119 | "aptNotes": "This dog is coming in for a nail trim and grooming", 120 | "aptDate": "2018-12-1 15:15" 121 | }, 122 | { 123 | "petName": "Chip", 124 | "ownerName": "Jason Hemlock", 125 | "aptNotes": "This fish has a spotty white patch developing on his back ", 126 | "aptDate": "2018-12-1 8:45" 127 | }, 128 | { 129 | "petName": "Tibbs", 130 | "ownerName": "Shad Cayden", 131 | "aptNotes": "Tibbs has had an ongoing rash and cold symptoms and we are going to run allergy tests", 132 | "aptDate": "2018-12-2 13:30" 133 | }, 134 | { 135 | "petName": "Stich", 136 | "ownerName": "Dennis Nicholback", 137 | "aptNotes": "Stich has been having some stomach issues and is due for his vaccinations", 138 | "aptDate": "2018-12-2 10:15" 139 | }, 140 | { 141 | "petName": "Shadow", 142 | "ownerName": "Audry Topsy", 143 | "aptNotes": "This cat has a red swollen eye with a discharge", 144 | "aptDate": "2018-12-2 15:00" 145 | }, 146 | { 147 | "petName": "Nugget", 148 | "ownerName": "Darla Branson", 149 | "aptNotes": "This little fish Nugget, has a rash on his stomach area", 150 | "aptDate": "2018-12-2 9:00" 151 | } 152 | ] 153 | -------------------------------------------------------------------------------- /src/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 | # 5 | # For IE 9-11 support, please remove 'not' from the last line of the file and adjust as needed 6 | 7 | > 0.5% 8 | last 2 versions 9 | Firefox ESR 10 | not dead 11 | not IE 9-11 -------------------------------------------------------------------------------- /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 | * For easier debugging in development mode, you can import the following file 11 | * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`. 12 | * 13 | * This import should be commented out in production mode because it will have a negative impact 14 | * on performance if an error is thrown. 15 | */ 16 | // import 'zone.js/dist/zone-error'; // Included with Angular CLI. 17 | -------------------------------------------------------------------------------- /src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/planetoftheweb/angularinterface/5cc64856a59d9f9ba9813f4227ba17362f241b61/src/favicon.ico -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Angularinterface 6 | 7 | 8 | 9 | 10 | 11 | 12 |

Wisdom Pet Medicine

13 | 14 | 15 | 16 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /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', 'text-summary'], 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 | if (environment.production) { 8 | enableProdMode(); 9 | } 10 | 11 | platformBrowserDynamic().bootstrapModule(AppModule) 12 | .catch(err => console.error(err)); 13 | -------------------------------------------------------------------------------- /src/polyfills.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This file includes polyfills needed by Angular and is loaded before the app. 3 | * You can add your own extra polyfills to this file. 4 | * 5 | * This file is divided into 2 sections: 6 | * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. 7 | * 2. Application imports. Files imported after ZoneJS that should be loaded before your main 8 | * file. 9 | * 10 | * The current setup is for so-called "evergreen" browsers; the last versions of browsers that 11 | * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera), 12 | * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile. 13 | * 14 | * Learn more in https://angular.io/guide/browser-support 15 | */ 16 | 17 | /*************************************************************************************************** 18 | * BROWSER POLYFILLS 19 | */ 20 | 21 | /** IE9, IE10 and IE11 requires all of the following polyfills. **/ 22 | // import 'core-js/es6/symbol'; 23 | // import 'core-js/es6/object'; 24 | // import 'core-js/es6/function'; 25 | // import 'core-js/es6/parse-int'; 26 | // import 'core-js/es6/parse-float'; 27 | // import 'core-js/es6/number'; 28 | // import 'core-js/es6/math'; 29 | // import 'core-js/es6/string'; 30 | // import 'core-js/es6/date'; 31 | // import 'core-js/es6/array'; 32 | // import 'core-js/es6/regexp'; 33 | // import 'core-js/es6/map'; 34 | // import 'core-js/es6/weak-map'; 35 | // import 'core-js/es6/set'; 36 | 37 | /** 38 | * If the application will be indexed by Google Search, the following is required. 39 | * Googlebot uses a renderer based on Chrome 41. 40 | * https://developers.google.com/search/docs/guides/rendering 41 | **/ 42 | // import 'core-js/es6/array'; 43 | 44 | /** IE10 and IE11 requires the following for NgClass support on SVG elements */ 45 | // import 'classlist.js'; // Run `npm install --save classlist.js`. 46 | 47 | /** IE10 and IE11 requires the following for the Reflect API. */ 48 | // import 'core-js/es6/reflect'; 49 | 50 | /** 51 | * Web Animations `@angular/platform-browser/animations` 52 | * Only required if AnimationBuilder is used within the application and using IE/Edge or Safari. 53 | * Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0). 54 | **/ 55 | // import 'web-animations-js'; // Run `npm install --save web-animations-js`. 56 | 57 | /** 58 | * By default, zone.js will patch all possible macroTask and DomEvents 59 | * user can disable parts of macroTask/DomEvents patch by setting following flags 60 | */ 61 | 62 | // (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame 63 | // (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick 64 | // (window as any).__zone_symbol__BLACK_LISTED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames 65 | 66 | /* 67 | * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js 68 | * with the following flag, it will bypass `zone.js` patch for IE/Edge 69 | */ 70 | // (window as any).__Zone_enable_cross_context_check = true; 71 | 72 | /*************************************************************************************************** 73 | * Zone JS is required by default for Angular itself. 74 | */ 75 | import 'zone.js/dist/zone'; // Included with Angular CLI. 76 | 77 | 78 | /*************************************************************************************************** 79 | * APPLICATION IMPORTS 80 | */ 81 | -------------------------------------------------------------------------------- /src/styles.css: -------------------------------------------------------------------------------- 1 | /** Basic Page CSS **/ 2 | body { 3 | font-weight: 200; 4 | background-color: #337ab7; 5 | } 6 | 7 | header a, 8 | footer a { 9 | color: white; 10 | } 11 | 12 | footer { 13 | font-size: 0.8em; 14 | } 15 | 16 | .bg-primary, 17 | .btn-primary { 18 | background-color: #377bb5 !important; 19 | border: inherit; 20 | } 21 | 22 | .add-appointment .card-body { 23 | display: none; 24 | } 25 | 26 | .apt-addheading { 27 | cursor: pointer; 28 | user-select: none; 29 | } 30 | 31 | .appointment-list { 32 | font-size: 1.1em; 33 | } 34 | 35 | .appointment-list .pet-item { 36 | border-bottom: 1px dotted gray; 37 | } 38 | 39 | .appointment-list .pet-item:last-child { 40 | border-bottom: none; 41 | } 42 | 43 | .appointment-list .pet-name { 44 | font-weight: 600; 45 | color: #337ab7; 46 | font-size: 1.3em; 47 | line-height: 100%; 48 | } 49 | 50 | .appointment-list .label-item { 51 | font-weight: 600; 52 | color: #667b82; 53 | } 54 | 55 | .appointment-list .apt-date { 56 | font-style: italic; 57 | } 58 | 59 | .appointment-list .apt-notes { 60 | line-height: 120%; 61 | } 62 | 63 | .dropdown-item.active, 64 | .dropdown-item:active { 65 | background-color: #a9aeb2; 66 | } 67 | -------------------------------------------------------------------------------- /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 | "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 | "app", 8 | "camelCase" 9 | ], 10 | "component-selector": [ 11 | true, 12 | "element", 13 | "app", 14 | "kebab-case" 15 | ] 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "baseUrl": "./", 5 | "outDir": "./dist/out-tsc", 6 | "sourceMap": true, 7 | "declaration": false, 8 | "module": "es2015", 9 | "moduleResolution": "node", 10 | "emitDecoratorMetadata": true, 11 | "experimentalDecorators": true, 12 | "importHelpers": true, 13 | "target": "es5", 14 | "typeRoots": [ 15 | "node_modules/@types" 16 | ], 17 | "lib": [ 18 | "es2018", 19 | "dom" 20 | ] 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /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-redundant-jsdoc": true, 69 | "no-shadowed-variable": true, 70 | "no-string-literal": false, 71 | "no-string-throw": true, 72 | "no-switch-case-fall-through": true, 73 | "no-trailing-whitespace": true, 74 | "no-unnecessary-initializer": true, 75 | "no-unused-expression": true, 76 | "no-use-before-declare": true, 77 | "no-var-keyword": true, 78 | "object-literal-sort-keys": false, 79 | "one-line": [ 80 | true, 81 | "check-open-brace", 82 | "check-catch", 83 | "check-else", 84 | "check-whitespace" 85 | ], 86 | "prefer-const": true, 87 | "quotemark": [ 88 | true, 89 | "single" 90 | ], 91 | "radix": true, 92 | "semicolon": [ 93 | true, 94 | "always" 95 | ], 96 | "triple-equals": [ 97 | true, 98 | "allow-null-check" 99 | ], 100 | "typedef-whitespace": [ 101 | true, 102 | { 103 | "call-signature": "nospace", 104 | "index-signature": "nospace", 105 | "parameter": "nospace", 106 | "property-declaration": "nospace", 107 | "variable-declaration": "nospace" 108 | } 109 | ], 110 | "unified-signatures": true, 111 | "variable-name": false, 112 | "whitespace": [ 113 | true, 114 | "check-branch", 115 | "check-decl", 116 | "check-operator", 117 | "check-separator", 118 | "check-type" 119 | ], 120 | "no-output-on-prefix": true, 121 | "use-input-property-decorator": true, 122 | "use-output-property-decorator": true, 123 | "use-host-property-decorator": true, 124 | "no-input-rename": true, 125 | "no-output-rename": true, 126 | "use-life-cycle-interface": true, 127 | "use-pipe-transform-interface": true, 128 | "component-class-suffix": true, 129 | "directive-class-suffix": true 130 | } 131 | } 132 | --------------------------------------------------------------------------------