Slides: bit.ly/hack-a-stack
Oh hey, these are some notes. They'll be hidden in your presentation, but you can see them if you open the speaker notes window (hit 's' on your keyboard).Install
Aysegul Yonet @AysegulYonet
Sr Software Engineer - Fullstack @A360
Teach/Speak/Code/Repeat
CTO AnnieCannons - AnnieCannons.com
Fullstack JavaScript
node -v v6.1.0 npm -v 3.8.9 nodenode starts a process.
node var a = 1; global.a 1Compare to console.
npm install -g learnyounode learnyounode
mkdir mean-app cd mean-app npm init -y
Step 0: bit.ly/mean-step-0
npm i -S expresssudo chown -R $(whoami) "$HOME/.npm" npm -g install npm Check package.json
mkdir server cd server touch server.js
var express = require('express'); var app = express();
app.get('/', function (req, res) { res.send('Hello World!'); }); app.listen(4200, function () { console.log('Example app listening on port 4200!'); });res.sendFile('index.html');
app.get('/', function (req, res) { res.sendFile('client/index.html', {'root': './'}); }); app.listen(4200, function () { console.log('Example app listening on port 4200!'); });
npm install -g expressworks $ expressworks
npm install mongodb --save
Creata a data/db folder on your root folder.
npm install mongodb --save which mongo cd /usr/local/bin/ mongodMacHD or C drive. cd into that folder and run mongod if permission issues sudo chown -R 'id -u' /data/db https://docs.c9.io/v1.0/docs/setup-a-database https://community.c9.io/t/setting-up-mongodb/1717
mkdir data echo 'mongod --bind_ip=$IP --dbpath=data --nojournal --rest "$@"' > mongod chmod a+x mongod ./mongod mongo
mongo db use myDataBase show databasesmongo to start the shell db to see our databases
db.myDataCollection.insert({b: 1}) db.myDataCollection.find() db["3test"].find() db.getCollection("3test").find()mongo to start the shell db to see our databases
db["3test"].insert({c: 3}) db["3test"].find() db.getCollection("3test").find()mongo to start the shell db to see our databases Why?
npm install mongoose --save cd server mkdir db touch db.jsmongo to start the shell db to see our databases
npm install mongoose --save cd server mkdir db touch db.js
Syntactic sugar for JavaScript prototypes introduced in ES2015.
class Car { manufacturer:string; constructor(manufacturer:string = 'BMW') { this.manufacturer = manufacturer; } drive(miles:number) {} } let bmw = new Car();
Syntactic sugar for JavaScript prototypes introduced in ES2015.
class Car { manufacturer:string; constructor(manufacturer:string = 'BMW') { this.manufacturer = manufacturer; } drive(miles:number) {} } let bmw = new Car();
class Car { ... } class Convertible extends Car { } let cabrio = new Convertible();
ES2015 brings a module system to the table that enables us to write modular code.
// Car.js export class Car { ... } export class Convertible extends Car { ... }
ES2015 brings a module system to the table that enables us to write modular code.
// App.js import {Car, Convertible} from 'Car'; let bmw = new Car(); let cabrio = new Convertible();
Type annotations provide optional static typing. Applied using : T syntax
var height:number = 6; var isDone:boolean = true; var name:string = 'a360'; var list:number[] = [1, 2, 3]; var list:Array<number> = [1, 2, 3]; function add(x: number, y: number): number { return x+y; }
class ContactsApp { }
import {Component} from 'angular2/core'; @Component({ selector: 'contacts-app', template: 'Hello World!' }) class ContactsApp { }
How to instantiate that component?
import {Component} from 'angular2/core'; @Component({ selector: 'contacts-app', template: 'Hello World!' }) class ContactsApp { }
import {Component} from 'angular2/core'; import {bootstrap} from 'angular2/platform/browser'; @Component({ selector: 'contacts-app', template: 'Hello World!' }) class ContactsApp { } bootstrap(ContactsApp);
import {Component} from 'angular2/core'; import {bootstrap} from 'angular2/platform/browser'; @Component({ selector: 'contacts-app', template: 'Hello World!' }) class ContactsApp { } bootstrap(ContactsApp);
In a real world app, we put bootstrap() in a separate file to make the root component easier to test.
<html lang="en"> <head> <meta charset="utf-8"> <title>My first Angular 2 App!</title> </head> <body> <script src="..."></script> </body> </html>
<html lang="en"> <head> <meta charset="utf-8"> <title>My first Angular 2 App!</title> </head> <body> Loading... <script src="..."></script> </body> </html>
Angular performs the following tasks to bootstrap an application (simplified):
Upgrades located DOM element into Angular component Creates injector for the application Creates (emulated) Shadow DOM on component's host element Instantiates specified component Performs change detectionDirective dependencies are defined in the directives property.
@Component({ selector: 'contacts-app', template: '' }) class ContactsApp {}
Directive dependencies are defined in the directives property.
import {ContactsHeaderComponent} from '...'; @Component({ selector: 'contacts-app', template: '', directives: [ContactsHeaderComponent] }) class ContactsApp {}
Directive dependencies are defined in the directives property.
import {ContactsHeaderComponent} from '...'; @Component({ selector: 'contacts-app', template: '<contacts-header></contacts-header>', directives: [ContactsHeaderComponent] }) class ContactsApp {}
import {Component} from 'angular2/core'; @Component({ selector: 'contacts-header', template: ` <div class="some class"> <h1>Contacts App</h1> </div> ` }) export class ContactsHeaderComponent { }
We can bind data to elements in HTML templates and Angular automatically updates the UI as data changes.
@Component({ selector: 'contacts-app', template: 'Hello {{name}}' }) class ContactsApp { name:string = 'Angular 2'; }
@Component({ selector: 'contacts-app', template: 'Hello {{name}}' }) class ContactsApp { name:string = 'Angular 2'; }
To enable type safety during compilation, we can define interfaces using the interface key word
interface Contact { id: Number; name: string; email: string; phone: string; ... }
import {Contact} from './models/contact'; @Component(...) class ContactsApp { }
import {Contact} from '...'; @Component(...) class ContactsApp { contact: Contact = { id: 1, name: 'Aysegul', email: 'chris@burdorf.io', image: 'path/to/image', ... } }
@Component({ selector: 'contacts-app', template: `
@Component({ selector: 'contacts-app', template: ` <div> <img [src]="contact.image"> <span> {{contact.name}} </span> </div> ` }) class ContactsApp { ... }
Attributes are always strings and only set up the initial value
<input value="Aysegul"> <img src="path/to/img.png">
Properties can be any value and changed imperatively at run-time
var input = document.querySelector('input'); input.value; // 'Aysegul' var img = document.querySelector('img'); img.src; // 'path/to/img.png'
Properties and attributes values aren't always reflected automatically
var input = document.querySelector('input'); input.value; // 'Aysegul' input.value = 'Pascal'; input.getAttribute('value'); // 'Aysegul'
Properties and attributes values aren't always reflected automatically
var input = document.querySelector('input'); input.value; // 'Aysegul' input.value = 'Pascal'; input.getAttribute('value'); // 'Aysegul'
Angular 2 binds to element properties instead of attributes in order to work with any element
[] - syntax is the declarative way of writing to a property
var CONTACTS: Contact[] = [ { id: 1, firstname: 'Aysegul', ...}, { id: 2, firstname: 'Pascal', ...}, { id: 3, firstname: 'Julie', ...}, { id: 4, firstname: 'Igor', ...}, ... ];
import {CONTACTS} from '...'; @Component({ selector: 'contacts-app', template: `
import {CONTACTS} from '...'; @Component({ selector: 'contacts-app', template: ` <ul> <li> <!-- each contact goes here --> </li> </ul> ` }) class ContactsApp { contacts: Contact[] = CONTACTS; }
The NgFor directive instantiates a template once per item from an iterable.
<li *ngFor="#item of items">...</li>
@Component({ selector: 'contacts-app', template: ` <ul> <li> <!-- each contact goes here --> </li> </ul> ` }) class ContactsApp { ... }
@Component({ selector: 'contacts-app', template: ` <ul> <li *ngFor="#contact of contacts"> <img [src]="contact.image"> <span> {{contact.name}} </span> </li> </ul> ` }) class ContactsApp { ... }
We can create services in Angular using ES2015 classes.
import {Injectable} from 'angular2/core'; @Injectable() export class ContactsService { private contacts: Contact[] = CONTACTS; getContacts() { return this.contacts; } }
A component's providers accepts a list of providers to configure the injector.
@Component({ selector: 'contacts-app', providers: [ContactsService] ... }) class ContactsApp {}
Dependencies can be injected using TypeScript type annotations.
@Component(...) class ContactsApp { contacts: Contact[]; constructor(contactsService: ContactsService) { this.contacts = contactsService.getContacts(); } }
Dependencies can be injected using TypeScript type annotations.
@Component(...) class ContactsApp { contacts: Contact[]; constructor(contactsService: ContactsService) { this.contacts = contactsService.getContacts(); } }
Our application consists of three components:
ROUTER_PROVIDERS configure the injector for router dependencies.
import {ROUTER_PROVIDERS} from 'angular2/router'; @Component({ ... providers: [ ContactsService, ROUTER_PROVIDERS ] }) class ContactsApp {}
ROUTER_PROVIDERS configure the injector for router dependencies.
import {ROUTER_PROVIDERS} from 'angular2/router'; @Component({ ... providers: [ ContactsService, ROUTER_PROVIDERS ] }) class ContactsApp {}
We use the @RouteConfig() decorator to configure routes for our application.
import {RouteConfig} from 'angular2/router'; @Component() @RouteConfig([ // route definition goes here ]) class ContactsApp { ... }
We use the @RouteConfig() decorator to configure routes for our application.
import {RouteConfig} from 'angular2/router'; @Component() @RouteConfig([ // route definition goes here ]) class ContactsApp { ... }
A Route Definition has a path, a component, and an optional name.
@RouteConfig([ { path: '/', component: ComponentClass, name: 'ComponentName' } ])
@Component() @RouteConfig([ ]) class ContactsApp { ... }
import {ContactListComponent} from '...'; @Component() @RouteConfig([ {path: '/', component: ContactListComponent, name: 'ContactList'} ]) class ContactsApp { ... }
<router-outlet> directive specifies a viewport where components should be loaded into.
@Component({ selector: 'contacts-app', template: '...', }) @RouteConfig() class ContactsApp {...}
<router-outlet> directive specifies a viewport where components should be loaded into.
import {ROUTER_DIRECTIVES} from 'angular2/router'; @Component({ selector: 'contacts-app', template: '', directives: [ROUTER_DIRECTIVES] }) @RouteConfig() class ContactsApp {...}
<router-outlet> directive specifies a viewport where components should be loaded into.
import {ROUTER_DIRECTIVES} from 'angular2/router'; @Component({ selector: 'contacts-app', template: '<router-outlet></router-outlet>', directives: [ROUTER_DIRECTIVES] }) @RouteConfig() class ContactsApp {...}
RouterLink lets link to a specific part of our app.
<li *ngFor="#contact of contacts"> <img src="{{contact.image}}"> <span> {{contact.firstname}} {{contact.lastname}} </span> </li>
RouterLink lets link to a specific part of our app.
<li *ngFor="#contact of contacts"> <a [routerLink]="['/ContactDetail', {id: contact.id}]"> <img src="{{contact.image}}"> <span> {{contact.firstname}} {{contact.lastname}} </span> </a> </li>
RouterLink lets link to a specific part of our app.
<li *ngFor="#contact of contacts"> <a [routerLink]="['/ContactDetail', {id: contact.id}]"> <img src="{{contact.image}}"> <span> {{contact.firstname}} {{contact.lastname}} </span> </a> </li>
<a [routerLink]="['/ContactDetail', {id: contact.id}]">
@Component() @RouteConfig([ ... { path: '/contact/:id', component: ContactDetailComponent, name: 'ContactDetail' } ]) class ContactsApp {...}
<a [routerLink]="['/ContactDetail', {id: contact.id}]">
@Component() @RouteConfig([ ... { path: '/contact/:id', component: ContactDetailComponent, name: 'ContactDetail' } ]) class ContactsApp {...}
<a [routerLink]="['/ContactDetail', {id: contact.id}]">
@Component() @RouteConfig([ ... { path: '/contact/:id', component: ContactDetailComponent, name: 'ContactDetail' } ]) class ContactsApp {...}
RouteParams is an injectable immutable map of parameters for the given route.
@Component(...) class ContactDetailComponent { constructor(routeParams: RouteParams) { // ask for routeParams.get('id') } }
RouteParams is an injectable immutable map of parameters for the given route.
@Component(...) class ContactDetailComponent { constructor(routeParams: RouteParams) { // ask for routeParams.get('id') } }
NgModel implements two-way data binding by providing and combining property and event bindings.
<input [(ngModel)]="name">
Hello, {{name}}!
Or, the canonical version:
<input bindon-ngModel="name">
Hello, {{name}}!
Two-way Binding can be implemented like this:
<input [value]="name" (input)="name=$event.target.value">
ngModel hides repetitive expressions:
<input [ngModel]="name" (ngModelChange)="name=$event">
Shorthand syntax:
<input [(ngModel)]="name">
Two-way Binding can be implemented like this:
<input [value]="name" (input)="name=$event.target.value">
ngModel hides repetitive expressions:
<input [ngModel]="name" (ngModelChange)="name=$event">
Shorthand syntax:
<input [(ngModel)]="name">
Using ngModel we can already create simple forms.
<form> <label>Firstname:</label> <input [(ngModel)]="contact.firstname"> <label>Lastname:</label> <input [(ngModel)]="contact.lastname"> ... </form>
Angular 2 introduces a completely redesigned Http layer based on Observables.
HTTP_PROVIDERS configure the injector for http dependencies
import {HTTP_PROVIDERS} from 'angular2/http'; import 'rxjs/add/operator/map'; @Component({ ... providers: [HTTP_PROVIDERS] }) export class ContactsApp {...}
HTTP_PROVIDERS configure the injector for http dependencies
import {HTTP_PROVIDERS} from 'angular2/http'; import 'rxjs/add/operator/map'; @Component({ ... providers: [HTTP_PROVIDERS] }) export class ContactsApp {...}
HTTP_PROVIDERS configure the injector for http dependencies
import {HTTP_PROVIDERS} from 'angular2/http'; import 'rxjs/add/operator/map'; @Component({ ... providers: [HTTP_PROVIDERS] }) export class ContactsApp {...}
Http is an injectable class with methods to perform http requests.
import {Http} from 'angular2/http'; import {Injectable} from 'angular/core'; @Injectable() class ContactsService { constructor(private http: Http) { } }
Http is an injectable class with methods to perform http requests.
import {Http} from 'angular2/http'; import {Injectable} from 'angular/core'; @Injectable() class ContactsService { constructor(private http: Http) { } }
Http is an injectable class with methods to perform http requests.
import {Http} from 'angular2/http'; import {Injectable} from 'angular/core'; @Injectable() class ContactsService { constructor(private http: Http) { } }
Http returns an Observable which will emit a single response it's received.
class ContactsService { ... getContacts() { return this.http.get('http://myapi.com/contacts') .map((res) => { return res.json(); }) .map((data) => { return data.items; }); } }
Http returns an Observable which will emit a single response it's received.
class ContactsService { ... getContacts() { return this.http.get('http://myapi.com/contacts') .map((res) => { return res.json(); }) .map((data) => { return data.items; }); } }
Http returns an Observable which will emit a single response it's received.
class ContactsService { ... getContacts() { return this.http.get('http://myapi.com/contacts') .map((res) => { return res.json(); }) .map((data) => { return data.items; }); } }
Http returns an Observable which will emit a single response it's received.
class ContactsService { ... getContacts() { return this.http.get('http://myapi.com/contacts') .map(res => res.json()) .map(data => data.items); } }
We can subscribe to Observables using .subscribe()
@Component(...) class ContactsListComponent { constructor(contactsService: ContactsService) { contactsService.getContacts() .subscribe(contacts => this.contacts = contacts); } }
We can subscribe to Observables using .subscribe()
@Component(...) class ContactsListComponent { constructor(contactsService: ContactsService) { contactsService.getContacts() .subscribe(contacts => this.contacts = contacts); } }
Angular throws a null reference error when null objects are accessed:
The null contact's name is {{nullContact.firstname}}
Results in:
TypeError: Cannot read property 'firstname' of null in [null]
The "safe navigation" operator (?.) is a convenient way to guard against nulls in property paths
The null contact's name is {{nullContact?.firstname}}
The "save navigation" operator (?.) is a convenient way to guard against nulls in property paths
The null contact's name is {{nullContact?.firstname}}
Http.put() performs a request with the put method
class ContactsService { ... updateContact(contact: Contact) { let url = `myapi.com/contacts/{$contact.id}`; let data = JSON.stringify(contact); let headers = new Headers({...}); return this.http.put(url, data, { headers }); } }
Http.put() performs a request with the put method
class ContactsService { ... updateContact(contact: Contact) { let url = `myapi.com/contacts/{$contact.id}`; let data = JSON.stringify(contact); let headers = new Headers({...}); return this.http.put(url, data, { headers }); } }
Http.put() performs a request with the put method
class ContactsService { ... updateContact(contact: Contact) { let url = `myapi.com/contacts/{$contact.id}`; let data = JSON.stringify(contact); let headers = new Headers({...}); return this.http.put(url, data, { headers }); } }
Http.put() performs a request with the put method
class ContactsService { ... updateContact(contact: Contact) { let url = `myapi.com/contacts/{$contact.id}`; let data = JSON.stringify(contact); let headers = new Headers({...}); return this.http.put(url, data, { headers }); } }
Http.put() performs a request with the put method
class ContactsService { ... updateContact(contact: Contact) { let url = `myapi.com/contacts/{$contact.id}`; let data = JSON.stringify(contact); let headers = new Headers({...}); return this.http.put(url, data, { headers }); } }
Http.put() performs a request with the put method
class ContactsService { ... updateContact(contact: Contact) { let url = `myapi.com/contacts/{$contact.id}`; let data = JSON.stringify(contact); let headers = new Headers({...}); return this.http.put(url, data, { headers }); } }
A pipe takes in data as input and transforms it to a desired output.
@Component({ selector: 'contact-detail', template: ` ... Birthday: {{contact?.dateOfBirth | date}} ` }) class ContactDetailComponent { }
A pipe takes in data as input and transforms it to a desired output.
@Component({ selector: 'contact-detail', template: ` ... <span>Birthday:</span> <span>{{contact?.dateOfBirth | date}}</span> ` }) class ContactDetailComponent { }
The Async pipe can receive a Promise or Observable as input and subscribe to the input automatically.
import {Observable} from 'rxjs/Observable'; @Component(...) class ContactsListComponent { contacts: Observable<Array<Contact>>; constructor(contactsService: ContactsService) { this.contacts = contactsService.getContacts(); } }
The Async pipe can receive a Promise or Observable as input and subscribe to the input automatically.
import {Observable} from 'rxjs/Observable'; @Component(...) class ContactsListComponent { contacts: Observable<Array<Contact>>; constructor(contactsService: ContactsService) { this.contacts = contactsService.getContacts(); } }
The Async pipe can receive a Promise or Observable as input and subscribe to the input automatically.
@Component({ template: ` <ul> <li *ngFor="#contact of contacts | async"> ... </li> </ul> ` }) class ContactsListComponent {...}
The Async pipe can receive a Promise or Observable as input and subscribe to the input automatically.
@Component({ template: ` <ul> <li *ngFor="#contact of contacts | async"> ... </li> </ul> ` }) class ContactsListComponent {...}
Thank you for your attention!