Michael K. Snead
ReactEverything:
...and pretty much everything else!
2
Aurelia
Kendo
Knockout
npm start
Bundle.js
A single entry-point (usually 'app.js') begins the tree of dependencies, which node-based webpack includes in the bundle.
Babel converts ES-next to ES-today
'import' statements become CommonJS 'require' that node.js understands
ES*
ES*
ES*
CJS
CJS
CJS
Angular2 is designed to be used in ES5, ES6, ES7, Dart or TypeScript.
//ES6 module import syntax
import {Component} from 'angular2/core';
import {bootstrap} from 'angular2/platform/browser';
@Component({ //ES7 decorator
selector: 'my-app' //matches <my-app></my-app> in HTML
})
@View({
template: `<h1>My First Angular 2 App</h1>` //ES6 multi-line string
});
class AppComponent { } //ES6 class
bootstrap(AppComponent);
There's a lot of modern JavaScript syntax here. If you know ES6/7 it should look familiar to you.
import { Component, View } from 'angular2/core';
//Either do this ... (ES6)
export class ReactComponent { }
ReactComponent.annotations = [
new Component({
selector: 'react-component'
}),
new View({
template: '<span>This will become a React Component!'
})
];
//Or this ... (ES7)
@Component({
selector: 'react-component'
})
@View({ template: '<span>This will become a React Component!' })
export class ReactComponent { }
I illustrate the differences in syntax on my blog
import { Component, View, Input } from 'angular2/core';
/* Using this element would be something like ...
template: `
<div>Hey, this is a cool app!</div>
<div><react-component component="FooBar"></react-component></div>
`
*/
//Using ES7 with class properties
@Component({ selector: 'react-component' })
@View({ template: '<span>You told me it was {{component}}</span>' })
export class ReactComponent {
@Input() component = undefined; //Must initialize to something (bug?)
}
//Using ES7 with @Component array
@Component({ selector: 'react-component', inputs: ['component'] })
@View({ template: '<span>You told me it was {{component}}</span>' })
export class ReactComponent {}
A2's class property decorators seem to have a bug where they become read-only if you don't initialize them with something in ES7 - anything works, even undefined.
import { Component, View, Input } from 'angular2/core';
import { ElementRef } from 'angular2/core';
//Using ES7 with class properties
@Component({ selector: 'react-component' })
@View({ template: '<span>You told me it was {{component}}</span>' })
export class ReactComponent {
@Input() component = undefined; //Must initialize to something (bug?)
static parameters = [ElementRef];
constructor(elementRef) {
this.element = elementRef.nativeElement; //Save a reference
}
}
//Using ES6 static getter (zero ES7)
export class ReactComponent {
static get parameters() { return [ElementRef]; }
constructor(elementRef) {
this.element = elementRef.nativeElement; //Save a reference
}
}
ReactComponent.annotations = [
new Component({ selector: 'react-component', inputs: ['component'] }),
new View({ template: '<span>You told me it was {{component}}</span>' })
];
TypeScript "sugars over" adding the parameters array and infers the type from the type annotation.
A 3rd alternative is to put 'bindings' array in @Component.
import { Component, View, Input } from 'angular2/core';
import { ElementRef } from 'angular2/core';
//This example is in ES7
@Component({ selector: 'react-component' })
@View({ template: '<span>You told me it was {{component}}</span>' })
export class ReactComponent {
@Input() component = undefined; //Must initialize to something (bug?)
static parameters = [ElementRef];
constructor(elementRef) {
}
ngOnInit() {
//Fires one time at the beginning
console.log("Hey! I'm initialized! Component is ", this.component);
}
ngOnChanges(changes) {
//Fires one time at the beginning and again when bindings change
console.log("Hey, I'm changing! Component is ", this.component);
}
ngOnDestroy() {
//Fires when A2 needs to rerender or remove element from DOM
console.log("Hey, I'm being destroyed! Ouch!");
}
}
1. Constructor, 2. ngOnChanges, 3. ngOnInit
import { Component, View, Input } from 'angular2/core';
import { ElementRef } from 'angular2/core';
@Component({ selector: 'react-component' })
@View({ template: '' })
export class ReactComponent {
@Input() component = undefined;
@Input() props = {};
@Input() children = [];
static parameters = [ElementRef];
constructor(elementRef) {
this.element = elementRef.nativeElement;
}
ngOnChanges(changes) {
ReactDOM.render(React.createElement(this.component,
this.props,
this.children),
this.element);
}
ngOnDestroy() {
ReactDOM.unmountComponentAtNode(this.element);
}
}
<react-component [props]="{
onRemovePerson: removePerson,
people: people
}" [component]="PeopleList"></react-component>
class App {
constructor() {
this.PeopleList = window.PeopleList;
this.onRemovePerson = this.onRemovePerson.bind(this);
}
onRemovePerson() {
//...
}
}
Your React components are likely to have events that your Angular2 components will want to subscribe to.
When React invokes them (ie: button click), your 'this' will be lost.
An easy way to fix this is binding your method in the constructor, like so:
class App {
constructor() {
//make sure "this" sticks with the function/method
this.onReactButtonClicked = this.onReactButtonClicked.bind(this);
}
onReactButtonClicked(event) {
//"this" refers to App!
}
}
This problem isn't limited or specific to React :(
ngReact's repo is here:
Working demo...
(Still waiting for the full release?)
<div data-bind="react: { $: PeopleList,
props: { people: people, onRemovePerson: removePerson } }"></div>
Very much inspired by...
Working demo...
(Reliable! Not flashy!)
TypeScript demo source:
(I feel so bad for you)
Like the Angular2 component, I wrote this from scratch.
<div data-role="reactcomponent"
data-bind="reactcomponent: { component: PeopleList,
props: { people: people, onRemovePerson: removePerson } }"></div>
Widget:
Demo markup:
...and also in TypeScript
(Aurelia is in beta now!)
I am not an Aurelia expert.
(If you're on Ember 2.x, good for you!)
I am also not an Ember expert.
React's isomorphic nature means you can render the initial state on the server.
This means the markup is in the correct state the moment the initial HTML hits the browser.
This is great for mobile!
(and everything else)
ECMAScript 6 and 7 (ES2015, ES2016)
What browsers support ES6 and ES7?
https://kangax.github.io/compat-table/es6/
ES6 Features
https://github.com/lukehoban/es6features
Try ES-next in your browser
https://babeljs.io/repl/
ECMAScript 6 - New Features: Overview & Comparison
http://es6-features.org/
Taming the asynchronous beast with ES7
http://pouchdb.com/2015/03/05/taming-the-async-beast-with-es7.html
JavaScript Decorators
https://github.com/wycats/javascript-decorators
ECMAScript 6 modules: the final syntax
http://www.2ality.com/2014/09/es6-modules-final.html