├── .gitignore ├── README.md ├── angular.json ├── angular2-features-list.md ├── build.sh ├── e2e ├── src │ ├── app.e2e-spec.ts │ └── app.po.ts └── tsconfig.e2e.json ├── package-lock.json ├── package.json ├── src ├── app │ ├── app-routing.module.ts │ ├── app.component.html │ ├── app.component.scss │ ├── app.component.spec.ts │ ├── app.component.ts │ ├── app.config.ts │ ├── app.module.ts │ ├── auth │ │ ├── auth-guard.service.ts │ │ ├── auth.service.ts │ │ ├── signin │ │ │ ├── signin.component.html │ │ │ ├── signin.component.scss │ │ │ ├── signin.component.spec.ts │ │ │ └── signin.component.ts │ │ ├── signout │ │ │ ├── signout.component.html │ │ │ ├── signout.component.scss │ │ │ ├── signout.component.spec.ts │ │ │ └── signout.component.ts │ │ └── signup │ │ │ ├── signup.component.html │ │ │ ├── signup.component.scss │ │ │ ├── signup.component.spec.ts │ │ │ └── signup.component.ts │ ├── footer │ │ ├── footer.component.html │ │ ├── footer.component.scss │ │ ├── footer.component.spec.ts │ │ └── footer.component.ts │ ├── header │ │ ├── header.component.html │ │ ├── header.component.scss │ │ ├── header.component.spec.ts │ │ └── header.component.ts │ ├── page-not-found.component.ts │ ├── shared │ │ ├── enum │ │ │ └── status.enum.ts │ │ └── pipes │ │ │ ├── temperature-converter.pipe.spec.ts │ │ │ └── temperature-converter.pipe.ts │ ├── weather-today │ │ ├── weather-today.component.html │ │ ├── weather-today.component.scss │ │ ├── weather-today.component.spec.ts │ │ └── weather-today.component.ts │ └── weather │ │ ├── weather-item │ │ ├── weather-item.component.html │ │ ├── weather-item.component.scss │ │ ├── weather-item.component.spec.ts │ │ └── weather-item.component.ts │ │ ├── weather-list │ │ ├── weather-list.component.html │ │ ├── weather-list.component.scss │ │ ├── weather-list.component.spec.ts │ │ └── weather-list.component.ts │ │ ├── weather-search │ │ ├── weather-search.component.html │ │ ├── weather-search.component.scss │ │ ├── weather-search.component.spec.ts │ │ └── weather-search.component.ts │ │ ├── weather.data.ts │ │ ├── weather.service.ts │ │ └── weather.ts ├── assets │ └── .gitkeep ├── browserslist ├── environments │ ├── environment.prod.ts │ ├── environment.qa.ts │ └── environment.ts ├── favicon.ico ├── index.html ├── karma.conf.js ├── main.ts ├── polyfills.ts ├── sass │ ├── _mixins.scss │ └── _variables.scss ├── styles.scss ├── test.ts ├── tsconfig.app.json ├── tsconfig.spec.json └── tslint.json ├── tsconfig.json └── tslint.json /.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # compiled output 4 | /dist 5 | /tmp 6 | 7 | # dependencies 8 | /node_modules 9 | /bower_components 10 | 11 | # IDEs and editors 12 | /.idea 13 | /.vscode 14 | .project 15 | .classpath 16 | *.launch 17 | .settings/ 18 | 19 | # misc 20 | /.sass-cache 21 | /connect.lock 22 | /coverage/* 23 | /libpeerconnection.log 24 | npm-debug.log 25 | testem.log 26 | /typings 27 | 28 | # e2e 29 | /e2e/*.js 30 | /e2e/*.map 31 | 32 | #System Files 33 | .DS_Store 34 | Thumbs.db 35 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Weather app 2 | =========== 3 | 4 | Weather app is the little web app for displaying the weather report from openweathermap.org using Components, Databinding, HTTP, Services, Observables, HTML5, CSS3, Responsiveness features and more! 5 | 6 | ## App features 7 | 8 | 1. Display today weather 9 | 2. Display forecast weather 10 | 3. Search by city/zip code 11 | 4. Open weather api integration 12 | 5. Convert celsius to fahrenheit 13 | 14 | This project was generated with [angular-cli](https://github.com/angular/angular-cli) version 1.0.0-beta.19-3. 15 | 16 | ## Prerequisites 17 | 1. Install [Node.js®](https://nodejs.org/en/download) and npm 18 | 19 | node -v 20 | 21 | npm -v 22 | 23 | 2. Install Angular cli 24 | 25 | npm install -g @angular/cli 26 | 27 | 3. Install node packages 28 | 29 | cd /go/to/app/directory having package.json 30 | 31 | npm install 32 | 33 | ## Development server 34 | Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files. 35 | 36 | ## Run Production/QA configuration 37 | 38 | #build 39 | 40 | ng build --environment=prod --output-path=build/prod/ 41 | 42 | #shorthand 43 | 44 | ng b -prod --output-path=build/prod/ 45 | 46 | #serve 47 | 48 | ng serve --environment=prod 49 | 50 | ng serve --environment=qa 51 | 52 | #shorthand 53 | 54 | $ ng s -prod 55 | 56 | ## Code scaffolding 57 | 58 | Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive/pipe/service/class`. 59 | 60 | ## Build 61 | 62 | Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `-prod` flag for a production build. 63 | 64 | ## Running unit tests 65 | 66 | Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io). 67 | 68 | ## Running end-to-end tests 69 | 70 | Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/). 71 | Before running the tests make sure you are serving the app via `ng serve`. 72 | 73 | ## Deploying to Github Pages 74 | 75 | Run `ng github-pages:deploy` to deploy to Github Pages. 76 | 77 | ## Further help 78 | 79 | To get more help on the `angular-cli` use `ng --help` or go check out the [Angular-CLI README](https://github.com/angular/angular-cli/blob/master/README.md). 80 | 81 | ## Angular 2 Style Guide 82 | 83 | [Angular 2 Style Guide](https://angular.io/guide/styleguide) 84 | 85 | ## Run all the tslint and codelyzer rules 86 | 87 | Method A - npm run lint 88 | 89 | Method B - Windows based command with backslash 90 | 91 | $ .\node_modules\.bin\tslint -c tslint.json .\src\app\weather\weather.service.ts 92 | 93 | $ .\node_modules\.bin\tslint -c tslint.json .\src\app\*\*.ts 94 | -------------------------------------------------------------------------------- /angular.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json", 3 | "version": 1, 4 | "newProjectRoot": "projects", 5 | "projects": { 6 | "angular-weather-app": { 7 | "root": "", 8 | "sourceRoot": "src", 9 | "projectType": "application", 10 | "prefix": "app", 11 | "schematics": { 12 | "@schematics/angular:component": { 13 | "style": "scss" 14 | } 15 | }, 16 | "architect": { 17 | "build": { 18 | "builder": "@angular-devkit/build-angular:browser", 19 | "options": { 20 | "outputPath": "dist/angular-weather-app", 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 | "src/styles.scss" 31 | ], 32 | "scripts": [], 33 | "es5BrowserSupport": true 34 | }, 35 | "configurations": { 36 | "production": { 37 | "fileReplacements": [ 38 | { 39 | "replace": "src/environments/environment.ts", 40 | "with": "src/environments/environment.prod.ts" 41 | } 42 | ], 43 | "optimization": true, 44 | "outputHashing": "all", 45 | "sourceMap": false, 46 | "extractCss": true, 47 | "namedChunks": false, 48 | "aot": true, 49 | "extractLicenses": true, 50 | "vendorChunk": false, 51 | "buildOptimizer": true, 52 | "budgets": [ 53 | { 54 | "type": "initial", 55 | "maximumWarning": "2mb", 56 | "maximumError": "5mb" 57 | } 58 | ] 59 | } 60 | } 61 | }, 62 | "serve": { 63 | "builder": "@angular-devkit/build-angular:dev-server", 64 | "options": { 65 | "browserTarget": "angular-weather-app:build" 66 | }, 67 | "configurations": { 68 | "production": { 69 | "browserTarget": "angular-weather-app:build:production" 70 | } 71 | } 72 | }, 73 | "extract-i18n": { 74 | "builder": "@angular-devkit/build-angular:extract-i18n", 75 | "options": { 76 | "browserTarget": "angular-weather-app:build" 77 | } 78 | }, 79 | "test": { 80 | "builder": "@angular-devkit/build-angular:karma", 81 | "options": { 82 | "main": "src/test.ts", 83 | "polyfills": "src/polyfills.ts", 84 | "tsConfig": "src/tsconfig.spec.json", 85 | "karmaConfig": "src/karma.conf.js", 86 | "styles": [ 87 | "src/styles.scss" 88 | ], 89 | "scripts": [], 90 | "assets": [ 91 | "src/favicon.ico", 92 | "src/assets" 93 | ] 94 | } 95 | }, 96 | "lint": { 97 | "builder": "@angular-devkit/build-angular:tslint", 98 | "options": { 99 | "tsConfig": [ 100 | "src/tsconfig.app.json", 101 | "src/tsconfig.spec.json" 102 | ], 103 | "exclude": [ 104 | "**/node_modules/**" 105 | ] 106 | } 107 | } 108 | } 109 | }, 110 | "angular-weather-app-e2e": { 111 | "root": "e2e/", 112 | "projectType": "application", 113 | "prefix": "", 114 | "architect": { 115 | "e2e": { 116 | "builder": "@angular-devkit/build-angular:protractor", 117 | "options": { 118 | "protractorConfig": "e2e/protractor.conf.js", 119 | "devServerTarget": "angular-weather-app:serve" 120 | }, 121 | "configurations": { 122 | "production": { 123 | "devServerTarget": "angular-weather-app:serve:production" 124 | } 125 | } 126 | }, 127 | "lint": { 128 | "builder": "@angular-devkit/build-angular:tslint", 129 | "options": { 130 | "tsConfig": "e2e/tsconfig.e2e.json", 131 | "exclude": [ 132 | "**/node_modules/**" 133 | ] 134 | } 135 | } 136 | } 137 | } 138 | }, 139 | "defaultProject": "angular-weather-app" 140 | } -------------------------------------------------------------------------------- /angular2-features-list.md: -------------------------------------------------------------------------------- 1 | 2 | ## Angular features 3 | 4 | 1. Event and properties binding 5 | 2. $event passing as object 6 | 3. Bootstrap link 7 | 4. Routing and guard 8 | 5. Create model/class 9 | 6. Create header component 10 | 7. Shared folder purpose 11 | 8. Detail page - Weather on right hand side 12 | 9. OAuth Login integration with local bowser support 13 | 10. Debugging - web pack and type script though source map debuuging 14 | 11. Component to component binding through input and output declarative 15 | 12. EventEmitter 16 | 13 Type script life cycle and hooks to each phase 17 | 14. Static meesage in entire project 18 | 15. Multilangual 19 | 16. Multiple environment 20 | 17. Paging control -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | #Get Operating System Values 4 | #Get training test 5 | echo "Getting Operating System Values..." 6 | 7 | OS=$(awk '/DISTRIB_ID=/' /etc/*-release | sed 's/DISTRIB_ID=//' | tr '[:upper:]' '[:lower:]') 8 | PWD=$(pwd) 9 | sleep 2 10 | 11 | if [ -z "$OS" ]; 12 | then 13 | OS=$(awk '{print $1}' /etc/redhat-release | tr '[:upper:]' '[:lower:]') 14 | echo $OS 15 | fi 16 | 17 | UID=$(id -u) 18 | USER=$(id -u -n) 19 | 20 | echo "OS:" $OS 21 | echo "USER:" $USER 22 | 23 | #Developer Tools 24 | yum install gcc-c++ make 25 | 26 | #Install Node.js v8 LTS on Enterprise Linux and Fedora including RHEL, CentOS or Fedora 27 | curl --silent --location https://rpm.nodesource.com/setup_8.x | sudo bash - 28 | sudo yum -y install nodejs 29 | 30 | #NVM 31 | env VERSION=`python tools/getnodeversion.py` make install DESTDIR=`nvm_version_path v$VERSION` PREFIX="" 32 | nvm use 8 33 | 34 | # Git set up 35 | sudo yum install git 36 | 37 | #App Dependencies 38 | nvm -v 39 | npm -v 40 | node -v 41 | git --version 42 | 43 | #Fetch git code 44 | cd /opt 45 | git clone https://github.com/amanpatial/angular6-weather-app.git 46 | mkdir ~/projects/ 47 | mv angular6-weather-app ~/projects/angular6-weather-app 48 | cd ~/projects/angular6-weather-app 49 | 50 | #Angular CLI set up 51 | npm install -g @angular/cli 52 | ng -v 53 | 54 | #Install app dependencies 55 | npm install 56 | 57 | #Build and bundle the code to destination directory 58 | ng b -prod --output-path=~/build/angular6-weather-app/prod/ 59 | -------------------------------------------------------------------------------- /e2e/src/app.e2e-spec.ts: -------------------------------------------------------------------------------- 1 | import { AppPage } from './app.po'; 2 | import { browser, logging } from 'protractor'; 3 | 4 | describe('workspace-project App', () => { 5 | let page: AppPage; 6 | 7 | beforeEach(() => { 8 | page = new AppPage(); 9 | }); 10 | 11 | it('should display welcome message', () => { 12 | page.navigateTo(); 13 | expect(page.getTitleText()).toEqual('Welcome to weather app!'); 14 | }); 15 | 16 | afterEach(async () => { 17 | // Assert that there are no errors emitted from the browser 18 | const logs = await browser.manage().logs().get(logging.Type.BROWSER); 19 | expect(logs).not.toContain(jasmine.objectContaining({ 20 | level: logging.Level.SEVERE, 21 | } as logging.Entry)); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /e2e/src/app.po.ts: -------------------------------------------------------------------------------- 1 | import { browser, by, element } from 'protractor'; 2 | 3 | export class AppPage { 4 | navigateTo() { 5 | return browser.get(browser.baseUrl) as Promise; 6 | } 7 | 8 | getTitleText() { 9 | return element(by.css('weather-app-root h1')).getText() as Promise; 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": "angular-weather-app", 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.2.0", 15 | "@angular/common": "~7.2.0", 16 | "@angular/compiler": "~7.2.0", 17 | "@angular/core": "~7.2.0", 18 | "@angular/forms": "~7.2.0", 19 | "@angular/platform-browser": "~7.2.0", 20 | "@angular/platform-browser-dynamic": "~7.2.0", 21 | "@angular/router": "~7.2.0", 22 | "core-js": "^2.5.4", 23 | "rxjs": "^6.4.0", 24 | "rxjs-compat": "^6.4.0", 25 | "tslib": "^1.9.0", 26 | "zone.js": "~0.8.26" 27 | }, 28 | "devDependencies": { 29 | "@angular-devkit/build-angular": "^0.13.10", 30 | "@angular/cli": "~7.3.7", 31 | "@angular/compiler-cli": "~7.2.0", 32 | "@angular/language-service": "~7.2.0", 33 | "@types/jasmine": "~2.8.8", 34 | "@types/jasminewd2": "~2.0.3", 35 | "@types/node": "~8.9.4", 36 | "codelyzer": "~4.5.0", 37 | "jasmine-core": "~2.99.1", 38 | "jasmine-spec-reporter": "~4.2.1", 39 | "karma": "~4.0.0", 40 | "karma-chrome-launcher": "~2.2.0", 41 | "karma-coverage-istanbul-reporter": "~2.0.1", 42 | "karma-jasmine": "~1.1.2", 43 | "karma-jasmine-html-reporter": "^0.2.2", 44 | "protractor": "~5.4.0", 45 | "ts-node": "~7.0.0", 46 | "tslint": "~5.11.0", 47 | "typescript": "~3.2.2" 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/app/app-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { RouterModule, Routes } from '@angular/router'; 3 | import { PageNotFoundComponent } from './page-not-found.component'; 4 | import { WeatherSearchComponent } from './weather/weather-search/weather-search.component'; 5 | import { WeatherTodayComponent } from './weather-today/weather-today.component'; 6 | import { SigninComponent } from './auth/signin/signin.component'; 7 | import { SignupComponent } from './auth/signup/signup.component'; 8 | import { AuthGuard } from './auth/auth-guard.service'; 9 | import { SignoutComponent } from './auth/signout/signout.component'; 10 | 11 | const routes: Routes = [ 12 | { path: 'today', component: WeatherTodayComponent}, 13 | { path: 'forecast', component: WeatherSearchComponent, canActivate: [AuthGuard]}, 14 | { path: 'signin', component: SigninComponent }, 15 | { path: 'signup', component: SignupComponent }, 16 | { path: 'signout', component: SignoutComponent }, 17 | { path: '', redirectTo: '/signin', pathMatch: 'full' }, 18 | { path: '**', component: PageNotFoundComponent } 19 | ]; 20 | 21 | @NgModule({ 22 | imports: [ RouterModule.forRoot(routes) ], 23 | exports: [ RouterModule ] 24 | }) 25 | export class AppRoutingModule { } 26 | -------------------------------------------------------------------------------- /src/app/app.component.html: -------------------------------------------------------------------------------- 1 |

Welcome to {{title}}

2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/app/app.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amanpatial/angular-weather-app/b7266ba0bc7e0e9d508a533b8737562b96c96474/src/app/app.component.scss -------------------------------------------------------------------------------- /src/app/app.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed, async } from '@angular/core/testing'; 2 | import { RouterTestingModule } from '@angular/router/testing'; 3 | import { AppComponent } from './app.component'; 4 | 5 | describe('AppComponent', () => { 6 | beforeEach(async(() => { 7 | TestBed.configureTestingModule({ 8 | imports: [ 9 | RouterTestingModule 10 | ], 11 | declarations: [ 12 | AppComponent 13 | ], 14 | }).compileComponents(); 15 | })); 16 | 17 | it('should create the Weather app', () => { 18 | const fixture = TestBed.createComponent(AppComponent); 19 | const app = fixture.debugElement.componentInstance; 20 | expect(app).toBeTruthy(); 21 | }); 22 | 23 | it(`should have as title 'weather app!'`, () => { 24 | const fixture = TestBed.createComponent(AppComponent); 25 | const app = fixture.debugElement.componentInstance; 26 | expect(app.title).toEqual('weather app!'); 27 | }); 28 | 29 | it('should render title in a h1 tag', () => { 30 | const fixture = TestBed.createComponent(AppComponent); 31 | fixture.detectChanges(); 32 | const compiled = fixture.debugElement.nativeElement; 33 | expect(compiled.querySelector('h1').textContent).toContain('Welcome to weather app!'); 34 | }); 35 | }); 36 | -------------------------------------------------------------------------------- /src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'weather-app-root', 5 | templateUrl: './app.component.html', 6 | }) 7 | export class AppComponent { 8 | title = 'weather app!'; 9 | } 10 | -------------------------------------------------------------------------------- /src/app/app.config.ts: -------------------------------------------------------------------------------- 1 | import { InjectionToken } from '@angular/core'; 2 | 3 | export let APP_CONFIG = new InjectionToken('app.config'); 4 | 5 | export interface IAppConfig { 6 | apiEndpoint: string; 7 | } 8 | 9 | export const AppConfig: IAppConfig = { 10 | apiEndpoint: 'http://localhost:15422/api/' 11 | }; 12 | -------------------------------------------------------------------------------- /src/app/app.module.ts: -------------------------------------------------------------------------------- 1 | import { BrowserModule } from '@angular/platform-browser'; 2 | import { NgModule } from '@angular/core'; 3 | import { FormsModule, FormBuilder, ReactiveFormsModule } from '@angular/forms'; 4 | import { HttpClientModule } from '@angular/common/http'; 5 | import { APP_CONFIG, AppConfig } from './app.config'; 6 | import { AppComponent } from './app.component'; 7 | import { WeatherListComponent } from './weather/weather-list/weather-list.component'; 8 | import { WeatherItemComponent } from './weather/weather-item/weather-item.component'; 9 | import { WeatherSearchComponent } from './weather/weather-search/weather-search.component'; 10 | import { WeatherService } from './weather/weather.service'; 11 | import { HeaderComponent } from './header/header.component'; 12 | import { PageNotFoundComponent } from './page-not-found.component'; 13 | import { AppRoutingModule } from './app-routing.module'; 14 | import { WeatherTodayComponent } from './weather-today/weather-today.component'; 15 | import { SigninComponent } from './auth/signin/signin.component'; 16 | import { SignupComponent } from './auth/signup/signup.component'; 17 | import { AuthService } from './auth/auth.service'; 18 | import { AuthGuard } from './auth/auth-guard.service'; 19 | import { SignoutComponent } from './auth/signout/signout.component'; 20 | import { TemperatureConverterPipe } from './shared/pipes/temperature-converter.pipe'; 21 | import { RouterModule } from '@angular/router'; 22 | 23 | @NgModule({ 24 | 25 | imports: [ 26 | BrowserModule, 27 | FormsModule, 28 | ReactiveFormsModule, 29 | HttpClientModule, 30 | AppRoutingModule, 31 | RouterModule 32 | ], 33 | 34 | declarations: [ 35 | AppComponent, 36 | WeatherSearchComponent, 37 | WeatherListComponent, 38 | WeatherItemComponent, 39 | HeaderComponent, 40 | PageNotFoundComponent, 41 | WeatherTodayComponent, 42 | SigninComponent, 43 | SignupComponent, 44 | SignoutComponent, 45 | TemperatureConverterPipe 46 | ], 47 | 48 | providers: [WeatherService, AuthService, AuthGuard, { provide: APP_CONFIG, useValue: AppConfig }], 49 | bootstrap: [AppComponent] 50 | }) 51 | export class AppModule { } 52 | -------------------------------------------------------------------------------- /src/app/auth/auth-guard.service.ts: -------------------------------------------------------------------------------- 1 | 2 | import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router'; 3 | import { Injectable } from '@angular/core'; 4 | import { AuthService } from './auth.service'; 5 | 6 | @Injectable() 7 | export class AuthGuard implements CanActivate { 8 | constructor(private authService: AuthService) {} 9 | 10 | canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) { 11 | return this.authService.isAuthenticated(); 12 | } 13 | } -------------------------------------------------------------------------------- /src/app/auth/auth.service.ts: -------------------------------------------------------------------------------- 1 | import { Router } from "@angular/router"; 2 | import { Injectable } from "@angular/core"; 3 | 4 | @Injectable() 5 | export class AuthService { 6 | email: string = "aman.patial@gmail.com"; 7 | password: string = "AmanPatial"; 8 | signedIn: boolean = false; 9 | 10 | constructor(private router: Router) {} 11 | 12 | SignIn(email: string, password: string) { 13 | if (this.email === email && this.password === password) { 14 | this.signedIn = true; 15 | this.router.navigate(["/forecast"]); 16 | } 17 | } 18 | SignOut() { 19 | this.signedIn = false; 20 | this.router.navigate(["/today"]); 21 | } 22 | isAuthenticated() { 23 | return this.signedIn; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/app/auth/signin/signin.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 | 6 | 7 |
8 |
9 | 10 | 11 |
12 | 13 |
14 |
15 |
16 | -------------------------------------------------------------------------------- /src/app/auth/signin/signin.component.scss: -------------------------------------------------------------------------------- 1 | /* Use the variables */ 2 | @import '../../../sass/variables'; 3 | @import '../../../sass/mixins'; 4 | 5 | 6 | label { 7 | background-color: $bgcolor; 8 | color: $textcolor; 9 | font-size: $fontsize; 10 | font-family: $fontfamily; 11 | @include text-hide(); 12 | // @include special-text(); //call single mixin 13 | // @include bordered(blue, 4px); // Call mixin with two values 14 | } 15 | 16 | input { 17 | background-color: $bgcolor; 18 | color: $textcolor; 19 | font-size: $fontsize; 20 | font-family: $fontfamily; 21 | } 22 | 23 | button { 24 | background-color: $bgcolor; 25 | color: $textcolor; 26 | font-size: $fontsize; 27 | font-family: $fontfamily; 28 | @include transform(rotate(5deg)); // use the mixin to rotate the button 29 | } 30 | 31 | button-report { 32 | @extend button; 33 | background-color: red; 34 | } 35 | 36 | 37 | #container { 38 | width: $width; 39 | } -------------------------------------------------------------------------------- /src/app/auth/signin/signin.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed} from '@angular/core/testing'; 2 | import { FormsModule, ReactiveFormsModule } from '@angular/forms'; 3 | import { SigninComponent } from './signin.component'; 4 | import { AuthService } from '../auth.service'; 5 | import { AppRoutingModule } from '../../app-routing.module'; 6 | import { RouterTestingModule } from '@angular/router/testing'; 7 | import { BrowserModule } from '@angular/platform-browser'; 8 | 9 | describe('SigninComponent', () => { 10 | let component: SigninComponent; 11 | let fixture: ComponentFixture; 12 | 13 | beforeEach(async(() => { 14 | TestBed.configureTestingModule({ 15 | declarations: [ SigninComponent ], 16 | imports:[BrowserModule, FormsModule, ReactiveFormsModule], 17 | providers: [AuthService, AppRoutingModule, RouterTestingModule] 18 | }) 19 | })); 20 | 21 | beforeAll(()=> { 22 | // runs once for complete unit suites 23 | }) 24 | 25 | // runs before each test 26 | beforeEach(() => { 27 | fixture = TestBed.createComponent(SigninComponent); 28 | component = fixture.componentInstance; 29 | fixture.detectChanges(); 30 | }); 31 | 32 | //runs after each test 33 | afterEach(() => { 34 | // tearn down 35 | // perform clean up activity 36 | }); 37 | 38 | it('should create', () => { 39 | expect(component).toBeTruthy(); 40 | }); 41 | }); 42 | -------------------------------------------------------------------------------- /src/app/auth/signin/signin.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { NgForm } from '@angular/forms'; 3 | import { AuthService } from '../auth.service'; 4 | 5 | @Component({ 6 | selector: 'app-signin', 7 | templateUrl: './signin.component.html', 8 | styleUrls: ['./signin.component.scss'] 9 | }) 10 | export class SigninComponent implements OnInit { 11 | 12 | constructor(private authService: AuthService) { } 13 | 14 | ngOnInit() { 15 | } 16 | 17 | OnSignin(form: NgForm) { 18 | const email = form.value.email; 19 | const password = form.value.password; 20 | console.log('Sign in', email, password); 21 | this.authService.SignIn(email, password); 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /src/app/auth/signout/signout.component.html: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 |
5 | -------------------------------------------------------------------------------- /src/app/auth/signout/signout.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amanpatial/angular-weather-app/b7266ba0bc7e0e9d508a533b8737562b96c96474/src/app/auth/signout/signout.component.scss -------------------------------------------------------------------------------- /src/app/auth/signout/signout.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { SignoutComponent } from './signout.component'; 4 | 5 | describe('SignoutComponent', () => { 6 | let component: SignoutComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ SignoutComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(SignoutComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /src/app/auth/signout/signout.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { AuthService } from '../auth.service'; 3 | 4 | @Component({ 5 | selector: 'app-signout', 6 | templateUrl: './signout.component.html', 7 | styleUrls: ['./signout.component.scss'] 8 | }) 9 | export class SignoutComponent implements OnInit { 10 | 11 | constructor(private authService: AuthService) { } 12 | 13 | ngOnInit() { 14 | } 15 | 16 | OnSignOut() { 17 | console.log('Sign Out'); 18 | this.authService.SignOut(); 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/app/auth/signup/signup.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 |
6 | 7 | 8 | This field is required! 9 |
10 |
11 | 12 | 13 | This field is required! 14 | Please enter a valid email 15 | address! 16 |
17 |
18 | 19 | 20 | This field is required! 21 |
22 |
23 | 26 |
27 | 28 |
29 |
30 |
31 |
32 | -------------------------------------------------------------------------------- /src/app/auth/signup/signup.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amanpatial/angular-weather-app/b7266ba0bc7e0e9d508a533b8737562b96c96474/src/app/auth/signup/signup.component.scss -------------------------------------------------------------------------------- /src/app/auth/signup/signup.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | import { BrowserModule, By } from '@angular/platform-browser'; 3 | import { FormsModule, ReactiveFormsModule } from '@angular/forms'; 4 | import { DebugElement } from '@angular/core'; 5 | import { SignupComponent } from './signup.component'; 6 | 7 | describe('SignupComponent', () => { 8 | let component: SignupComponent; 9 | let fixture: ComponentFixture; 10 | let de: DebugElement; 11 | let el: HTMLElement; 12 | 13 | beforeEach(async(() => { 14 | TestBed.configureTestingModule({ 15 | imports:[BrowserModule, FormsModule, ReactiveFormsModule], 16 | declarations: [ SignupComponent ] 17 | }).compileComponents().then(()=>{ 18 | // create component and test fixture 19 | fixture = TestBed.createComponent(SignupComponent); 20 | // get test component from the fixture 21 | component = fixture.componentInstance; 22 | de = fixture.debugElement.query(By.css('form')); 23 | el = de.nativeElement; 24 | fixture.detectChanges(); 25 | }); 26 | })); 27 | 28 | it('should create', async() => { 29 | expect(component).toBeTruthy(); 30 | }); 31 | 32 | it ('should set submitted to true', async()=>{ 33 | component.onSubmit(); 34 | expect(component.submitted).toBeTruthy(); 35 | }); 36 | 37 | it('should call onSubmit() method', async()=>{ 38 | fixture.detectChanges(); 39 | spyOn(component, 'onSubmit'); 40 | el = fixture.debugElement.query(By.css('button')).nativeElement; 41 | el.click(); 42 | expect(component.onSubmit).toHaveBeenCalledTimes(1); 43 | }); 44 | 45 | it('form should be invalid', async()=>{ 46 | component.signupForm.controls['name'].setValue(''); 47 | component.signupForm.controls['email'].setValue(''); 48 | component.signupForm.controls['password'].setValue(''); 49 | expect(component.signupForm.valid).toBeFalsy(); 50 | }); 51 | 52 | it('form should be valid', async()=>{ 53 | component.signupForm.controls['name'].setValue('aman'); 54 | component.signupForm.controls['email'].setValue('aman.patial@gmail.com'); 55 | component.signupForm.controls['password'].setValue('Am$1245'); 56 | expect(component.signupForm.valid).toBeTruthy(); 57 | }); 58 | 59 | }); 60 | -------------------------------------------------------------------------------- /src/app/auth/signup/signup.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { FormGroup, Validators, FormControl } from '@angular/forms'; 3 | 4 | @Component({ 5 | selector: 'app-signup', 6 | templateUrl: './signup.component.html', 7 | styleUrls: ['./signup.component.scss'] 8 | }) 9 | export class SignupComponent implements OnInit { 10 | 11 | genders = ['male', 'female']; 12 | signupForm: FormGroup; 13 | submitted: boolean = false; 14 | 15 | constructor() { } 16 | 17 | ngOnInit() { 18 | this.signupForm = new FormGroup({ 19 | 'name': new FormControl(null, Validators.required), 20 | 'email': new FormControl(null, [Validators.required, Validators.email]), 21 | 'password': new FormControl(null, [Validators.required]) 22 | }) 23 | 24 | this.signupForm.statusChanges.subscribe( 25 | (status) => console.log(status) 26 | ) 27 | 28 | //set default value 29 | this.signupForm.setValue({ 30 | 'name': 'Aman', 31 | 'email': 'aman.patial@gmail.com2', 32 | 'password': 'AmanPatial' 33 | }) 34 | } 35 | onSubmit() { 36 | this.submitted = true; 37 | console.log('After submit', this.signupForm); 38 | console.log('Values', this.signupForm.value); 39 | //this.signupForm.reset(); 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /src/app/footer/footer.component.html: -------------------------------------------------------------------------------- 1 |

2 | footer works! 3 |

4 | -------------------------------------------------------------------------------- /src/app/footer/footer.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amanpatial/angular-weather-app/b7266ba0bc7e0e9d508a533b8737562b96c96474/src/app/footer/footer.component.scss -------------------------------------------------------------------------------- /src/app/footer/footer.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { FooterComponent } from './footer.component'; 4 | 5 | describe('FooterComponent', () => { 6 | let component: FooterComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ FooterComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(FooterComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /src/app/footer/footer.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-footer', 5 | templateUrl: './footer.component.html', 6 | styleUrls: ['./footer.component.scss'] 7 | }) 8 | export class FooterComponent implements OnInit { 9 | 10 | constructor() { } 11 | 12 | ngOnInit() { 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/app/header/header.component.html: -------------------------------------------------------------------------------- 1 | 9 |
10 | 11 |
-------------------------------------------------------------------------------- /src/app/header/header.component.scss: -------------------------------------------------------------------------------- 1 | 2 | /* Use the variables */ 3 | @import '../../sass/variables'; 4 | 5 | /*.nav { 6 | padding:12px; 7 | margin-left: 7px; 8 | color: blue 9 | }*/ 10 | 11 | 12 | nav { 13 | ul { 14 | margin: 0; 15 | padding: 0; 16 | list-style: none; 17 | } 18 | li { 19 | display: inline-block; 20 | } 21 | a { 22 | display: block; 23 | padding: 6px 12px; 24 | text-decoration: none; 25 | } 26 | } -------------------------------------------------------------------------------- /src/app/header/header.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | import { HeaderComponent } from './header.component'; 3 | 4 | describe('HeaderComponent', () => { 5 | let component: HeaderComponent; 6 | let fixture: ComponentFixture; 7 | 8 | beforeEach(async(() => { 9 | TestBed.configureTestingModule({ 10 | declarations: [ HeaderComponent ] 11 | }) 12 | .compileComponents(); 13 | })); 14 | 15 | beforeEach(() => { 16 | fixture = TestBed.createComponent(HeaderComponent); 17 | component = fixture.componentInstance; 18 | fixture.detectChanges(); 19 | }); 20 | 21 | it('should create', () => { 22 | let fixture = TestBed.createComponent(HeaderComponent); 23 | let app = fixture.debugElement.componentInstance; 24 | expect(app).toBeTruthy(); 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /src/app/header/header.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-header', 5 | templateUrl: './header.component.html', 6 | styleUrls: ['./header.component.scss'] 7 | }) 8 | export class HeaderComponent implements OnInit { 9 | 10 | constructor() { } 11 | 12 | ngOnInit() { 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/app/page-not-found.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { Location } from '@angular/common'; 3 | 4 | @Component({ 5 | selector: 'app-page-not-found', 6 | template: `

Page Not Found.

7 | 8 |
`, 9 | }) 10 | export class PageNotFoundComponent { 11 | constructor(private location: Location) { } 12 | 13 | goBack(): any { 14 | this.location.back(); 15 | } 16 | } 17 | 18 | -------------------------------------------------------------------------------- /src/app/shared/enum/status.enum.ts: -------------------------------------------------------------------------------- 1 | export enum Status { 2 | Active, 3 | Inactive 4 | } 5 | -------------------------------------------------------------------------------- /src/app/shared/pipes/temperature-converter.pipe.spec.ts: -------------------------------------------------------------------------------- 1 | import { TemperatureConverterPipe } from './temperature-converter.pipe'; 2 | 3 | describe('Pipe: TemperatureConverterPipe', () => { 4 | it('create an instance', () => { 5 | const tempConverterPipe = new TemperatureConverterPipe(); 6 | expect(tempConverterPipe).toBeTruthy(); 7 | }); 8 | it('should convert celcius to farenhiet', () => { 9 | const tempConverterPipe = new TemperatureConverterPipe(); 10 | expect(tempConverterPipe.transform(100, 'F')).toEqual('212.00'); 11 | }); 12 | it('should convert farenhiet to celcius', () => { 13 | const tempConverterPipe = new TemperatureConverterPipe(); 14 | expect(tempConverterPipe.transform(32, 'C')).toEqual('0.00'); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /src/app/shared/pipes/temperature-converter.pipe.ts: -------------------------------------------------------------------------------- 1 | import { Pipe, PipeTransform } from '@angular/core'; 2 | 3 | @Pipe({ 4 | name: 'temperatureConverter' 5 | }) 6 | export class TemperatureConverterPipe implements PipeTransform { 7 | 8 | transform(value: number, unit: string) { 9 | if (value && !isNaN(value)) { 10 | if (unit === 'F') { 11 | var tempareature = (value * 9 / 5) + 32; 12 | return tempareature.toFixed(2); 13 | } 14 | if (unit === 'C') { 15 | var tempareature = (value - 32) * 5 / 9; 16 | return tempareature.toFixed(2); 17 | } 18 | } 19 | return; 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /src/app/weather-today/weather-today.component.html: -------------------------------------------------------------------------------- 1 |

2 | weather-today works! 3 |

4 | -------------------------------------------------------------------------------- /src/app/weather-today/weather-today.component.scss: -------------------------------------------------------------------------------- 1 | @import '../../sass/variables'; 2 | 3 | p { 4 | color: $header-color; 5 | } -------------------------------------------------------------------------------- /src/app/weather-today/weather-today.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { WeatherTodayComponent } from './weather-today.component'; 4 | 5 | describe('WeatherTodayComponent', () => { 6 | let component: WeatherTodayComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ WeatherTodayComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(WeatherTodayComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /src/app/weather-today/weather-today.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-weather-today', 5 | templateUrl: './weather-today.component.html', 6 | styleUrls: ['./weather-today.component.scss'] 7 | }) 8 | export class WeatherTodayComponent implements OnInit { 9 | 10 | constructor() { } 11 | 12 | ngOnInit() { 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/app/weather/weather-item/weather-item.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |

{{ weather.weather[0].main }}

4 |

{{ weather.weather[0].description | uppercase }}

5 |

Wind speed (meter/sec): {{ weather.wind.speed }}

6 | Temperature (celsius): {{ weather.main.temp }}
7 | Temperature (Fahrenheit): {{ ( weather.main.temp | temperatureConverter:'F') }} 8 | 9 |

{{ weather.dt_txt | date:'medium' }}

10 | Detail Report 11 |
12 |
13 | -------------------------------------------------------------------------------- /src/app/weather/weather-item/weather-item.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amanpatial/angular-weather-app/b7266ba0bc7e0e9d508a533b8737562b96c96474/src/app/weather/weather-item/weather-item.component.scss -------------------------------------------------------------------------------- /src/app/weather/weather-item/weather-item.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | import { WeatherItemComponent } from './weather-item.component'; 3 | import { TemperatureConverterPipe } from '../../shared/pipes/temperature-converter.pipe'; 4 | import { RouterTestingModule } from '@angular/router/testing'; 5 | 6 | describe('WeatherItemComponent', () => { 7 | let component: WeatherItemComponent; 8 | let fixture: ComponentFixture; 9 | 10 | beforeEach(async(() => { 11 | TestBed.configureTestingModule({ 12 | imports: [RouterTestingModule], 13 | declarations: [ WeatherItemComponent, TemperatureConverterPipe], 14 | }) 15 | .compileComponents(); 16 | })); 17 | 18 | beforeEach(() => { 19 | fixture = TestBed.createComponent(WeatherItemComponent); 20 | component = fixture.componentInstance; 21 | fixture.detectChanges(); 22 | }); 23 | 24 | it('should create', () => { 25 | expect(component).toBeTruthy(); 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /src/app/weather/weather-item/weather-item.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input, Output, EventEmitter } from '@angular/core'; 2 | import { Weather } from '../weather'; 3 | 4 | @Component({ 5 | selector: 'weather-item', 6 | templateUrl: './weather-item.component.html' 7 | }) 8 | export class WeatherItemComponent { 9 | // tslint:disable-next-line:no-input-rename 10 | @Input('WeatherItem') weather: Weather; 11 | //declare event for subscription 12 | @Output() weatherItemChanges = new EventEmitter(); 13 | 14 | ngOnInit(): any { 15 | } 16 | 17 | selectedWeatherItem(event: Event) { 18 | var item = (event.target); 19 | console.log('Selected div element', item); 20 | //Raise event 21 | this.weatherItemChanges.emit(item.innerHTML); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/app/weather/weather-list/weather-list.component.html: -------------------------------------------------------------------------------- 1 | You have selected weather record {{ selectedItem }} 2 | 4 | 5 | -------------------------------------------------------------------------------- /src/app/weather/weather-list/weather-list.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amanpatial/angular-weather-app/b7266ba0bc7e0e9d508a533b8737562b96c96474/src/app/weather/weather-list/weather-list.component.scss -------------------------------------------------------------------------------- /src/app/weather/weather-list/weather-list.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { WeatherListComponent } from './weather-list.component'; 4 | 5 | describe('WeatherListComponent', () => { 6 | let component: WeatherListComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ WeatherListComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(WeatherListComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /src/app/weather/weather-list/weather-list.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit, Input } from '@angular/core'; 2 | import { WeatherService } from '../weather.service'; 3 | 4 | @Component({ 5 | selector: 'weather-list', 6 | templateUrl: './weather-list.component.html', 7 | styleUrls: ['./weather-list.component.scss'], 8 | providers: [WeatherService] 9 | }) 10 | 11 | export class WeatherListComponent implements OnInit { 12 | @Input('WeatherForecastList') weathers: any; 13 | errorMessage: string; 14 | selectedItem: String = 'nothing'; 15 | 16 | constructor() { 17 | } 18 | 19 | ngOnInit(): any { 20 | } 21 | 22 | updateSelectedWeatherItem(event: any) { 23 | console.log('Recieved selected weather item in list component', event); 24 | this.selectedItem = event; 25 | } 26 | 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/app/weather/weather-search/weather-search.component.html: -------------------------------------------------------------------------------- 1 | 17 | -------------------------------------------------------------------------------- /src/app/weather/weather-search/weather-search.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amanpatial/angular-weather-app/b7266ba0bc7e0e9d508a533b8737562b96c96474/src/app/weather/weather-search/weather-search.component.scss -------------------------------------------------------------------------------- /src/app/weather/weather-search/weather-search.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | import { WeatherSearchComponent } from './weather-search.component'; 3 | import {FormsModule} from '@angular/forms'; 4 | import { WeatherListComponent } from '../weather-list/weather-list.component'; 5 | import { WeatherItemComponent } from '../weather-item/weather-item.component'; 6 | import { NO_ERRORS_SCHEMA } from '@angular/core'; 7 | 8 | describe('WeatherSearchComponent', () => { 9 | let component: WeatherSearchComponent; 10 | let fixture: ComponentFixture; 11 | 12 | beforeEach(async(() => { 13 | TestBed.configureTestingModule({ 14 | imports: [FormsModule], 15 | declarations: [ WeatherSearchComponent, WeatherListComponent, WeatherItemComponent], 16 | schemas: [ NO_ERRORS_SCHEMA] 17 | }) 18 | .compileComponents(); 19 | })); 20 | 21 | beforeEach(() => { 22 | fixture = TestBed.createComponent(WeatherSearchComponent); 23 | component = fixture.componentInstance; 24 | fixture.detectChanges(); 25 | 26 | }); 27 | 28 | 29 | }); 30 | -------------------------------------------------------------------------------- /src/app/weather/weather-search/weather-search.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { WeatherService } from '../weather.service'; 3 | 4 | 5 | @Component({ 6 | selector: 'weather-search', 7 | templateUrl: './weather-search.component.html', 8 | }) 9 | export class WeatherSearchComponent implements OnInit { 10 | 11 | errorMessage: string; 12 | weatherForecastData: any; 13 | disabledForecastButton: boolean; 14 | cityName: string; 15 | 16 | constructor(private _weatherService: WeatherService) { 17 | } 18 | 19 | ngOnInit() { 20 | } 21 | onSubmit(cityName: string) { 22 | console.log(cityName); 23 | this._weatherService.getWeatherForecast(cityName) 24 | .subscribe(data => { this.weatherForecastData = data.list }, error => this.errorMessage = error); 25 | } 26 | 27 | onSearchLocation(cityName: string) { 28 | this.disabledForecastButton = false; 29 | console.log(cityName); 30 | } 31 | 32 | onSubmitDatabinding() { 33 | console.log('Inside the two way', this.cityName); 34 | this._weatherService.getWeatherForecast(this.cityName) 35 | .subscribe(data => { this.weatherForecastData = data.list }, error => this.errorMessage = error); 36 | this.onResetControls(); 37 | } 38 | 39 | onSearchLocationWithEvent(event: Event) { 40 | console.log('Control event value', (event.target).value); 41 | this.cityName = (event.target).value; 42 | this.disabledForecastButton = false; 43 | } 44 | 45 | onResetControls() { 46 | this.cityName = ''; 47 | this.disabledForecastButton = true; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/app/weather/weather.data.ts: -------------------------------------------------------------------------------- 1 | import { Weather } from './weather'; 2 | export const WEATHER_LIST: Weather[] = [ 3 | new Weather('Japan', 'CLoudy', 34), 4 | new Weather('China', 'Rainy', 24), 5 | new Weather('IN', 'Clear', 14), 6 | ]; 7 | 8 | -------------------------------------------------------------------------------- /src/app/weather/weather.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable, Inject } from '@angular/core'; 2 | import { WEATHER_LIST } from './weather.data'; 3 | import { HttpClient } from '@angular/common/http'; 4 | import 'rxjs/Rx'; 5 | import { environment } from '../../environments/environment'; 6 | import { Observable } from 'rxjs'; 7 | 8 | @Injectable() 9 | export class WeatherService { 10 | constructor(private http: HttpClient) { 11 | console.log('Production', environment.production); 12 | } 13 | 14 | getWeatherItems() { 15 | return WEATHER_LIST; 16 | } 17 | 18 | getWeatheritemsbyCity(cityName: string): Observable { 19 | return this.http.get( 20 | environment.baseUrl + 21 | 'weather?q=' + cityName + 22 | '&appid=' + environment.appId + 23 | '&units=' + environment.units 24 | ) 25 | } 26 | 27 | getWeatherForecast(cityName: string): Observable { 28 | return this.http.get( 29 | environment.baseUrl + 30 | 'forecast?q=' + cityName + 31 | '&appid=' + environment.appId + 32 | '&units=' + environment.units 33 | ) 34 | //.catch(this.handleError); 35 | } 36 | 37 | private extractData(res: any) { 38 | let body = res.json(); 39 | return body.list || {}; 40 | } 41 | 42 | private handleError(error: any) { 43 | // In a real world app, we might use a remote logging infrastructure 44 | let errMsg: string; 45 | errMsg = error.message ? error.message : error.toString(); 46 | console.error(errMsg); 47 | return Observable.throw(errMsg); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/app/weather/weather.ts: -------------------------------------------------------------------------------- 1 | export class Weather { 2 | constructor( 3 | public city: string, 4 | public description: string, 5 | public temperature: number) { 6 | this.city = city; 7 | this.description = description; 8 | this.temperature = temperature; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amanpatial/angular-weather-app/b7266ba0bc7e0e9d508a533b8737562b96c96474/src/assets/.gitkeep -------------------------------------------------------------------------------- /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 | appId: '45f4dd45e0f724512ba044c5a2caf4bc', 4 | baseUrl: 'http://api.openweathermap.org/data/2.5/', 5 | units: 'metric' 6 | }; 7 | -------------------------------------------------------------------------------- /src/environments/environment.qa.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: false, 3 | appId: '45f4dd45e0f724512ba044c5a2caf4bc', 4 | baseUrl: 'http://api.openweathermap.org/data/2.5/', 5 | units: 'metric' 6 | }; 7 | -------------------------------------------------------------------------------- /src/environments/environment.ts: -------------------------------------------------------------------------------- 1 | // The file contents for the current environment will overwrite these during build. 2 | // The build system defaults to the dev environment which uses `environment.ts`, but if you do 3 | // `ng build --env=prod` then `environment.prod.ts` will be used instead. 4 | // The list of which env maps to which file can be found in `angular-cli.json`. 5 | 6 | export const environment = { 7 | production: false, 8 | appId: '45f4dd45e0f724512ba044c5a2caf4bc', 9 | baseUrl: 'http://api.openweathermap.org/data/2.5/', 10 | units: 'imperial' 11 | }; 12 | -------------------------------------------------------------------------------- /src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amanpatial/angular-weather-app/b7266ba0bc7e0e9d508a533b8737562b96c96474/src/favicon.ico -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Angular Weather App 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | Loading... 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /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/angular-weather-app'), 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 | restartOnFileChange: true 31 | }); 32 | }; 33 | -------------------------------------------------------------------------------- /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 | /** IE10 and IE11 requires the following for NgClass support on SVG elements */ 22 | // import 'classlist.js'; // Run `npm install --save classlist.js`. 23 | 24 | /** 25 | * Web Animations `@angular/platform-browser/animations` 26 | * Only required if AnimationBuilder is used within the application and using IE/Edge or Safari. 27 | * Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0). 28 | */ 29 | // import 'web-animations-js'; // Run `npm install --save web-animations-js`. 30 | 31 | /** 32 | * By default, zone.js will patch all possible macroTask and DomEvents 33 | * user can disable parts of macroTask/DomEvents patch by setting following flags 34 | * because those flags need to be set before `zone.js` being loaded, and webpack 35 | * will put import in the top of bundle, so user need to create a separate file 36 | * in this directory (for example: zone-flags.ts), and put the following flags 37 | * into that file, and then add the following code before importing zone.js. 38 | * import './zone-flags.ts'; 39 | * 40 | * The flags allowed in zone-flags.ts are listed here. 41 | * 42 | * The following flags will work for all browsers. 43 | * 44 | * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame 45 | * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick 46 | * (window as any).__zone_symbol__BLACK_LISTED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames 47 | * 48 | * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js 49 | * with the following flag, it will bypass `zone.js` patch for IE/Edge 50 | * 51 | * (window as any).__Zone_enable_cross_context_check = true; 52 | * 53 | */ 54 | 55 | /*************************************************************************************************** 56 | * Zone JS is required by default for Angular itself. 57 | */ 58 | import 'zone.js/dist/zone'; // Included with Angular CLI. 59 | 60 | 61 | /*************************************************************************************************** 62 | * APPLICATION IMPORTS 63 | */ 64 | -------------------------------------------------------------------------------- /src/sass/_mixins.scss: -------------------------------------------------------------------------------- 1 | @import './_variables.scss'; 2 | /* create the css code that is to be reused throughout the website. */ 3 | 4 | // create mixin to reuse 5 | @mixin important-text { 6 | color: red; 7 | font-size: 25px; 8 | font-weight: bold; 9 | border: 1px solid blue; 10 | } 11 | 12 | @mixin special-border { 13 | border: 2px solid green; 14 | } 15 | 16 | // mixin includes other mixins 17 | @mixin special-text { 18 | @include important-text; 19 | @include special-border; 20 | } 21 | 22 | /* Define mixin with two arguments */ 23 | @mixin bordered($color, $width) { 24 | border: $width solid $color; 25 | } 26 | 27 | // Vendor prefixes 28 | @mixin transform($property) { 29 | -webkit-transform: $property; 30 | -ms-transform: $property; 31 | transform: $property; 32 | } 33 | 34 | @mixin no-word-wrap() { 35 | -moz-hyphens: auto; 36 | -ms-hyphens: auto; 37 | -webkit-hyphens: auto; 38 | hyphens: auto; 39 | overflow: hidden; 40 | overflow-wrap: normal; 41 | text-overflow: ellipsis; 42 | white-space: nowrap; 43 | word-break: normal; 44 | } 45 | 46 | @mixin hide-text() { 47 | font: 0/0 a; 48 | color: transparent; 49 | text-shadow: none; 50 | text-decoration: none; 51 | background-color: transparent; 52 | border: 0; 53 | } 54 | 55 | @mixin text-hide() { 56 | @include hide-text(); 57 | } 58 | -------------------------------------------------------------------------------- /src/sass/_variables.scss: -------------------------------------------------------------------------------- 1 | /* Define standard variables and values for app */ 2 | $bgcolor: lightblue; 3 | $textcolor: black; 4 | $fontsize: 12px; 5 | $fontfamily: Helvetica, sans-serif; 6 | $header-color: green; 7 | $main-color: green; 8 | $second-color: yellow; 9 | $width:680px; -------------------------------------------------------------------------------- /src/styles.scss: -------------------------------------------------------------------------------- 1 | /* You can add global styles to this file, and also import other style files */ 2 | 3 | 4 | body { 5 | color: darkgrey; 6 | } 7 | 8 | button { 9 | font-size: 12px; 10 | border-radius: 4px; 11 | background-color: white; 12 | cursor: pointer; 13 | border: none; 14 | } 15 | 16 | input { 17 | border: 1px solid lightgrey; 18 | } 19 | 20 | body, h1, h2, h3, h4, h5, h6, p, ol, ul { 21 | margin: 0; 22 | padding: 0; 23 | font-weight: normal; 24 | } 25 | 26 | /*nav { 27 | ul { 28 | margin: 0; 29 | padding: 0; 30 | list-style: none; 31 | } 32 | li { 33 | display: inline-block; 34 | } 35 | a { 36 | display: block; 37 | padding: 6px 12px; 38 | text-decoration: none; 39 | } 40 | } */ -------------------------------------------------------------------------------- /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 | "extends": "tslint:recommended", 3 | "rulesDirectory": [ 4 | "codelyzer" 5 | ], 6 | "rules": { 7 | "array-type": false, 8 | "arrow-parens": false, 9 | "deprecation": { 10 | "severity": "warn" 11 | }, 12 | "import-blacklist": [ 13 | true, 14 | "rxjs/Rx" 15 | ], 16 | "interface-name": false, 17 | "max-classes-per-file": false, 18 | "max-line-length": [ 19 | true, 20 | 140 21 | ], 22 | "member-access": false, 23 | "member-ordering": [ 24 | true, 25 | { 26 | "order": [ 27 | "static-field", 28 | "instance-field", 29 | "static-method", 30 | "instance-method" 31 | ] 32 | } 33 | ], 34 | "no-consecutive-blank-lines": false, 35 | "no-console": [ 36 | true, 37 | "debug", 38 | "info", 39 | "time", 40 | "timeEnd", 41 | "trace" 42 | ], 43 | "no-empty": false, 44 | "no-inferrable-types": [ 45 | true, 46 | "ignore-params" 47 | ], 48 | "no-non-null-assertion": true, 49 | "no-redundant-jsdoc": true, 50 | "no-switch-case-fall-through": true, 51 | "no-use-before-declare": true, 52 | "no-var-requires": false, 53 | "object-literal-key-quotes": [ 54 | true, 55 | "as-needed" 56 | ], 57 | "object-literal-sort-keys": false, 58 | "ordered-imports": false, 59 | "quotemark": [ 60 | true, 61 | "single" 62 | ], 63 | "trailing-comma": false, 64 | "no-output-on-prefix": true, 65 | "use-input-property-decorator": true, 66 | "use-output-property-decorator": true, 67 | "use-host-property-decorator": true, 68 | "no-input-rename": true, 69 | "no-output-rename": true, 70 | "use-life-cycle-interface": true, 71 | "use-pipe-transform-interface": true, 72 | "component-class-suffix": true, 73 | "directive-class-suffix": true 74 | } 75 | } 76 | --------------------------------------------------------------------------------