Angular2

01 | Template on Plunker

https://plnkr.co/edit/gQjnRg?p=info

02 | Setup a project locally using ng-cli

if you already have an older version:

npm uninstall -g angular-cli
npm cache clean
npm install -g @angular/cli@latest

03 | Configure Routes

WARNING! AppComponent Shouldn’t be part of routing. But it is set as the default route by ng-cli.

> Never Use this:

{ path: '', component: AppComponent },

> Should use something like below:

import { Routes } from '@angular/router';
import { AppComponent } from './';
import { ContactComponent } from './contact/contact.component';
import { AboutComponent } from './about/about.component';

export const routes: Routes = [
  { path: '', redirectTo: '/contact', pathMatch: 'full' },
  { path: 'contact',  component: ContactComponent },
  { path: 'about',  component: AboutComponent },
  { path: '**', redirectTo: '/contact', pathMatch: 'full' },
];
Route Redirection: 
  { path: '**', redirectTo: '/contact', pathMatch: 'full' },
Wildcard Routes: 
  { path: '**', component: PageNotFoundComponent },

04 | Routing

From Template:

<a [routerLink]="['/signup']">Click here to Signup</a>

From your .ts file (typescript):

// src/app/landing/landing.component.ts
import { Component } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { Router } from '@angular/router';

@Component({
  selector: 'app-root',
  templateUrl: './landing.component.html',
  styleUrls: ['./landing.component.css']
})
export class LandingComponent {
  title = 'landing page';
  constructor(private router: Router) {}

  launchHome(): void {
    this.router.navigate(['/home']);
  }
}

05 | Barrel Files for the rescue

One of the first things you will notice when you start working on it is, you have to write so many import statements. Gets worst, when you have to make 2 dedicated imports to get hold of 2 components sitting inside the same folder.

Well, this isn’t one of those where you blame at Angular! This is something to do with Typescript. The resolution is to use barrel files. As a practice you can start creating an index.ts file in each of your folders containing services, components or feature modules.

Assume you src folder has too many components (example: Home, About, Contact):

The barrel file:

// src/app/index.ts
export { AppComponent } from './app.component';
export { HomeComponent } from './home.component';
export { AboutComponent } from './about.component';
export { ContactComponent } from './contact.component';

Usage:

import { AppComponent, HomeComponent, AboutComponent, ContactComponent} form './';

// or even simpler
import { * } from './';

There is a neat blog on this which can give you a better understanding of this.

06 | Running the App

ng serve

and navigate your browser to http://localhost:4200

07 | Unit Test and other CI/CD Concerns

There are scripts setup on package.json for these, one can refer it. For more on configuring karma & etc read this blog. Example, Running unit tests (jasmine – BDD):

ng test

08 | Need to create your feature module?

After sometime you realize your app module has grown out of proportion. It is time to consider some logical organization of your code. First is to consider organizing them into modules. Here is a neat read

09 | Adding a Simple Form

You will notice I’ve placed a simple form in ContactComponent. At this point it’s important that you don’t forget to import FormsModule in your app.module.ts.

However, one thing you will notice there is a realtime representation of form data. It is quite useful for you to debug, it can be achieved with the following peace of markup:


    <form #form="ngForm">
      ...
    </form>

    <div>Formdata in realtime:
      <pre>{{ form.value | json }}</pre>
    </div>

10 | Lifecycle Hooks

Intention behind post was to have an Angular2 guide which would help one to start a project from scratch & take it all way to deployment. I haven’t covered much of Angular2 concepts or structs on this post for brevity & aligning with my intention to keep it super short. However, Lifecycle Hooks are a thing it can’t afford to miss. Angular2 provides 8 of them in total: ngOnInit, ngOnChanges, ngDoCheck, ngAfterContentInit, ngAfterContentChecked, ngAfterViewInit, ngAfterViewChecked, ngOnDestroy. Here is an example:

import { Component, OnInit, OnDestroy } from '@angular/core';
@Component({
  selector: 'app-root',
  templateUrl: './app.component.html'
})
export class AppComponent implements OnInit, OnDestroy {
  constructor() {}

  ngOnInit() {
    console.log('App Component Init');
  }

  ngOnDestroy() {
    console.log('App Component Destroyed');
  }
}

You can refer this blog if you want to know them in detail

11 | Before you go for Production

You are just a command away if you want to do a production build for your awesome app. Afterwards you just need to copy the generated dist folder to your live server.

ng build --aot --environment prod

Yet, there are few things one should know going to this step. When you execute above the step there are so many things happening behind the scene. Here are some, concatenation, minification, uglification & tree shaking.

Since I have already made a note on first 3 on a different blog, I would like you to come to terms with Tree Shaking.

Tree Shaking is the process of getting rid of dead code. If you’re coming from Java world or most other OOP environments, you will be familiar with a warning called ‘unused code/method’. However, if you want to go deep into it with examples & numbers here is great post from an expert. Minko Gechev is the author of the official Angular Style Guide.

If you are already an expert at Angular & came across this post whilst considering ways to improve the page load time & performance tuning for your app here is a great stack overflow thread which you shouldn’t miss!

12 | Sourcecode

Here is the github repo used for this post.

Advertisements

oauth client

A boilerplate code

<!DOCTYPE html>
<html>
<head>
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <title></title>
  <meta charset="utf-8">
  <meta name="description" content="">
  <meta name="author" content="">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <!-- <link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css"> -->
  <link rel="stylesheet" href="index.css">
  <!--[if lt IE 9]>
      <script src="//cdn.jsdelivr.net/html5shiv/3.7.2/html5shiv.min.js"></script>
      <script src="//cdnjs.cloudflare.com/ajax/libs/respond.js/1.4.2/respond.min.js"></script>
      <![endif]-->
  <link rel="shortcut icon" href="">
</head>
<body>

  <button onclick="hello('google').login()">google</button>
  <button onclick="hello('google').logout()">logout</button>
  <!-- SCRIPTS -->
  <!--
      <script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
      <script src="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/js/bootstrap.min.js"></script>
      -->
  <script src="hello.all.js"></script>
  <script>
    hello.init(
      {google: "516591458871-s6jr870dbt8ep543258oaaofsl3v189v.apps.googleusercontent.com"}
    );
  </script>

  <script>
    hello.on('auth.login', function (auth) {

      hello(auth.network).api('/me').then(function (r) {
        console.log(JSON.stringify(r));
        debugger;
        var lab = document.createElement("div");
        lab.id = "pic_and_greet";
        lab.innerHTML = '<img src="' + r.thumbnail + '" /> Hey ' + r.name + '<p>'+r.aboutMe;
        document.body.appendChild(lab);
      });
    });

    hello.on('auth.logout', function () {
      var lab = document.getElementById("pic_and_greet");
      if (lab != null) document.body.removeChild( lab );
    });

  </script>

</body>
</html>

Spin up a local server

# Simple Server (to test it locally): 
$ python -m SimpleHTTPServer 5000

# Following aren't required

# Chrome Disable Security: 
$ open -a Google\ Chrome --args --disable-web-security --user-data-dir

# install pip: 
$ sudo easy_install pip

# install virtualenv: 
$ pip install virtualenv virtualenvwrapper
Image

dynamically add/remove siblings

1. App.js

App = Ember.Application.create();

App.IndexRoute = Ember.Route.extend({
});

App.IndexController = Ember.Controller.extend({
  items: [{
    id: 1,
    name: 'Pizza'
  },
  {
    id: 2,
    name: 'Nachos'
  },{
    id: 3,
    name: 'Spaghetti'
  }],
  actions: {
    remove: function (item) {
      this.items.removeObject(item);
      for(var i = 0; i < this.items.length; i++) {
         var temp = this.items.objectAt(i);
         Ember.set(temp, "id", (i+1));
      }
    },
    add: function (index) {
      this.items.insertAt(this.items.length, {
        name: 'Hot Dogs',
        id: this.items.length+1,
      });
    },
    summary: function() {
      Ember.$('#sum').text(JSON.stringify(this.items));
    }
  }
});

App.ListContainerComponent = Ember.Component.extend({
  tagName: 'ul',
  items: [],
  actions: {
    onRemove: function (item) {
      this.sendAction('onRemove', item);
    },
    onAdd: function (item) {
      var insertAt = this.items.indexOf(item) + 1;
      this.sendAction('onAdd', insertAt);
    }
  }
});

App.ListItemComponent = Ember.Component.extend({
  tagName: 'li'
});

2. The Markup

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <link rel="stylesheet" href="http://cdnjs.cloudflare.com/ajax/libs/normalize/2.1.0/normalize.css">
    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
    <script src="//builds.emberjs.com/tags/v1.10.0/ember-template-compiler.js"></script>
    <script src="//builds.emberjs.com/tags/v1.10.0/ember.debug.js"></script>
  </head>
<body>
  <script type="text/x-handlebars" data-template-name="application">
  <h1>Ember Food</h1>
  {{outlet}}
  </script>

  <script type="text/x-handlebars" data-template-name="index">
    {{list-container items=items onRemove='remove' onAdd='add'}}
    <div id="sum" {{action 'summary'}}>Summary</div>
  </script>
  
  <script type="text/x-handlebars" data-template-name="components/list-container">
    {{#each items as |item|}}
      {{#list-item content=item label=item.id }}
        {{item.name}}
        <button {{action 'onAdd' item}}>+</button>
        <button {{action 'onRemove' item}}>-</button>
      {{/list-item}}
    {{/each}}
  </script>
  
  <script type="text/x-handlebars" data-template-name="components/list-item">
    {{label}} 
    {{yield}}
  </script>
</body>
</html>

Javascript

Lempel–Ziv–Welch (LZW) Compression in JS

// Decode LZW-encoded string
function lzwDecode(s) {
        var dict = {};
        var data = (s + "").split("");
        var currChar = data[0];
        var oldPhrase = currChar;
        var out = [currChar];
        var code = 256;
        var phrase;
        for (var i = 1; i < data.length; i++) {
            var currCode = data[i].charCodeAt(0);
            if (currCode < 256) {
                phrase = data[i];
            }
            else {
                phrase = dict[currCode] ? dict[currCode] : (oldPhrase + currChar);
            }
            out.push(phrase);
            currChar = phrase.charAt(0);
            dict = oldPhrase + currChar;
            code++;
            oldPhrase = phrase;
        }
        return out.join("");
}

// Compress a string with LZW
function lzwEncode(s) {
        var dict = {};
        var data = (s + "").split("");
        var out = [];
        var currChar;
        var phrase = data[0];
        var code = 256;
        var i, l;
        for (i = 1, l = data.length; i < l; i++) {
            currChar = data[i];
            if (dict[phrase + currChar] != null) {
                phrase += currChar;
            }
            else {
                out.push(phrase.length > 1 ? dict[phrase] : phrase.charCodeAt(0));
                dict[phrase + currChar] = code;
                code++;
                phrase = currChar;
            }
        }
        out.push(phrase.length > 1 ? dict[phrase] : phrase.charCodeAt(0));
        for (i = 0, l = out.length; i < l; i++) {
            out[i] = String.fromCharCode(out[i]);
        }
        return out.join("");
}
Image

JSON

JS Var to JSON & vice-versa

function getObj() {
  var arrA = [22,43], arrB = [];
  return {
   ar1:arrA, 
   ar2:arrB
  };
}
var arr = getObj().ar1;
console.log(arr);

var arr1 = [1,2];
var arr2 = [3,4];

var obj = {a:arr1,b:arr2};

console.log(JSON.stringify(obj)); // {"a":[1,2],"b":[3,4]}
console.log(JSON.stringify(arr2)); // [3,4]

// adding top node to existing obj
var withPref = {'pref' : obj}

console.log(JSON.stringify(withPref)); // {"pref": {"a":[1,2], "b":[3,4]} }

var arr = (obj.a).concat(obj.b);
console.log(arr); // plain js array: [1, 2, 3, 4]

var jane = {"person":[{"id":121},{"title":"Jane"}]};

All About JSON Manipulation

<!DOCTYPE html>
<html >
  <head>
    <meta charset="UTF-8">
	<style>
	html,body {
		margin: 5px 0px 0px 5px;
		padding: 10px 0px 0px 0px;
	}
	span {
		border-radius: 3px;
		background: #220;
		color: #fff;
		padding: 10px;
	}
	</style>
  </head>

  <body>
    <div class="content"></div>
    <script src='jquery3.1.1.js'></script>

	<script type="text/javascript">
	$( "div.content" ).html("<span>Hello Again! I will exapnd</span>");
	$( "div.content span" ).append("&nbsp; > ABC" );
	
	var arry1 = [];
	var temp = {
		"gpc": [
          {
            "top": "Top1",
            "gmp": [
              {"name": "1. Basic Acc Srv"},
              {"name": "jquery"},
              {"name": "json"}
            ]
          },
			    {
            "top": "Top2",
            "gmp": [
              {"name": "1. Basic Acc Srv"},
              {"name": "ember"},
              {"name": "Top1"}
            ]
          }
		]
	};

function getObjects(obj, key, val) {
    var objects = [];
    for (var i in obj) {
        if (!obj.hasOwnProperty(i)) continue;
        if (typeof obj[i] == 'object') {
            objects = objects.concat(getObjects(obj[i], key, val));    
        } else 
        //if key matches and value matches or if key matches and value is not passed (eliminating the case where key matches but passed value does not)
        if (i == key && obj[i] == val || i == key && val == '') { //
            objects.push(obj);
        } else if (obj[i] == val && key == ''){
            //only add if the object is not already in the array
            if (objects.lastIndexOf(obj) == -1){
                objects.push(obj);
            }
        }
    }
    return objects;
}

function getValues(obj, key) {
    var objects = [];
    for (var i in obj) {
        if (!obj.hasOwnProperty(i)) continue;
        if (typeof obj[i] == 'object') {
            objects = objects.concat(getValues(obj[i], key));
        } else if (i == key) {
            objects.push(obj[i]);
        }
    }
    return objects;
}

function getKeys(obj, val) {
    var objects = [];
    for (var i in obj) {
        if (!obj.hasOwnProperty(i)) continue;
        if (typeof obj[i] == 'object') {
			objects = objects.concat(getKeys(obj[i], val));
        } else if (obj[i] == val) {
			objects.push(i);
        }
    }
    return objects;
}


// then to get the JSON string
if(arry1.length === 0) {
  // console.log("empty arry");
} else {
  var strJSON = JSON.stringify({gpc: arry1});
  // console.log('New::: \n'+strJSON );
}

// console.log('Org::: \n'+JSON.stringify(temp));
var obj1 = getObjects(temp,'top','Top1');
var obj2 = getObjects(temp,'top','Top2');

var main = obj1.concat(obj2);

console.log(JSON.stringify(main));
	</script>
  </body>

</html>

adding a parent node

Stack Answer

Concat

Pen

See the Pen parse json by Hamzeen H (@hamzeen) on CodePen.

https://production-assets.codepen.io/assets/embed/ei.js

JSFiddle

http://jsfiddle.net/hamzeen/dw3q67md/

filter component

1. All urls to 1 route

Router.map(function() {
  this.route('about', {path: '/'});
  this.route('about', { path: '/*wildcard' });
});

2. Dom Manipulation

// in any controller
Ember.$('#component-x').val('My Value');

3. Adapter: rental.js

import DS from 'ember-data';

export default DS.RESTAdapter.extend({
  'namespace': 'api'
});

4. Template: index.hbs

<div class="jumbo">
  <div class="right tomster"></div>
  <h2>Welcome!</h2>
  <p>
    We hope you find exactly what you're looking for in a place to stay.
    <br>Browse our listings, or use the search box above to narrow your search.
  </p>
</div>

{{input value=filterText type='text' placeholder='Search City'}}
  {{#unless filteredResults.length}}
    <h4>no record matched your criteria</h4>
  {{/unless}}

  <ul>
    {{#each filteredResults as |item|}}
      <li>{{item.id}} {{item.attributes.city}}</li>
    {{/each}}
  </ul>

5. Route: index.js

import Ember from 'ember';

export default Ember.Route.extend({
  filterText: '',

  setupController(controller, model) {
    this._super(controller, model);
    controller.set('filterText', this.get('filterText'));
  },

  queryParams: {
    search: {
      refreshModel: true
    }
  },

  beforeModel: function(transition) {
    // console.log('before model: '+transition.queryParams.search);
    this.set('filterText', transition.queryParams.search);
  },

  model: function(params) {
    var endPoint = 'http://localhost:4200/api/deals';
    var results = [];

    endPoint = (params.search)? endPoint+'/city='+params.search : endPoint;

    $.ajax({
      url: endPoint,
      type: 'GET',
      accepts: 'application/json',
      success: function(data) {
        if(data.rentals) {
          data.rentals.forEach(function(deal) {
            results.addObject(deal);
          });
        } else if(data.data === 'no records found'){
          console.log('No results found');
        } else {
          data.data.forEach(function(deal) {
            results.addObject(deal);
          });
        }
      },
      error: function() {
          console.log('DEBUG: GET Deals Failed');
      },async:false
    });
    return results;
  }
});

6. Controller: index.js

import Ember from 'ember';

export default Ember.Controller.extend({
  queryParams: ['search'],

  onFilterTextChange: function() {
    Ember.run.debounce(this, this.applyFilter, 0);
  }.observes('filterText'),

  applyFilter: function() {
    this.set('search', this.get('filterText'));
    console.log('search set: ' + this.get('search'));
  },
  
  filteredResults: function() {
    return this.get('model');
  }.property('search'),
});

7. Mock Response(http-mock): rentals.js

/*jshint node:true*/
module.exports = function(app) {
  var express = require('express');
  var rentalsRouter = express.Router();
  var rentals = [
      {
        type: 'rentals',
        id: 1,
        attributes: {
          title: 'Grand Old Mansion',
          owner: 'Veruca Salt',
          city: 'San Francisco',
          type: 'Estate',
          bedrooms: 15,
          image: 'https://upload.wikimedia.org/wikipedia/commons/c/cb/Crane_estate_(5).jpg'
        }
      }, {
        type: 'rentals',
        id: 2,
        attributes: {
          title: 'Urban Living',
          owner: 'Mike Teavee',
          city: 'Seattle',
          type: 'Condo',
          bedrooms: 1,
          image: 'https://upload.wikimedia.org/wikipedia/commons/0/0e/Alfonso_13_Highrise_Tegucigalpa.jpg'
        }
      }, {
        type: 'rentals',
        id: 3,
        attributes: {
          title: 'Downtown Charm',
          owner: 'Violet Beauregarde',
          city: 'Portland',
          type: 'Apartment',
          bedrooms: 3,
          image: 'https://upload.wikimedia.org/wikipedia/commons/f/f7/Wheeldon_Apartment_Building_-_Portland_Oregon.jpg'
        }
      }
    ];

  rentalsRouter.get('/', function(req, res) {
    res.send({
      rentals
    });
  });


  rentalsRouter.post('/', function(req, res) {
    res.status(201).end();
  });

  /*rentalsRouter.get('/:id', function(req, res) {
    res.send({
      '':req.params
    });
  });*/

  rentalsRouter.get('/:city', function(req, res) {
    let val = req.params.city.split("=");
    let filteredRentals = rentals.filter(function(i) {
      return i.attributes.city.toLowerCase().indexOf(val[1].toLowerCase()) !== -1;
    });

    if(filteredRentals.length>=1) {
      res.send({
        'data': filteredRentals
      });
    } else {
      res.send({
        'data': 'no records found'
      });
    }

    
  });

  rentalsRouter.put('/:id', function(req, res) {
    res.send({
      'rentals': {
        id: req.params.id
      }
    });
  });

  rentalsRouter.delete('/:id', function(req, res) {
    res.status(204).end();
  });

  // The POST and PUT call will not contain a request body
  // because the body-parser is not included by default.
  // To use req.body, run:

  //    npm install --save-dev body-parser

  // After installing, you need to `use` the body-parser for
  // this mock uncommenting the following line:
  //
  //app.use('/api/rentals', require('body-parser').json());
  app.use('/api/rentals', rentalsRouter);
};

Journal – publish bower package; setup ember mock

1. Publish Bower Package

Bower was started by twitter.
https://bower.io/docs/creating-packages/

2. Simple Ember App

Super Rentals

3. Mocks with EmberJS

EmberJS comes with ExpressJS (internally, http-mock) here are steps to setup a mock. Once setup it works like a real service & it actually be used to build REST API for your backend if it is fueled with real data.

  • generate ember project with ember-cli
  • generate an express route
    ember g http-mock destinations
  • to view: http://localhost:4200/api/destinations
  • Unlike fixtures, this helps you test your Adapter as well
  • setting up a mock response
// server/mocks/destinations.js
/*jshint node:true*/
module.exports = function(app) {
  var express = require('express');
  var destinationsRouter = express.Router();

  destinationsRouter.get('/', function(req, res) {
    res.send({
      'destinations': [
		{
			id:'001',
			loc: 'london'
		}, {
			id:'002', 
			loc: 'rotterdam'
		}, {
			id:'003', 
			loc: 'amsterdam'
		}
	  ]
    });
  });

  destinationsRouter.post('/', function(req, res) {
    res.status(201).end();
  });

  destinationsRouter.get('/:id', function(req, res) {
    res.send({
      'destinations': {
        id: req.params.id
      }
    });
  });

  destinationsRouter.put('/:id', function(req, res) {
    res.send({
      'destinations': {
        id: req.params.id
      }
    });
  });

  destinationsRouter.delete('/:id', function(req, res) {
    res.status(204).end();
  });

  // The POST and PUT call will not contain a request body
  // because the body-parser is not included by default.
  // To use req.body, run:
  //    npm install --save-dev body-parser

  // After installing, you need to `use` the body-parser for
  // this mock uncommenting the following line:
  //app.use('/api/destinations', require('body-parser').json());
  app.use('/api/destinations', destinationsRouter);
};