Doguhan Uluca PRO
Author of the best-selling Angular for Enterprise-Ready Web Apps. Google Developers Expert in Angular. Agile, JavaScript and Cloud expert, Go player.
An out-of-the-box comparison
Doguhan Uluca
AdWords
2016Q4 Revenue - $22.4 billion
2016 - 1.86 billion users
What problems do they address?
What are the appropriate use cases?
Shadow
DOM
vs
Virtual
DOM
Lack of Native F12 Dev Tools Support
😢
😞
scoping
performance
Read MVVM vs MVC on DevPro here. (10/2013)
Just Don't Do It
✔
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { HttpModule } from '@angular/http';
import { AppComponent } from './app.component';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
FormsModule,
HttpModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
// templateUrl: './app.component.html',
// styleUrls: ['./app.component.css'],
styles: [`
.card {
height: 70px;
width: 100px;
}
`],
encapsulation: ViewEncapsulation.Native
// encapsulation: ViewEncapsulation.None
// encapsulation: ViewEncapsulation.Emulated is default
})
@View({
template: `
<h1>
{{title}}
</h1>
`,
directives: [CORE_DIRECTIVES, FORM_DIRECTIVES]
})
export class AppComponent {
title = 'app works!'
}
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import './index.css';
ReactDOM.render(
<App />,
document.getElementById('root')
)
import React, { Component } from 'react'
import logo from './logo.svg'
import './App.css'
class App extends Component {
render() {
return (
<div className="App">
<div className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<h2>Welcome to React</h2>
</div>
<p className="App-intro">
Yay, it works!.
</p>
</div>
)
}
}
import React, { Component } from 'react'
import logo from './logo.svg'
import './App.css'
class App extends Component {
render() {
return
React.createElement('App', {className: 'App'},
React.createElement('div', {className: 'App-header'},
React.createElement('img', {src: this.props.logo, className: 'App-logo', alt: 'logo'},
React.createElement('h2', {}, 'Welcome to React')
)
),
React.createElement('p', {className: 'App-intro'}, 'Yay, it works!')
)
}
}
... is a predictable state container
... programming with asynchronous data streams
Event Source
Event Handler
user clicks
window.alert('Are you sure?')
onClick='confirmSave()'
updated data
fetchDetails()
updateCache()
showToastMessage()
import { connect } from 'react-redux'
import { toggleTodo } from '../actions'
import TodoList from '../components/TodoList'
const getVisibleTodos = (todos, filter) => {
switch (filter) {
case 'SHOW_ALL':
return todos
case 'SHOW_COMPLETED':
return todos.filter(t => t.completed)
case 'SHOW_ACTIVE':
return todos.filter(t => !t.completed)
}
}
const mapStateToProps = (state) => {
return {
todos: getVisibleTodos(state.todos, state.visibilityFilter)
}
}
const mapDispatchToProps = (dispatch) => {
return {
onTodoClick: (id) => {
dispatch(toggleTodo(id))
}
}
}
const VisibleTodoList = connect(
mapStateToProps,
mapDispatchToProps
)(TodoList)
import React, { PropTypes } from 'react'
import Todo from './Todo'
const TodoList = ({ todos, onTodoClick }) => (
<ul>
{todos.map(todo =>
<Todo
key={todo.id}
{...todo}
onClick={() => onTodoClick(todo.id)}
/>
)}
</ul>
)
TodoList.propTypes = {
todos: PropTypes.arrayOf(PropTypes.shape({
id: PropTypes.number.isRequired,
completed: PropTypes.bool.isRequired,
text: PropTypes.string.isRequired
}).isRequired).isRequired,
onTodoClick: PropTypes.func.isRequired
}
Event Source
mouse clicks
filter(x >= 2)
throttle(250ms)
map(list.length)
<li *ng-for="i in list | async">
window.alert('Are you sure?')
var button = document.querySelector('.this')
var clickStream = Rx.Observable.fromEvent(button, 'click')
var multiClickStream = clickStream
.buffer(function() { return clickStream.throttle(250) }
.map(function(list) { return list.length })
.filter(function(x) { return x >= 2 })
multiClickStream.subscribe(function (numclicks) {
window.alert('You double clicked!')
})
Presentation
API
Business
Persistence
Best Practices
IDE
Patterns
Libraries
View
View Model
Controller / Router
Services ($http, redux, logic)
Best Practices
IDE
Patterns
Libraries
app.ts
rootRouter
services
filters
directives
/a: default
/master
/detail
/b/...
/c
childRouter
/d
/e
/f
import { NgModule } from '@angular/core'
import { BrowserModule } from '@angular/platform-browser'
import { FormsModule } from '@angular/forms'
import { BrowserAnimationsModule } from '@angular/platform-browser/animations'
import { Router } from '@angular/router'
import { AppComponent } from './app.component'
import { AppRoutingModule } from './app-routing.module'
import { HeroesModule } from './heroes/heroes.module'
import { ComposeMessageComponent } from './compose-message.component'
import { LoginRoutingModule } from './login-routing.module'
import { LoginComponent } from './login.component'
import { PageNotFoundComponent } from './not-found.component'
import { DialogService } from './dialog.service'
@NgModule({
imports: [
BrowserModule,
FormsModule,
HeroesModule,
LoginRoutingModule,
AppRoutingModule,
BrowserAnimationsModule
],
declarations: [
AppComponent,
ComposeMessageComponent,
LoginComponent,
PageNotFoundComponent
],
providers: [
DialogService
],
bootstrap: [ AppComponent ]
})
export class AppModule {
// Diagnostic only: inspect router configuration
constructor(router: Router) {
console.log('Routes: ', JSON.stringify(router.config, undefined, 2))
}
}
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { ComposeMessageComponent } from './compose-message.component';
import { PageNotFoundComponent } from './not-found.component';
import { CanDeactivateGuard } from './can-deactivate-guard.service';
import { AuthGuard } from './auth-guard.service';
import { SelectivePreloadingStrategy } from './selective-preloading-strategy';
const appRoutes: Routes = [
{
path: 'compose',
component: ComposeMessageComponent,
outlet: 'popup'
},
{
path: 'admin',
loadChildren: 'app/admin/admin.module#AdminModule',
canLoad: [AuthGuard]
},
{
path: 'crisis-center',
loadChildren: 'app/crisis-center/crisis-center.module#CrisisCenterModule',
data: { preload: true }
},
{ path: '', redirectTo: '/heroes', pathMatch: 'full' },
{ path: '**', component: PageNotFoundComponent }
];
@NgModule({
imports: [
RouterModule.forRoot(
appRoutes,
{ preloadingStrategy: SelectivePreloadingStrategy }
)
],
exports: [
RouterModule
],
providers: [
CanDeactivateGuard,
SelectivePreloadingStrategy
]
})
export class AppRoutingModule { }
import { Component } from '@angular/core';
@Component({
selector: 'my-app',
template: `
<h1 class="title">Angular Router</h1>
<nav>
<a routerLink="/crisis-center" routerLinkActive="active">Crisis Center</a>
<a routerLink="/heroes" routerLinkActive="active">Heroes</a>
<a routerLink="/admin" routerLinkActive="active">Admin</a>
<a routerLink="/login" routerLinkActive="active">Login</a>
<a [routerLink]="[{ outlets: { popup: ['compose'] } }]">Contact</a>
</nav>
<router-outlet></router-outlet>
<router-outlet name="popup"></router-outlet>
`
})
export class AppComponent {
}
App.js
Presentational
Container
Provider
Router
Component Legend
react-router
react-redux
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import './index.css';
ReactDOM.render(
<App />,
document.getElementById('root')
)
import React from 'react'
import {
BrowserRouter as Router,
Route,
Link
} from 'react-router-dom'
import { Home } from '../components/home'
import { About } from '../components/about'
import { Topics } from '../components/topics'
const App = () => (
<Router>
<div>
<ul>
<li><Link to="/">Home</Link></li>
<li><Link to="/about">About</Link></li>
<li><Link to="/topics">Topics</Link></li>
</ul>
<hr/>
<Route exact path="/" component={Home}/>
<Route path="/about" component={About}/>
<Route path="/topics" component={Topics}/>
</div>
</Router>
)
export default App
npm install -g create-react-app
create-react-app my-app
cd my-app/
npm start
npm install -g @angular/cli
ng new my-dream-app
cd my-dream-app
ng serve
ng generate component my-new-component
ng g component my-new-component # using the alias
# components support relative path generation
# if in the directory src/app/feature/ and you run
ng g component new-cmp
# your component will be generated in src/app/feature/new-cmp
# but if you were to run
ng g component ../newer-cmp
# your component will be generated in src/app/newer-cmp
So, which one is better?
   Slides
  Follow
By Doguhan Uluca
What are the similarities and differences between Angular and React? Why and when should you use one versus the other?
Author of the best-selling Angular for Enterprise-Ready Web Apps. Google Developers Expert in Angular. Agile, JavaScript and Cloud expert, Go player.