Table of Contents
Angular and The Secret Life(cycle) of Components
- Angular 6 Setup
Verify Node.js and Npm Install
C:\Users\Helmut\AppData\Roaming\npm\node_modules> node -v v8.11.3 C:\Users\Helmut\AppData\Roaming\npm\node_modules> npm -v 5.6.0
Install Angular Cli
C:\Users\Helmut\AppData\Roaming\npm\node_modules> npm install -g @angular/cli C:\Users\Helmut\AppData\Roaming\npm\ng -> C:\Users\Helmut\AppData\Roaming\npm\node_modules\@angular\cli\bin\ng > @angular/cli@6.0.8 postinstall C:\Users\Helmut\AppData\Roaming\npm\node_modules\@angular\cli > node ./bin/ng-update-message.js npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@1.2.4 (node_modules\@angular\cli\node_modules\fsevents): npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@1.2.4: wanted {"os":"darwin","arch":"any"} (current: {"os":"win32","arch":"x64"}) + @angular/cli@6.0.8 added 246 packages in 13.439s Ingore above warning: See https://github.com/npm/npm/issues/20639
Verify Angular CLI
C:\Users\Helmut\AppData\Roaming\npm\node_modules> ng -v _ ____ _ ___ / \ _ __ __ _ _ _| | __ _ _ __ / ___| | |_ _| / △ \ | '_ \ / _` | | | | |/ _` | '__| | | | | | | / ___ \| | | | (_| | |_| | | (_| | | | |___| |___ | | /_/ \_\_| |_|\__, |\__,_|_|\__,_|_| \____|_____|___| |___/ Angular CLI: 6.0.8 Node: 8.11.3 OS: win32 x64 Angular: ... Package Version ------------------------------------------------------ @angular-devkit/architect 0.6.8 @angular-devkit/core 0.6.8 @angular-devkit/schematics 0.6.8 @schematics/angular 0.6.8 @schematics/update 0.6.8 rxjs 6.2.1 typescript 2.7.2
Reference
Create First Angular Project
Prepare Project Structure
D:\angular> ng new FirstAngularPoject CREATE FirstAngularPoject/angular.json (3656 bytes) CREATE FirstAngularPoject/package.json (1324 bytes) CREATE FirstAngularPoject/README.md (1035 bytes) CREATE FirstAngularPoject/tsconfig.json (384 bytes) CREATE FirstAngularPoject/tslint.json (2805 bytes) CREATE FirstAngularPoject/.editorconfig (245 bytes) CREATE FirstAngularPoject/.gitignore (503 bytes) CREATE FirstAngularPoject/src/environments/environment.prod.ts (51 bytes) CREATE FirstAngularPoject/src/environments/environment.ts (631 bytes) CREATE FirstAngularPoject/src/favicon.ico (5430 bytes) CREATE FirstAngularPoject/src/index.html (305 bytes) CREATE FirstAngularPoject/src/main.ts (370 bytes) CREATE FirstAngularPoject/src/polyfills.ts (3194 bytes) CREATE FirstAngularPoject/src/test.ts (642 bytes) CREATE FirstAngularPoject/src/assets/.gitkeep (0 bytes) CREATE FirstAngularPoject/src/styles.css (80 bytes) CREATE FirstAngularPoject/src/browserslist (375 bytes) CREATE FirstAngularPoject/src/karma.conf.js (964 bytes) CREATE FirstAngularPoject/src/tsconfig.app.json (194 bytes) CREATE FirstAngularPoject/src/tsconfig.spec.json (282 bytes) CREATE FirstAngularPoject/src/tslint.json (314 bytes) CREATE FirstAngularPoject/src/app/app.module.ts (314 bytes) CREATE FirstAngularPoject/src/app/app.component.html (1141 bytes) CREATE FirstAngularPoject/src/app/app.component.spec.ts (1001 bytes) CREATE FirstAngularPoject/src/app/app.component.ts (207 bytes) CREATE FirstAngularPoject/src/app/app.component.css (0 bytes) CREATE FirstAngularPoject/e2e/protractor.conf.js (752 bytes) CREATE FirstAngularPoject/e2e/src/app.e2e-spec.ts (314 bytes) CREATE FirstAngularPoject/e2e/src/app.po.ts (208 bytes) CREATE FirstAngularPoject/e2e/tsconfig.e2e.json (213 bytes) npm WARN deprecated istanbul-lib-hook@1.2.1: 1.2.0 should have been a major version bump npm WARN notice [SECURITY] ws has the following vulnerability: 1 ...
Start Web Server and Run this project FirstAngularPoject
D:\angular\FirstAngularPoject> ng serve ** Angular Live Development Server is listening on localhost:4200, open your browser on http://localhost:4200/ ** Date: 2018-06-29T07:21:10.097Z Hash: e8e3505bb172de5054c9 Time: 5298ms chunk {main} main.js, main.js.map (main) 10.7 kB [initial] [rendered] chunk {polyfills} polyfills.js, polyfills.js.map (polyfills) 227 kB [initial] [rendered] chunk {runtime} runtime.js, runtime.js.map (runtime) 5.22 kB [entry] [rendered] chunk {styles} styles.js, styles.js.map (styles) 15.6 kB [initial] [rendered] chunk {vendor} vendor.js, vendor.js.map (vendor) 3.07 MB [initial] [rendered] i 「wdm」: Compiled successfully.
Access the first Angular App via Webbrowser
URL: http://localhost:4200 “;[/insert_php] https://material.angularjs.org/1.0.5/layout/container
Webstorm Angular Poductivity Hints
Create First Angular Project
There are two ways to make a service a singleton in Angular:
- Declare that the service should be provided in the application root.
- Include the service in the AppModule or in a module that is only imported by the AppModule.
- Reference Angular Singleton Services
Sample:
@Injectable({ providedIn: 'root', }) export class myServices implements OnInit { ...
Apply complex Styles by calling a Component Function
Component HTML Code:: [ngStyle]="calculateStyles(semester.semester)"
{{semester.semester}}
Sem
Typescript Code calculateStyles(semId) { const style1 = {'background': ''}; const style2 = {'background': AppComponent.mainColor }; const style3 = {'background-image': 'linear-gradient(-90deg, #0046A0, #009FD2)'}; if (semId === this.currentSemesterId) { return style2; } else { if ((semId !== null) && (this.dataService.getSemEctsById(semId)) > 0) { return style3; } else { return style1; } } }
Angular Difference between Constructor an NgOninit
Import Modules
- Import Http Module to an existing project
- File: app.module.ts
import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; import { HttpClientModule } from '@angular/common/http'; import { AppComponent } from './app.component'; @NgModule({ declarations: [ AppComponent ], imports: [ BrowserModule, HttpClientModule ], providers: [], bootstrap: [AppComponent] }) export class AppModule { }
Passing query paramters to a router link
The triggering Code
import { Component, OnInit } from '@angular/core'; import {DataService} from '../services/data-service.service'; import {Router} from '@angular/router'; ... export class StudyProgressComponent implements OnInit { constructor(private dataService: DataService, private router: Router) { } ... gotoSemester(semesterId: number) { console.log('_____ Goto Semester : ' + semesterId); this.router.navigate(['current'], { queryParams: { semester: semesterId} }); }
The receiving side waiting in an Observable
import {Component, Input, OnInit} from '@angular/core'; import {ActivatedRoute, Router} from '@angular/router'; import {Subscription} from 'rxjs'; export class SemesterHeaderComponent implements OnInit { ... constructor( private route: ActivatedRoute, private router: Router) { } ngOnInit() { this.activeRouteSubscription = this.route .queryParams .subscribe(params => { /* Defaults to 1.st Semester if no query param provided. +params converts the data from params array to number - no further type checking needed */ this.currentSemesterId = +params['semester'] || -1; if ( this.currentSemesterId === -1) { console.log('_____________________ SemesterHeaderComponent:: Warning Semester not found in Query Params ! '); this.currentSemesterId = 1; } console.log('___ Updating Headline with SemesterId: ', this.currentSemesterId); this.setHeadline( this.currentSemesterId + '.Semester'); }); }
Reference about routes
External Links
- Angular 2 Router can’t redirect externally. This doesn’t make sense, as an external resource can’t actually be a state of the app
- Angular 2 offers the client-side Component Router for single page apps. Always redirecting to a component is consistent
Reference
- How to redirect to an external URL from angular2 route without using component?
- Our solution for getting a previous route with Angular 5
Access-Control-Allow-Origin: Dealing with CORS Errors in Angular
Browser shows following Eror
Failed to load http://localhost:8080/api/auth/test: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:4200' is therefore not allowed access.
Potential Pitfalls running inta Access-Control-Allow-Origin problem
Ajax Request Sample Hitting a server from a locally-served file file:///YourApp/index.html —— http://api.awesome.com Hitting an external API http://yourapp.com —— http://api.awesome.com Hitting an internal API http://yourapp.com —— http://api.yourapp.com Hitting a different port on the same host http://localhost:3000 —— http://localhost:4000 Requesting over http from https or vice-versa https://yourapp.com —— https://yourapp.com Fix 1: Dev-Only: Disable Same Origin inside the browser
- Using Chrom 32-bit Broser on Windows 10
"C:\Program Files (x86)\Google\Chrome\Application\chrome.exe" --disable-web-security --user-data-dir="C:/ChromeDevSession"
Fix 2: Using a Proxy Server
- Using Chrom 32-bit Broser on Windows 10
Create a File proxy.json in your Project Top level Directory
D:\dev\myProjects\dobby-the-companion_TDB> type proxy.json { "/api": { "target": "http://localhost:8080", "secure": false } }
In file package.json add flag –proxy-config proxy.json to ng server
{ "name": "dobby-the-companion", "version": "0.0.0", "scripts": { "ng": "ng", "start": "ng serve --proxy-config proxy.json", "build": "ng build", "test": "ng test", "lint": "ng lint", "e2e": "ng e2e" }, ..
Change the Rest URL now pointing to the proxy Server
URL 'http://localhost:8080/api/auth/test'; --> New URL 'http://localhost:4200/api/auth/test';
Restart the Web Server with: npm start
Reference
- Web Server Configuration for Angular Development
- Access-Control-Allow-Origin: Dealing with CORS Errors in Angular
Angular Layouts
Passing Data from Component to Component
Passing Data via HTML
HTML-Code: common-header.component.html
{{headline}}
... Passing Data from Child to Parent
Parent Typescript Code receiveMessage($event) { this.message = $event; console.log('____________________________________ receiveMessage: ' + this.message); } Parent HTML Code
(); sendMessage() { this.messageEvent.emit('Hi: ' + this.selectedVal ); } Reference
Angular Forms
Snackbar Sample – Angular 6
import {MatSnackBar} from '@angular/material'; .. constructor( public snackBar: MatSnackBar) { } .. sendUrgenMessage(errorMessage) { const snackBarRef = this.snackBar.open(errorMessage, 'OK', { duration: 5000, panelClass: 'center', verticalPosition: 'top' }); // OK button pressed snackBarRef.onAction().subscribe(() => { this.logger.error('The snack-bar action was triggered!'); }); }
Angular Using Date Format
Typescript-Code
import { DatePipe } from '@angular/common'; .. getLogTimeStamp(): string { // const df1 = 'yyyy-MM-dd hh:mm:ss:SSS'; const df1 = 'hh:mm:ss:SSS'; return this.datePipe.transform(new Date(), df1); } sendInfoMessage(m: string) { this.message = this.getLogTimeStamp() + ':: ' + m; }
app.module.ts
import { DatePipe } from '@angular/common'; @NgModule({ declarations: [ ... ], .. providers: [ ... DatePipe], ..
Angular and CSS
Angular HTTP
Working wiht HTTP Headers
From Angular Tutorial Chapter: Update headers
- You can’t directly modify the existing headers within the previous options object because instances of the HttpHeaders class are immutable.
- Use the set() method instead. It returns a clone of the current instance with the new changes applied.
- Here’s how you might update the authorization header (after the old token expired) before making the next request.
The above description isn*’t clear enough whether the original HTTP Headers will get deleted or not !
Let’ investigate this
Angular Code
Adding a Header to the Http GET Request getTestPage (): Observable
{ const testUrl1 = 'http://localhost:4200/api/auth/testjwd'; const requestHeaders = new HttpHeaders().set('Content-Type', 'text/plain'); return this.http.get (testUrl1, { headers: requestHeaders }); Interceptor Code export class MyFirstInterceptor implements HttpInterceptor { intercept(req: HttpRequest , next: HttpHandler) { const startTime = Date.now(); const url = new URL(req.urlWithParams); this.logDetails('______Running HTTPRequest ' + url.pathname ); this.dumpHttpHeader(req); const jwtToken = localStorage.getItem('jwtToken'); if (jwtToken) { // At this stage we need to change HTTP headers but HTTP headers are READ ONLY. // Thats why we need to clone the HTTP request first. // According to // https://stackoverflow.com/questions/45552677/interceptor-angular-4-3-set-multiple-headers-on-the-cloned-request // The code below adds the new Authorization Http to the available Header ! const cloned = req.clone({ headers: req.headers.set('Authorization', 'Bearer ' + jwtToken) }); this.logDetails('Sending Page with Authorization Bearer Header - URL: ' + url.pathname + ' ' + jwtToken ); this.dumpHttpHeader(cloned); return next.handle(cloned); } else { /* Shiro should be configured that /api/auth/login is the only URL to be processed without JWT Token Simple shiro.ini Sample [main] jwtv = de.thnuernberg.in.stuv.phytia.server.security.JWTVerifyingFilter [urls] /api/auth/login = anon /api/auth/testjwd = jwtv */ this.logDetails('Sending Login Page without Authorization Bearer Header - URL: ' + url.pathname ); return next.handle(req); } } private dumpHttpHeader(req: HttpRequest ) { const httpHeaders: HttpHeaders = req.headers; const keys = req.headers.keys(); console.log('__________ Header Count : ' + keys.length ); keys.forEach( key => { console.log (`___ Key: ${key} + ___ Value: + ${req.headers.get(key)}` ); }); } private logDetails(msg: string) { console.log('__ HTTP Interceptor Message: ' + msg); } } Log Output
Inside testpage() http-interceptor.js:55 __ HTTP Interceptor Message: ______Running HTTPRequest /api/auth/testjwd http-interceptor.js:51 __________ Header Count : 1 http-interceptor.js:52 ___ Key: Content-Type + ___ Value: + text/plain http-interceptor.js:55 __ HTTP Interceptor Message: Sending Page with Authorization Bearer Header - URL: /api/auth/testjwd eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiI5OTkiLCJpYXQiOjE1MzMxNDA3NTAsInN1YiI6IkhlbG11dCIsImlzcyI6IlRIIE51ZXJuYmVyZyIsImV4cCI6MTUzMzE0NDM1MH0._rRPzs0kbW1tOLZvLigjfm-63uKo1Sgumny5DcrfR4w http-interceptor.js:51 __________ Header Count : 2 http-interceptor.js:52 ___ Key: Content-Type + ___ Value: + text/plain http-interceptor.js:52 ___ Key: Authorization + ___ Value: + Bearer eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiI5OTkiLCJpYXQiOjE1MzMxNDA3NTAsInN1YiI6IkhlbG11dCIsImlzcyI6IlRIIE51ZXJuYmVyZyIsImV4cCI6MTUzMzE0NDM1MH0._rRPzs0kbW1tOLZvLigjfm-63uKo1Sgumny5DcrfR4w
ANGULAR Materials: mat-button-toggle-group set default selection
- Initialize the value in ngOnInit() and add the default value to the mat-button-toggle-group HTML line
Typescript Code: ngOnInit() { this.setSelectedVal('generalPlaning'); } HTML Code
[value]="selectedVal" (change)="onValChange(group.value)" > ANGULAR Materials: using Mat-select
- Code is based on google Sample
- mat-select [(value)]=”s[0]” is the essential part of this Code Snippet
- Stackblitz Sample
HTML Code:
Select an option None Option 1 Option 2 Option 3 You selected: {{s[0]}}
Select an option None Option 1 Option 2 Option 3 You selected: {{s[1]}}
URL: http://localhost:4200 “;[/insert_php] ANGULAR Materials: Using Mat-Dialog
app.module.ts import { MatDialogModule } from '@angular/material/dialog'; ... @NgModule({ declarations: [ .. InfoDialogComponent .. imports: [ .. MatDialogModule, .. providers: [ .. InfoDialogComponent] .. Dialog code: info-dialog.components import {Component, Inject, OnInit} from '@angular/core'; import {MatDialogRef, MAT_DIALOG_DATA} from '@angular/material'; export interface DialogData { id: number; name: string; } @Component({ selector: 'app-info-dialog', templateUrl: './info-dialog.component.html', styleUrls: ['./info-dialog.component.css'] }) export class InfoDialogComponent implements OnInit { constructor( public dialogRef: MatDialogRef
, @Inject(MAT_DIALOG_DATA) public data: DialogData) {} ngOnInit() { } afterClosingInfoDialog() { // Validate Dialog Data by by using a CAST operation. const data ={id: 1, name: 'Info Dialog'}; // Return the data tp the semester-goal-setting Component this.dialogRef.close(data); } } Component Code import {MatDialog, MatDialogConfig} from '@angular/material'; .. constructor( ... public infoDialog: MatDialog) { .. openInfoDialog(): void { this.logger.warn('StudyProgressComponent:: Opening InfoDialog Dialog '); const dialogConfig = new MatDialogConfig(); // Setting disableClose to true, which means that the user will not be able to close the dialog just by clicking outside of it // In short: The user must press the Weiter Button to continue processing dialogConfig.disableClose = true; const infoDialogRef = this.infoDialog.open(InfoDialogComponent, dialogConfig ); infoDialogRef.afterClosed().subscribe(data => { if ( data ) { this.logger.warn('StudyProgressComponent:: Closing Dialog: ' + data.name + ' with Id: ' + data.id); } else { this.logger.warn('StudyProgressComponent:: Closing Dialog: No Data available '); } }); } Reference
ANGULAR Materials: mat-dialog calls falls in ngAfterViewInit Lifecycle
Quick Workarounds
- try moving the code into ngAfterViewInit lifecycle hook
- try wrapping the code inside the setTimeout [ Fixed my problem ]
Reference
ANGULAR Materials: Mat-Grid-List can’t align text left/right
CSS code to Fix the problem
CSS Code: .align_left { width: 100%; display: flex; align-items: flex-start; } HTML Code: Check for class="align_left"
class="align_left" >Lernunterstützung
- Mat-Grid-List can’t align text left/right
https://github.com/angular/material2/issues/2736
ANGULAR Materials: Using MAT-Table with Intellij Throws warning Attribute *matHeaderCellDef is not allowed here
Warning Attribute *matHeaderCellDef is not allowed here
D:\dev\myProjects\clone4\dobby-the-companion\src\app\goals\semester-goal-setting\semester-goal-setting.component.html Warning:(36, 27) Attribute *matHeaderCellDef is not allowed here Warning:(37, 45) Attribute *matCellDef is not allowed here Error:(37, 69) 'of' expected
HTML CODE
No. {{element.position}} Reference
ANGULAR Materials: mat-dialog-container creates a white box with 48×48 px with 24px padding
CSS Code -> use global styles.css
/* Fix for STUV-197 */ .no-padding-for-dialog-container .mat-dialog-container { padding: 0 !important; } Typescript Code -> Use panelClass attribute from MatDialogConfig
openWarningDialog(): void { const dialogConfig = new MatDialogConfig(); this.disableBagdeButtonFunc(); dialogConfig.disableClose = true; dialogConfig.autoFocus = false; // Fix for STUV-197 dialogConfig.panelClass = ['no-padding-for-dialog-container']; const dialogRef = this.warningDialog.open(WarningDialogComponent, dialogConfig);
Reference
ANGULAR Materials: Using Snackbarcomponent
Snackbar Sample
Install the related NPMs
D:\dev\myTestProjects\dobby-the-companion>npm install --save @angular/material @angular/cdk @angular/animations npm WARN @angular/animations@6.1.1 requires a peer of @angular/core@6.1.1 but none is installed. You must install peer dependencies yourself. npm WARN bootstrap@4.1.1 requires a peer of jquery@1.9.1 - 3 but none is installed. You must install peer dependencies yourself. npm WARN bootstrap@4.1.1 requires a peer of popper.js@^1.14.3 but none is installed. You must install peer dependencies yourself. npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@1.2.4 (node_modules\fsevents): npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@1.2.4: wanted {"os":"darwin","arch":"any"} (current: {"os":"win32","arch":"x64"}) + @angular/cdk@6.4.2 + @angular/material@6.4.2 + @angular/animations@6.1.1 updated 3 packages in 14.633s
Add a Material Theme to global CSS Files src/style.css- Dont forget THIS !
/* You can add global styles to this file, and also import other style files */ @import '~@angular/material/prebuilt-themes/deeppurple-amber.css';
Re-Install Angular
D:\dev\myTestProjects\dobby-the-companion> npm uninstall -g @angular/cli removed 244 packages in 5.817s D:\dev\myTestProjects\dobby-the-companion> npm cache clean --force npm WARN using --force I sure hope you know what you are doing. D:\dev\myTestProjects\dobby-the-companion> npm install -g @angular/cli C:\Users\Helmut\AppData\Roaming\npm\ng -> C:\Users\Helmut\AppData\Roaming\npm\node_modules\@angular\cli\bin\ng npm WARN rollback Rolling back readable-stream@2.3.6 failed (this is probably harmless): EPERM: operation not permitted, lstat 'C:\Users\Helmut\AppData\Roaming\npm\node_modules\@angular\cli\node_modules\fsevents\node_modules' npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@1.2.4 (node_modules\@angular\cli\node_modules\fsevents): npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@1.2.4: wanted {"os":"darwin","arch":"any"} (current: {"os":"win32","arch":"x64"}) + @angular/cli@6.1.5 added 244 packages in 17.228s D:\dev\myTestProjects\dobby-the-companion> ng -v Your global Angular CLI version (6.1.5) is greater than your local version (6.1.4). The local Angular CLI version is used. To disable this warning use "ng config -g cli.warnings.versionMismatch false". _ _ ____ _ ___ / \ _ __ __ _ _ _| | __ _ _ __ / ___| | |_ _| / △ \ | '_ \ / _` | | | | |/ _` | '__| | | | | | | / ___ \| | | | (_| | |_| | | (_| | | | |___| |___ | | /_/ \_\_| |_|\__, |\__,_|_|\__,_|_| \____|_____|___| |___/ Angular CLI: 6.1.4 Node: 8.11.3 OS: win32 x64 Angular: 6.1.3 ... animations, common, compiler, compiler-cli, core, forms ... http, language-service, platform-browser ... platform-browser-dynamic, router Package Version ----------------------------------------------------------- @angular-devkit/architect 0.7.4 @angular-devkit/build-angular 0.7.4 @angular-devkit/build-optimizer 0.7.4 @angular-devkit/build-webpack 0.7.4 @angular-devkit/core 0.7.4 @angular-devkit/schematics 0.7.4 @angular/cdk 6.4.5 @angular/cli 6.1.4 @angular/material 6.4.5 @ngtools/webpack 6.1.4 @schematics/angular 0.7.4 @schematics/update 0.7.4 rxjs 6.2.2 typescript 2.9.2 webpack 4.9.2
About Angular Logging
Angular and D3