Response Mapping

REST API
http://jsonplaceholder.typicode.com/users/

export class MyApp {
  private users = [];

  constructor(http: Http) {
    http.get('http://jsonplaceholder.typicode.com/users/')
        .flatMap((response) => response.json())
        .filter((person) => person.id > 5)
        .map((person) => "Mr. " + person.name)
        .subscribe((data) => {
          this.users.push(data);
        });
  }
}


On Console:
var users = [];
[
  {
    "id": 2,
    "name": "Ervin Howell",
    "username": "Antonette",
    "email": "Shanna@melissa.tv",
    "address": {
      "street": "Victor Plains",
      "suite": "Suite 879",
      "city": "Wisokyburgh",
      "zipcode": "90566-7771"
    },
    "phone": "010-692-6593 x09125",
    "website": "anastasia.net"
  },
  {
    "id": 3,
    "name": "Clementine Bauch",
    "username": "Samantha",
    "email": "Nathan@yesenia.net",
    "address": {
      "street": "Douglas Extension",
      "suite": "Suite 847",
      "city": "McKenziehaven",
      "zipcode": "59590-4157"
    },
    "phone": "1-463-123-4447",
    "website": "ramiro.info"
  },
  {
    "id": 5,
    "name": "Chelsey Dietrich",
    "username": "Kamren",
    "email": "Lucio_Hettinger@annie.ca",
    "address": {
      "street": "Skiles Walks",
      "suite": "Suite 351",
      "city": "Roscoeview",
      "zipcode": "33263"
    },
    "phone": "(254)954-1289",
    "website": "demarco.info"
  },
  {
    "id": 6,
    "name": "Mrs. Dennis Schulist",
    "username": "Leopoldo_Corkery",
    "address": {
      "street": "Norberto Crossing",
      "suite": "Apt. 950",
      "city": "South Christy",
      "zipcode": "23505-1337"
    },
    "phone": "1-477-935-8478 x6430",
    "website": "ola.org"
  }
].map((response) => response)
        .filter((person) => person.id > 3)
        .map((person) => { 
          return {
            name: 'Mr.' + person.name,
            company: 'laziest approach to constructing objs',
            id: person.id
          };
        })
        .map((data) => {
          users.push(data);
        });
Advertisements

Callbacks

function eat(at, callback) {
  setTimeout(function() {
    console.log('3. In 3 secs');
  }, 3000);
  console.log(`1. eat at ${at} & `);
  callback(5);
}

function sleep(at){
  setTimeout(function() {
    console.log(`2. sleep at ${at}`);
  }, 2000);
}

eat(3, sleep);

/> This does nothing

function greetWorld() {
  console.log(new Date() + ': Hello, world!');
}
var timerId = setTimeout(greetWorld, 2000);
clearTimeout(timerId);

Ex2:

function sleep(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}
function log() {
  console.log('LG');
}

async function demo() {
  setTimeout("console.log('first');", 5000);
  await sleep(5000);
  console.log('second one');log();
}
demo();

// call books api
this.bookService.getBooks()
  .subscribe(
    response => {
      this.booksLoaded = true;
      this.books = response;
    },
    err => {
      console.log(‘books failed to load: ' + JSON.stringify(err));
  });

Angular Survival Kit

https://stackoverflow.com/questions/35435042/how-can-i-define-an-array-of-objects-in-typescript

https://stackoverflow.com/questions/29382389/defining-array-with-multiple-types-in-typescript

https://basarat.gitbooks.io/typescript/docs/types/generics.html

Check if already selected item was clicked

ts:
public onUpdate(event, index) {
  const preventAction = event
    .target.classList.contains('selected');
  if(!preventAction) {
    // handle the event that got fired!
  }
}
tpl:
(click)="onUpdate($event, i)"

jQuery:
$(document).ready(function() {
    $("a").click(function(event) {
        console.log(event.target.class);
    });
});

Routing & Dynamic Form Controls setup


constructor(private router: Router) {}

this.arryUsers.forEach(user=>{
  this.userForm.addControl(user.id , control);
});

goHome() {
    this.router.navigate(['']);
}

<!-- *ngFor="let item of items; let i = index" -->

Form State

onChanges(): void {
  this.myForm.valueChanges.subscribe(val => {
    console.log(val);
  });
}

onSubmit() {
  if (this.myform.valid) {
    console.log("Form Submitted!");
  }
}
tpl:
{{myform.value | json}}

Promises Basic

// Promises Basic
var p1 = Promise.resolve("calling next");
p1.then((res)=>console.log(res));

JSON Filters

// lookup attributes for a value
vendors = [{
      Name: 'Apple',
      ID: 'APL'
    },
    {
      Name: 'Microsoft',
      ID: 'MST'
    }];

# solution 1 ES6 Arrow
vendors.filter(e => e.Name === 'Microsoft');

# solution 2 only till 1st result is found
vendors.find(item => {
    return item.Name === 'Microsoft';
});

Test DOM Elements with Jasmine

// Jasmine query selector by id
de = fixture.debugElement.query(by.css('#main-container'));

import { TestBed, async, ComponentFixture } from '@angular/core/testing';

describe('', () => {
  let fixture: ComponentFixture;
  let component: TestComponent;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      imports: [ ],
      declarations: [ TestComponent ],
      providers[  ]
    }).compileComponents().then(() => {
      fixture = TestBed.createComponent(TestComponent);
      component = fixture.componentInsance;
    });
  }));
});

it('should', async(() => {
  spyOn(component, 'onEditButtonClick');

  let button = fixture.debugElement.nativeElement.querySelector('button');
  button.click();

  fixture.whenStable().then(() => {
    expect(component.onEditButtonClick).toHaveBeenCalled();
  })
}));

OR

it('should', fakeAsync( () => {
    fixture.detectChanges();
    spyOn(componentInstance, 'method name'); //method attached to the click.
    let btn = fixture.debugElement.query(By.css('button'));
    btn.triggerEventHandler('click', null);
    tick(); // simulates the passage of time until all pending asynchronous activities finish
    fixture.detectChanges();
    expect(componentInstance.methodName).toHaveBeenCalled();
}));

Typescript Getters

these shorthands really help to get rid of long getters that you have to write in order to access a from property.

export class SignupFormComponent {
  get email() {
    return this.signupForm.get('email');
  }

  get password() {
    return this.signupForm.get('password');
  }

  get terms() {
    return this.signupForm.get('terms');
  }
}

Re-render Child Component

Sometimes updating @input doesn’t guarantee a re-render of child components. Here is an approach to resolve it.

export class ChildComponent implements OnChanges {
  @Input() data: Data;
  @ViewChild(‘childComp’) childComp: ElementRef;

  ngOnChanges(changes: SimpleChanges) {
    this.updateData();
  }
}

Proxy Conf in Angular 2+

in case, if we run mock apis on http://localhost:3000/api & want all calls to http://localhost:4200/api reach the first (mock) server.

DOC - https://github.com/angular/angular-cli/blob/master/docs/documentation/stories/proxy.md

Serve Angular on XAMPP (apache) server

change in my root directory index.html file which is:

<base href="/"> to <base href="./">

Now build the app:


/> ng build --prod
copy dist folder and paste it in my xampp htdocs folder and access the site using:

localhost:8080/dist/

Ref:

https://codecraft.tv/courses/angular/http/http-with-promises/
https://scotch.io/tutorials/how-to-deal-with-different-form-controls-in-angular-2
https://ciphertrick.com/2017/07/24/parent-child-component-communication-angular/
https://toddmotto.com/angular-dynamic-components-forms
https://scotch.io/tutorials/3-ways-to-pass-async-data-to-angular-2-child-components

View story at Medium.com
http://marclloyd.co.uk/angular2/spying-on-router-navigate-in-angular-2-unit-tests/

https://vsavkin.com/three-ways-to-test-angular-2-components-dcea8e90bd8d

HighCharts: https://medium.com/@balramchavan/using-highcharts-with-angular-5-6c6564a55cf0

Aside

Angular5 part-3

a minimalist quiz component in Angular5 in less than 100 lines of code (including its data).

/> Typescript source

import { Component } from '@angular/core';

@Component({
  selector: 'app-authors',
  templateUrl: './authors.component.html',
  styleUrls: ['./authors.component.css']
})
export class AuthorsComponent {

  // maintains the state of the quiz
  private queId: number = -1;
  private current: number = 0;

  // capture user interaction
  public user: any = {};
  private responses: any = [];

  questions: any = [{
            question: 'Which is the largest country in the world by population?',
            options: ['India', 'USA', 'China', 'Russia'],
            answer: 3
        }, {
            question: 'When did the second world war end?',
            options: ['1945', '1939', '1944', '1942'],
            answer: 1
        }, {
            question: 'Which was the first country to issue paper currency?',
            options: ['USA', 'France', 'Italy', 'China'],
            answer: 4
        }, {
            question: 'Which city hosted the 1996 Summer Olympics?',
            options: ['Atlanta', 'Sydney', 'Athens', 'Beijing'],
            answer: 1
        }, {
            question: 'Who invented telephone?',
            options: ['Albert Einstein', 'Alexander Graham Bell', 'Isaac Newton', 'Marie Curie'],
            answer: 1
        }];

  constructor() {
    this.queId = this.getRandom(0, 4);
    this.user.response = '';
    this.user.score = 0;
    this.user.responses = [];
    this.updateState();
  }

  get isQuizMode() {
      return (this.responses.length <= 4);
  }

  get score(): number {
    return this.user.score;
  }

  onNext() {
    event.preventDefault();
    if (this.current < this.responses.length) {
          this.queId = this.responses[this.current].queId;
          this.user.response = this.responses[this.current].response;
    } else {
          this.responses.push({'response': this.user.response, 'queId': this.queId});
          this.queId = this.getRandom(0, 4);
          this.user.response = '';
    }
    this.updateState();
  }

  onPrev() {
     if (this.current >= 2) {
         if (this.responses.length < this.current) {
             this.responses.push({'response': this.user.response, 'queId': this.queId});
         }
         this.queId = this.responses[this.current - 2].queId;
         this.user.response = this.responses[this.current - 2].response;
         this.current--;
     }// console.log(this.responses);
  }

  updateState (): void {
      if (!this.isQuizMode) {
          for (let i = 0; i < 5; i++) {
              let usrRes = this.responses[i].response;
              let ans = this.questions[this.responses[i].queId].answer;
              if (usrRes == ans) {
                  this.user.score++;
              }
          }
          this.user.score *= 20;
      } else {
          this.current++;
      }
  }

  getRandom(min, max): number {
      return Math.floor(Math.random() * (max - min + 1)) + min;
  }
}

/> The Markup


<!-- render questions //-->
<div *ngIf="isQuizMode; else other_content">
    <form name="frm" #f="ngForm" novalidate>

        <div style="text-align:left">
            {{current}}. {{questions[queId].question}}
            <ul>
                <li>{{questions[queId].options[0]}}</li>
                <li>{{questions[queId].options[1]}}</li>
                <li>{{questions[queId].options[2]}}</li>
                <li>{{questions[queId].options[3]}}</li>
            </ul>
            <input autofocus name="answer" [(ngModel)]="user.response" type="text">
        </div>

        <button type="submit" (click)="onNext()">Next</button>
        <button type="submit" (click)="onPrev()">Prev</button>
    </form>
</div>

<!-- show summary //-->
<ng-template #other_content>
    <h2>Results</h2>
    <h3>Your Score: {{score}}</h3>
        <p *ngFor="let resp of responses; let correct; let i = index;">
            {{i+1}}. queid: {{questions[resp.queId].question}} <br/>
            correct ans: {{questions[resp.queId].answer}}
            user sel: {{resp.response}}<br/>
        </p>
</ng-template>


<br/><br/>

breaking down, package.json

01 | Using Config within package.json

package.json
{
  "name": "npm-labs",
  "version": "1.0.0",
  "description": "",
  "main": "start.js",
  "config": {
    "env": "dev"
  },
  "scripts": {
    "show-conf": "echo env: $npm_package_config_env",
    "first-task": "npm run second-task",
    "second-task": "npm run build --env=$npm_package_config_env",
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "node start.js"
  },
  "author": "hamzeen",
  "license": "ISC"
}
start.js
console.log("name:" + process.env.npm_config_env);

usage:

$ npm run first-task

02 | Passing args to JS file

package.json
{
  "name": "npm-labs",
  "version": "1.0.0",
  "description": "",
  "main": "start.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "node start.js"
  },
  "author": "hamzeen",
  "license": "ISC"
}
start.js
console.log("name:" + process.env.npm_config_name);

usage:

$ npm run build --name=app-name

Angular5 part-2

There is only word which is synonymous with Angular, C H A N G E. Few months ago I made a post on Angular2 & now we have landed on Angular5. It is definitely challenging to keep us on par with the latest in the Angular world.

So to keep my dice rolling, I decided to make my angular posts into a series. In this we look at debugging our unit tests on a browser. In Javascript world, we all know and/or have used Jasmine for Unit Testing at some point.

By default it runs on a headless browser (usually PhantomJS) so we launch the test case/suite from terminal & just wait till test results but we never get to see anything!

But if we change this to a browser like Chrome, we could actually see how the test cases are being executed. Just like UI automation tests, fro example, this would show us text being inserted on forms, button being clicked & etc. Without no more explaination, let’s dive into how we establish this; Just 3 steps!

01 | Get the karma chrome launcher for your project

npm install karma-chrome-launcher --save-dev

02 | Modify karma.conf.json to add the following

    customLaunchers: {
      Chrome_without_security: {
        base: 'Chrome',
        flags: ['--disable-web-security']
      }
    },
    broswers [
        'phantomJS','Chrome', 'Chrome_without_security'
    ]

03 | add the following script definition to package.json

"test-watch": "node node_modules/karma/bin/karma start --browsers=Chrome,Chrome_without_security --single-run=true --auto-watch",

That is it!

Now open up terminal from your project path and hit the following and see magic happen on chrome (look for port on the output)

npm run test-watch