SlideShare una empresa de Scribd logo
1 de 26
--How Reactive do we need to be
Javaskop 2017, Skopje
Jana Karcheska - Netcetera
2
--What is my background?
Big web applications primarily for the desktop
with frontend built in Javascript
Enthusiasm for functional programming
3
--intro
--how long will your app live?
--how big, how good, how flexible is your team?
--how complex is the business?
--who are your users?
--how bullet-proof your UI/UX needs to be?
4
--Reactive, Immutable, Stateless
What is Reactive programming …and what it is
not
5
github.com/janakkar
6
--Project kick-off
Language features--
class IngredientThreshold {
constructor(grade, threshold, upperLimit) {
this.grade = grade;
this.threshold =
threshold;
this.upperLimit = upperLimit;
}
checkThreshold(ingredient) {
return this.upperLimit ?
this.threshold >= ingredient.amount :
this.threshold < ingredient.amount;
}
}
class IngredientThreshold {
grade: number;
threshold: number;
upperLimit?: boolean = false;
constructor(grade: number, threshold: number, upperLimit: boolean) {
this.grade = grade;
this.threshold = threshold;
this.upperLimit = upperLimit;
}
checkThreshold(ingredient: Ingredient) {
return this.upperLimit ? this.threshold >= ingredient.amount :
this.threshold < ingredient.amount;
}
}
7
class IngredientThreshold {
grade: number;
threshold: number;
upperLimit?: boolean = false;
constructor(grade: number, threshold: number, upperLimit: boolean) {
this.grade = grade;
this.threshold = threshold;
this.upperLimit = upperLimit;
}
checkThreshold(ingredient: Ingredient) {
return this.upperLimit ?
this.threshold >= ingredient.amount :
this.threshold < ingredient.amount;
}
}
8
class IngredientThreshold {
constructor(grade, threshold, upperLimit) {
this.grade = grade;
this.threshold = threshold;
this.upperLimit = upperLimit;
}
checkThreshold(ingredient) {
return this.upperLimit ?
this.threshold >= ingredient.amount :
this.threshold < ingredient.amount;
}
}
9
type ThresholdDictionary = {[id: number]: IngredientThreshold[]};
const initThresholds = function(thresholdInput: number[][], ingredients: string[]): thresholdDictionary
{
const thresholds: ThresholdDictionary = {};
ingredients.forEach(o => thresholds[o] = []);
thresholdInput.forEach((v, i) => {
ingredients.forEach((type,k) => {
thresholds[type].push(new IngredientThreshold(i, v[k], i === 0));
});
});
return thresholds;
};
const badThresholds: ThresholdDictionary = initThresholds(bad, Object.keys(BadIngredient));
const goodThresholds: ThresholdDictionary = initThresholds(good, Object.keys(GoodIngredient));
const initThresholds = (ingredients, thresholds) => {
return Object.keys(ingredients).map(type => ingredients[type])
.map(i => thresholds.map((row, j) => new IngredientThreshold(j, row[i], j === 0)));
};
const badIngredients = initThresholds(BAD, bad);
const goodIngredients = initThresholds(GOOD, good);
10
What does it return?
11
--Templating
export default class AmountInput extends Component {
render() {
return (
<div className="row ">
<label htmlFor={createId(this.props.controlName)}>
{this.props.controlLabel}
</label>
<div className="amount">
<input type="number" step="0.1"
id={createId(this.props.controlName)}
value={this.props.amount}
onChange={this.props.onAmountChange}/>
<span className="measure">{this.props.measure}</span>
</div>
</div>
);
}
}
AmountInput.propTypes = {
controlLabel: React.PropTypes.string,
controlName: React.PropTypes.string,
measure: React.PropTypes.string,
amount: React.PropTypes.number
};
<label for="field{{controlName}}">{{controlLabel}}</label>
<div class="amount">
<input type="number" step="0.1"
id="field{{controlName}}" [ngModel]="amount"
(ngModelChange)="onChange($event)">
<span class="measure">{{measure}}</span>
</div>
12
@Component({
selector: '[kyf-input]',
templateUrl: './input.component.html',
styleUrls: ['./input.component.css'],
providers: [{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => InputComponent),
multi: true}]
})
export class InputComponent implements OnInit, ControlValueAccessor {
private amount: number;
@Input()
private controlLabel: string;
@Input()
private measure: string;
@Input()
private controlName: string;
private propagateChange = (_: any) => {};
ngOnInit() {}
onChange(newValue) {
this.amount = newValue;
this.propagateChange(this.amount);
}
writeValue(obj: any): void {
this.amount = obj;
}
registerOnChange(fn: any): void {
this.propagateChange = fn;
}
registerOnTouched(fn: any): void {}
setDisabledState(isDisabled: boolean): void {}
}
13
<div class="container">
<template ngFor let-grade [ngForOf]="grades"
let-i="index">
<div kyf-badge [label]="grade"
[isActive]="isActive(grade)"></div>
<div *ngIf="i < grades.length - 1"
class="line"></div>
</template>
</div>
renderGradePanel() {
return this.grades.map((grade, i) =>
<GradeBadge key={grade} label={grade}
showLine={i < this.grades.length - 1}/>
);
}
<div className="h-container">
{this.renderGradePanel()}
</div>
14
--Forms
<form class="container" [formGroup]="productForm"
(ngSubmit)="onSave()">
<div class="row">
<label for="productName">Product</label>
<div class="h-container">
<input type="text" id="productName" formControlName="name">
</div>
</div>
<div class="row">
<label for="productType">Product type</label>
<kyf-dropdown id="productType"
[listItems]="productTypes"
formControlName="productType"></kyf-dropdown>
</div>
...
<div class="row">
<input type="submit" value="{{actionName}}"
[disabled]="!productForm.valid">
</div>
</form>
<form className="container">
<div className="row">
<label htmlFor="productName">Product</label>
<div className="h-container">
<input type="text" id="productName"/>
</div>
</div>
<div className="row">
<label htmlFor="productType">Product type</label>
<Dropdown listItems={this.productTypes}
onSelectItem={this.onProductTypeSelected}
selectedItem={this.state
.product.get('productType')}/>
</div>
...
<div className="row">
<input type="submit" value="Add" onClick={this.onSave}/>
</div>
</form>
15
this.productForm = this.formBuilder.group({
name: ['', Validators.required],
productType: 'Dairy',
badIngredients: this.formBuilder.array(this.getBadIngredients().map(createIngredientFormControl)),
goodIngredients:this.formBuilder.array(this.getGoodIngredients().map(createIngredientFormControl))
});
this.productForm.valueChanges.subscribe((value) => {
if (value && value.goodIngredients && value.badIngredients) {
this.calculate({value, valid: true});
}
});
16
const createIngredientFormControl = (key) => {
return formBuilder.group({
type: key,
amount: [0, createAmountValidator(key)]
}
);
};
17
const createAmountValidator = (key) => {
const maxValue = this.ingredientService.getIngredientMaxValue(key);
return (c: FormControl) => {
return c.value <= maxValue ? null : {
validateAmount: {
valid: false
}
};
};
};
18
constructor(props) {
super(props);
this.onAmountChange = this.onAmountChange.bind(this);
this.onProductTypeSelected = this.onProductTypeSelected.bind(this);
this.onSave = this.onSave.bind(this);
const product = ProductService.createProductTemplate();
const badIngredients = {};
product.get('badIngredients')
.forEach(ingredient => {badIngredients[ingredient.get('type')] = false});
const goodIngredients = {};
product.get('goodIngredients')
.forEach(ingredient => {goodIngredients[ingredient.get('type')] = false});
this.state = {
product: product,
calculation: undefined,
errors: {
badIngredients: badIngredients,
goodIngredients: goodIngredients
}
};
}
19
validate(value) {
return value < 0 || value > 1000;
}
onAmountChange(type, subtype, index, value) {
if (value) {
const changedProduct = this.state.product.setIn([type, index, 'amount'], value);
let currentErrors = this.state.errors;
currentErrors[type][subtype] = this.validate(value);
this.setState({
product: changedProduct,
errors: currentErrors
});
}
}
createOnAmountChangeHandler(type, subtype, index) {
return (event) => {
this.onAmountChange(type, subtype, index, event.target.valueAsNumber);
}
}
onProductTypeSelected(item) {
let changedProduct = this.state.product.set('productType', item);
this.setState({product: changedProduct});
}
20
--Routing /home /items /item/1/edit
21
const APP_ROUTES: Routes = [
{path: 'login', component: LoginComponent},
{path: 'home', component: CalculatorComponent,
canActivate: [CanActivateIfAuthenticatedGuard]},
{
path: 'products', component: ProductsComponent,
canActivate: [CanActivateIfAuthenticatedGuard],
children: [{path: ':id', component: ProductDetailComponent}]
},
{path: '', redirectTo: '/login', pathMatch: 'full'},
{path: '**', redirectTo: '/home', pathMatch: 'full'}
];
22
<div class="kyf-root">
<h1 class="kyf-header">
Know-Your-Food
</h1>
<kyf-navigation></kyf-navigation>
<div class="kyf-content">
<router-outlet></router-outlet>
</div>
</div>
23
<Router history={hashHistory}>
<Route path='/' component={App}>
<IndexRedirect to="/home"/>
<Route path='login' component={Login}/>
<Route path='home' component={CalculatorComponent}
onEnter={this.requireAuth}/>
<Route path='products' component={ProductList}
onEnter={this.requireAuth}>
<Route path='product/:id' component={ProductDetail} />
</Route>
<Route path='*' onEnter={this.avoidPageNotFound} />
</Route>
</Router>
24
class App extends Component {
render() {
return (
<div className="container">
<div className="kyf-root">
<h1 className="kyf-header">
Know-Your-Food
</h1>
{this.props.children}
</div>
</div>
);
}
}
25
--React vs. Angular2 ?!
React method count ~tens
Angular method count ~hundreds
React dependencies: ~750
Angular 2 with CLI: ~780
React fails on routing and forms, wins on code style
Angular fails on boilerplate, wins on completeness
Neither has anything to do with Reactive programming :)
26
--Big Thanks to my contributors!
Hristijan Emilija Valerija

Más contenido relacionado

La actualidad más candente

Modern JavaScript Engine Performance
Modern JavaScript Engine PerformanceModern JavaScript Engine Performance
Modern JavaScript Engine Performance
Catalin Dumitru
 
Dealing with Legacy PHP Applications
Dealing with Legacy PHP ApplicationsDealing with Legacy PHP Applications
Dealing with Legacy PHP Applications
Clinton Dreisbach
 

La actualidad más candente (18)

Apex 5 plugins for everyone version 2018
Apex 5 plugins for everyone   version 2018Apex 5 plugins for everyone   version 2018
Apex 5 plugins for everyone version 2018
 
Ip project
Ip projectIp project
Ip project
 
Deep dive into Oracle ADF
Deep dive into Oracle ADFDeep dive into Oracle ADF
Deep dive into Oracle ADF
 
Clean Test
Clean TestClean Test
Clean Test
 
Modern JavaScript Engine Performance
Modern JavaScript Engine PerformanceModern JavaScript Engine Performance
Modern JavaScript Engine Performance
 
Asynchronous JS in Odoo
Asynchronous JS in OdooAsynchronous JS in Odoo
Asynchronous JS in Odoo
 
Marcus Portfolio
Marcus  PortfolioMarcus  Portfolio
Marcus Portfolio
 
Spring 2.5
Spring 2.5Spring 2.5
Spring 2.5
 
Dealing with Legacy PHP Applications
Dealing with Legacy PHP ApplicationsDealing with Legacy PHP Applications
Dealing with Legacy PHP Applications
 
The biggest lies about react hooks
The biggest lies about react hooksThe biggest lies about react hooks
The biggest lies about react hooks
 
Java Script Best Practices
Java Script Best PracticesJava Script Best Practices
Java Script Best Practices
 
Ip project visual mobile
Ip project visual mobileIp project visual mobile
Ip project visual mobile
 
Higher-Order Components — Ilya Gelman
Higher-Order Components — Ilya GelmanHigher-Order Components — Ilya Gelman
Higher-Order Components — Ilya Gelman
 
10 tips for a reusable architecture
10 tips for a reusable architecture10 tips for a reusable architecture
10 tips for a reusable architecture
 
React lecture
React lectureReact lecture
React lecture
 
PHPUnit Episode iv.iii: Return of the tests
PHPUnit Episode iv.iii: Return of the testsPHPUnit Episode iv.iii: Return of the tests
PHPUnit Episode iv.iii: Return of the tests
 
What's up with Prototype and script.aculo.us?
What's up with Prototype and script.aculo.us?What's up with Prototype and script.aculo.us?
What's up with Prototype and script.aculo.us?
 
Window object methods (timer related)
Window object methods (timer related)Window object methods (timer related)
Window object methods (timer related)
 

Similar a How Reactive do we need to be

Similar a How Reactive do we need to be (20)

Recompacting your react application
Recompacting your react applicationRecompacting your react application
Recompacting your react application
 
Integrating React.js with PHP projects
Integrating React.js with PHP projectsIntegrating React.js with PHP projects
Integrating React.js with PHP projects
 
React + Redux. Best practices
React + Redux.  Best practicesReact + Redux.  Best practices
React + Redux. Best practices
 
React hooks
React hooksReact hooks
React hooks
 
Road to react hooks
Road to react hooksRoad to react hooks
Road to react hooks
 
N Things You Don't Want to Repeat in React Native
N Things You Don't Want to Repeat in React NativeN Things You Don't Want to Repeat in React Native
N Things You Don't Want to Repeat in React Native
 
React new features and intro to Hooks
React new features and intro to HooksReact new features and intro to Hooks
React new features and intro to Hooks
 
[FEConf Korea 2017]Angular 컴포넌트 대화법
[FEConf Korea 2017]Angular 컴포넌트 대화법[FEConf Korea 2017]Angular 컴포넌트 대화법
[FEConf Korea 2017]Angular 컴포넌트 대화법
 
React 101
React 101React 101
React 101
 
Day 5
Day 5Day 5
Day 5
 
Introduction to Redux
Introduction to ReduxIntroduction to Redux
Introduction to Redux
 
UI 모듈화로 워라밸 지키기
UI 모듈화로 워라밸 지키기UI 모듈화로 워라밸 지키기
UI 모듈화로 워라밸 지키기
 
Battle of React State Managers in frontend applications
Battle of React State Managers in frontend applicationsBattle of React State Managers in frontend applications
Battle of React State Managers in frontend applications
 
Building complex User Interfaces with Sitecore and React
Building complex User Interfaces with Sitecore and ReactBuilding complex User Interfaces with Sitecore and React
Building complex User Interfaces with Sitecore and React
 
Crossing platforms with JavaScript & React
Crossing platforms with JavaScript & React Crossing platforms with JavaScript & React
Crossing platforms with JavaScript & React
 
React redux
React reduxReact redux
React redux
 
Relay Modern: architecture and workflow
Relay Modern: architecture and workflowRelay Modern: architecture and workflow
Relay Modern: architecture and workflow
 
JS Fest 2019. Glenn Reyes. With great power comes great React hooks!
JS Fest 2019. Glenn Reyes. With great power comes great React hooks!JS Fest 2019. Glenn Reyes. With great power comes great React hooks!
JS Fest 2019. Glenn Reyes. With great power comes great React hooks!
 
Clean Javascript
Clean JavascriptClean Javascript
Clean Javascript
 
Stay with React.js in 2020
Stay with React.js in 2020Stay with React.js in 2020
Stay with React.js in 2020
 

Último

+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
Health
 
%+27788225528 love spells in Knoxville Psychic Readings, Attraction spells,Br...
%+27788225528 love spells in Knoxville Psychic Readings, Attraction spells,Br...%+27788225528 love spells in Knoxville Psychic Readings, Attraction spells,Br...
%+27788225528 love spells in Knoxville Psychic Readings, Attraction spells,Br...
masabamasaba
 

Último (20)

AI & Machine Learning Presentation Template
AI & Machine Learning Presentation TemplateAI & Machine Learning Presentation Template
AI & Machine Learning Presentation Template
 
Right Money Management App For Your Financial Goals
Right Money Management App For Your Financial GoalsRight Money Management App For Your Financial Goals
Right Money Management App For Your Financial Goals
 
Announcing Codolex 2.0 from GDK Software
Announcing Codolex 2.0 from GDK SoftwareAnnouncing Codolex 2.0 from GDK Software
Announcing Codolex 2.0 from GDK Software
 
Harnessing ChatGPT - Elevating Productivity in Today's Agile Environment
Harnessing ChatGPT  - Elevating Productivity in Today's Agile EnvironmentHarnessing ChatGPT  - Elevating Productivity in Today's Agile Environment
Harnessing ChatGPT - Elevating Productivity in Today's Agile Environment
 
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
 
WSO2Con2024 - From Code To Cloud: Fast Track Your Cloud Native Journey with C...
WSO2Con2024 - From Code To Cloud: Fast Track Your Cloud Native Journey with C...WSO2Con2024 - From Code To Cloud: Fast Track Your Cloud Native Journey with C...
WSO2Con2024 - From Code To Cloud: Fast Track Your Cloud Native Journey with C...
 
tonesoftg
tonesoftgtonesoftg
tonesoftg
 
%in Harare+277-882-255-28 abortion pills for sale in Harare
%in Harare+277-882-255-28 abortion pills for sale in Harare%in Harare+277-882-255-28 abortion pills for sale in Harare
%in Harare+277-882-255-28 abortion pills for sale in Harare
 
Direct Style Effect Systems - The Print[A] Example - A Comprehension Aid
Direct Style Effect Systems -The Print[A] Example- A Comprehension AidDirect Style Effect Systems -The Print[A] Example- A Comprehension Aid
Direct Style Effect Systems - The Print[A] Example - A Comprehension Aid
 
VTU technical seminar 8Th Sem on Scikit-learn
VTU technical seminar 8Th Sem on Scikit-learnVTU technical seminar 8Th Sem on Scikit-learn
VTU technical seminar 8Th Sem on Scikit-learn
 
%in Hazyview+277-882-255-28 abortion pills for sale in Hazyview
%in Hazyview+277-882-255-28 abortion pills for sale in Hazyview%in Hazyview+277-882-255-28 abortion pills for sale in Hazyview
%in Hazyview+277-882-255-28 abortion pills for sale in Hazyview
 
%in Midrand+277-882-255-28 abortion pills for sale in midrand
%in Midrand+277-882-255-28 abortion pills for sale in midrand%in Midrand+277-882-255-28 abortion pills for sale in midrand
%in Midrand+277-882-255-28 abortion pills for sale in midrand
 
MarTech Trend 2024 Book : Marketing Technology Trends (2024 Edition) How Data...
MarTech Trend 2024 Book : Marketing Technology Trends (2024 Edition) How Data...MarTech Trend 2024 Book : Marketing Technology Trends (2024 Edition) How Data...
MarTech Trend 2024 Book : Marketing Technology Trends (2024 Edition) How Data...
 
Architecture decision records - How not to get lost in the past
Architecture decision records - How not to get lost in the pastArchitecture decision records - How not to get lost in the past
Architecture decision records - How not to get lost in the past
 
%in kempton park+277-882-255-28 abortion pills for sale in kempton park
%in kempton park+277-882-255-28 abortion pills for sale in kempton park %in kempton park+277-882-255-28 abortion pills for sale in kempton park
%in kempton park+277-882-255-28 abortion pills for sale in kempton park
 
%+27788225528 love spells in Knoxville Psychic Readings, Attraction spells,Br...
%+27788225528 love spells in Knoxville Psychic Readings, Attraction spells,Br...%+27788225528 love spells in Knoxville Psychic Readings, Attraction spells,Br...
%+27788225528 love spells in Knoxville Psychic Readings, Attraction spells,Br...
 
OpenChain - The Ramifications of ISO/IEC 5230 and ISO/IEC 18974 for Legal Pro...
OpenChain - The Ramifications of ISO/IEC 5230 and ISO/IEC 18974 for Legal Pro...OpenChain - The Ramifications of ISO/IEC 5230 and ISO/IEC 18974 for Legal Pro...
OpenChain - The Ramifications of ISO/IEC 5230 and ISO/IEC 18974 for Legal Pro...
 
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
 
%in Soweto+277-882-255-28 abortion pills for sale in soweto
%in Soweto+277-882-255-28 abortion pills for sale in soweto%in Soweto+277-882-255-28 abortion pills for sale in soweto
%in Soweto+277-882-255-28 abortion pills for sale in soweto
 
WSO2Con2024 - WSO2's IAM Vision: Identity-Led Digital Transformation
WSO2Con2024 - WSO2's IAM Vision: Identity-Led Digital TransformationWSO2Con2024 - WSO2's IAM Vision: Identity-Led Digital Transformation
WSO2Con2024 - WSO2's IAM Vision: Identity-Led Digital Transformation
 

How Reactive do we need to be

  • 1. --How Reactive do we need to be Javaskop 2017, Skopje Jana Karcheska - Netcetera
  • 2. 2 --What is my background? Big web applications primarily for the desktop with frontend built in Javascript Enthusiasm for functional programming
  • 3. 3 --intro --how long will your app live? --how big, how good, how flexible is your team? --how complex is the business? --who are your users? --how bullet-proof your UI/UX needs to be?
  • 4. 4 --Reactive, Immutable, Stateless What is Reactive programming …and what it is not
  • 6. 6 --Project kick-off Language features-- class IngredientThreshold { constructor(grade, threshold, upperLimit) { this.grade = grade; this.threshold = threshold; this.upperLimit = upperLimit; } checkThreshold(ingredient) { return this.upperLimit ? this.threshold >= ingredient.amount : this.threshold < ingredient.amount; } } class IngredientThreshold { grade: number; threshold: number; upperLimit?: boolean = false; constructor(grade: number, threshold: number, upperLimit: boolean) { this.grade = grade; this.threshold = threshold; this.upperLimit = upperLimit; } checkThreshold(ingredient: Ingredient) { return this.upperLimit ? this.threshold >= ingredient.amount : this.threshold < ingredient.amount; } }
  • 7. 7 class IngredientThreshold { grade: number; threshold: number; upperLimit?: boolean = false; constructor(grade: number, threshold: number, upperLimit: boolean) { this.grade = grade; this.threshold = threshold; this.upperLimit = upperLimit; } checkThreshold(ingredient: Ingredient) { return this.upperLimit ? this.threshold >= ingredient.amount : this.threshold < ingredient.amount; } }
  • 8. 8 class IngredientThreshold { constructor(grade, threshold, upperLimit) { this.grade = grade; this.threshold = threshold; this.upperLimit = upperLimit; } checkThreshold(ingredient) { return this.upperLimit ? this.threshold >= ingredient.amount : this.threshold < ingredient.amount; } }
  • 9. 9 type ThresholdDictionary = {[id: number]: IngredientThreshold[]}; const initThresholds = function(thresholdInput: number[][], ingredients: string[]): thresholdDictionary { const thresholds: ThresholdDictionary = {}; ingredients.forEach(o => thresholds[o] = []); thresholdInput.forEach((v, i) => { ingredients.forEach((type,k) => { thresholds[type].push(new IngredientThreshold(i, v[k], i === 0)); }); }); return thresholds; }; const badThresholds: ThresholdDictionary = initThresholds(bad, Object.keys(BadIngredient)); const goodThresholds: ThresholdDictionary = initThresholds(good, Object.keys(GoodIngredient));
  • 10. const initThresholds = (ingredients, thresholds) => { return Object.keys(ingredients).map(type => ingredients[type]) .map(i => thresholds.map((row, j) => new IngredientThreshold(j, row[i], j === 0))); }; const badIngredients = initThresholds(BAD, bad); const goodIngredients = initThresholds(GOOD, good); 10 What does it return?
  • 11. 11 --Templating export default class AmountInput extends Component { render() { return ( <div className="row "> <label htmlFor={createId(this.props.controlName)}> {this.props.controlLabel} </label> <div className="amount"> <input type="number" step="0.1" id={createId(this.props.controlName)} value={this.props.amount} onChange={this.props.onAmountChange}/> <span className="measure">{this.props.measure}</span> </div> </div> ); } } AmountInput.propTypes = { controlLabel: React.PropTypes.string, controlName: React.PropTypes.string, measure: React.PropTypes.string, amount: React.PropTypes.number }; <label for="field{{controlName}}">{{controlLabel}}</label> <div class="amount"> <input type="number" step="0.1" id="field{{controlName}}" [ngModel]="amount" (ngModelChange)="onChange($event)"> <span class="measure">{{measure}}</span> </div>
  • 12. 12 @Component({ selector: '[kyf-input]', templateUrl: './input.component.html', styleUrls: ['./input.component.css'], providers: [{ provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => InputComponent), multi: true}] }) export class InputComponent implements OnInit, ControlValueAccessor { private amount: number; @Input() private controlLabel: string; @Input() private measure: string; @Input() private controlName: string; private propagateChange = (_: any) => {}; ngOnInit() {} onChange(newValue) { this.amount = newValue; this.propagateChange(this.amount); } writeValue(obj: any): void { this.amount = obj; } registerOnChange(fn: any): void { this.propagateChange = fn; } registerOnTouched(fn: any): void {} setDisabledState(isDisabled: boolean): void {} }
  • 13. 13 <div class="container"> <template ngFor let-grade [ngForOf]="grades" let-i="index"> <div kyf-badge [label]="grade" [isActive]="isActive(grade)"></div> <div *ngIf="i < grades.length - 1" class="line"></div> </template> </div> renderGradePanel() { return this.grades.map((grade, i) => <GradeBadge key={grade} label={grade} showLine={i < this.grades.length - 1}/> ); } <div className="h-container"> {this.renderGradePanel()} </div>
  • 14. 14 --Forms <form class="container" [formGroup]="productForm" (ngSubmit)="onSave()"> <div class="row"> <label for="productName">Product</label> <div class="h-container"> <input type="text" id="productName" formControlName="name"> </div> </div> <div class="row"> <label for="productType">Product type</label> <kyf-dropdown id="productType" [listItems]="productTypes" formControlName="productType"></kyf-dropdown> </div> ... <div class="row"> <input type="submit" value="{{actionName}}" [disabled]="!productForm.valid"> </div> </form> <form className="container"> <div className="row"> <label htmlFor="productName">Product</label> <div className="h-container"> <input type="text" id="productName"/> </div> </div> <div className="row"> <label htmlFor="productType">Product type</label> <Dropdown listItems={this.productTypes} onSelectItem={this.onProductTypeSelected} selectedItem={this.state .product.get('productType')}/> </div> ... <div className="row"> <input type="submit" value="Add" onClick={this.onSave}/> </div> </form>
  • 15. 15 this.productForm = this.formBuilder.group({ name: ['', Validators.required], productType: 'Dairy', badIngredients: this.formBuilder.array(this.getBadIngredients().map(createIngredientFormControl)), goodIngredients:this.formBuilder.array(this.getGoodIngredients().map(createIngredientFormControl)) }); this.productForm.valueChanges.subscribe((value) => { if (value && value.goodIngredients && value.badIngredients) { this.calculate({value, valid: true}); } });
  • 16. 16 const createIngredientFormControl = (key) => { return formBuilder.group({ type: key, amount: [0, createAmountValidator(key)] } ); };
  • 17. 17 const createAmountValidator = (key) => { const maxValue = this.ingredientService.getIngredientMaxValue(key); return (c: FormControl) => { return c.value <= maxValue ? null : { validateAmount: { valid: false } }; }; };
  • 18. 18 constructor(props) { super(props); this.onAmountChange = this.onAmountChange.bind(this); this.onProductTypeSelected = this.onProductTypeSelected.bind(this); this.onSave = this.onSave.bind(this); const product = ProductService.createProductTemplate(); const badIngredients = {}; product.get('badIngredients') .forEach(ingredient => {badIngredients[ingredient.get('type')] = false}); const goodIngredients = {}; product.get('goodIngredients') .forEach(ingredient => {goodIngredients[ingredient.get('type')] = false}); this.state = { product: product, calculation: undefined, errors: { badIngredients: badIngredients, goodIngredients: goodIngredients } }; }
  • 19. 19 validate(value) { return value < 0 || value > 1000; } onAmountChange(type, subtype, index, value) { if (value) { const changedProduct = this.state.product.setIn([type, index, 'amount'], value); let currentErrors = this.state.errors; currentErrors[type][subtype] = this.validate(value); this.setState({ product: changedProduct, errors: currentErrors }); } } createOnAmountChangeHandler(type, subtype, index) { return (event) => { this.onAmountChange(type, subtype, index, event.target.valueAsNumber); } } onProductTypeSelected(item) { let changedProduct = this.state.product.set('productType', item); this.setState({product: changedProduct}); }
  • 21. 21 const APP_ROUTES: Routes = [ {path: 'login', component: LoginComponent}, {path: 'home', component: CalculatorComponent, canActivate: [CanActivateIfAuthenticatedGuard]}, { path: 'products', component: ProductsComponent, canActivate: [CanActivateIfAuthenticatedGuard], children: [{path: ':id', component: ProductDetailComponent}] }, {path: '', redirectTo: '/login', pathMatch: 'full'}, {path: '**', redirectTo: '/home', pathMatch: 'full'} ];
  • 23. 23 <Router history={hashHistory}> <Route path='/' component={App}> <IndexRedirect to="/home"/> <Route path='login' component={Login}/> <Route path='home' component={CalculatorComponent} onEnter={this.requireAuth}/> <Route path='products' component={ProductList} onEnter={this.requireAuth}> <Route path='product/:id' component={ProductDetail} /> </Route> <Route path='*' onEnter={this.avoidPageNotFound} /> </Route> </Router>
  • 24. 24 class App extends Component { render() { return ( <div className="container"> <div className="kyf-root"> <h1 className="kyf-header"> Know-Your-Food </h1> {this.props.children} </div> </div> ); } }
  • 25. 25 --React vs. Angular2 ?! React method count ~tens Angular method count ~hundreds React dependencies: ~750 Angular 2 with CLI: ~780 React fails on routing and forms, wins on code style Angular fails on boilerplate, wins on completeness Neither has anything to do with Reactive programming :)
  • 26. 26 --Big Thanks to my contributors! Hristijan Emilija Valerija

Notas del editor

  1. My background is in working on big enterprise apps with JavaScript frontend. On the other hand I love functional programming and JavaScript is a functional programming language. My last project was an International train scheduling tool - rich web application for desktops. Lots of complex data shown to the user at once, complicated searches and results, data change comparisons, interface that leads the user through the complicated process of creating train timetables.
  2. Next generation projects in Netcetera are built usually with Angular 2 or React. Existing big apps done in AngularJs are being migrated. My goal is to talk about the pain points when building large JavaScript apps, in the scale of hundreds of thousands of lines of code, and examine the concepts that are in play today in the most popular frameworks. There are many things to be considered when building an enterprise application. It needs to live long in terms of decades sometimes, it is big and needs to grow in a sustainable manner, and your business case is probably fairly complex. There are more and more demanding needs about user experience - people expect these apps to be as fancy as other software they use in their daily life. And we need to meet all these requirements. There was quite some friction for the enterprise to accept JavaScript as a viable technology, or to learn to tolerate it. And yes it is hard so we need good frameworks to the rescue.
  3. The question is now how well we can cope with new (or more precisely recycled) concepts like Reactive programming? Reactive programming promises to solve the interaction with the real world. It simplifies our abstraction of asynchronous user interaction which is problematic in traditional approaches. Back in the days when we were building UI’s with Java we had one glorified dispatcher thread for handling user interaction in Swing. It was a nightmare. You’ve probably heard the term “the hell of handling callback” when developing with JQuery. But do not be fooled by promises of reactive and immutable everywhere and don’t look for it where it isn’t. Map-reduce in rendering your UI has nothing to do with reactive programming, which language you use has nothing to do with it either. And moreover it is not “The solution” to everything. Yes, in the end we still need to deal with keeping state and if everything is immutable then there will be no user interaction in your app. So what do we use today to build UI’s... yes in a way I will put React and Angular code side to side, but only to examine the different (or similar) concepts they propose. Both libraries have no direct connection to reactive programming. Apps built with React or Agular could be completely statefull and most that I’ve seen are. But it is also not true that you cannot do reactive programming with these tools.  
  4. In preparation for this presentation, I found couple of enthusiasts and we recreated the same app once with Angular and once with React. We held lengthy discussions and changed our minds quite too often. The app is fairly simple but it contains key features that are important in large applications. The app gives a label on how healthy a given product is, based on couple of ingredients in it, that the user should provide. It can authenticate a user, store his products, search them, compare them, show details etc.
  5. The first question that arises when kicking off a new project probably is what language to use. What do we feel comfortable working with? Can we live without types? Should we choose plain JavaScript, ES6 or not? Should we maybe use Typescript? If I didn’t have to consider anything else but what I would really enjoy working with most, I would definitely choose ES6. But wait, what about those hundreds of thousands of lines of code we were going to write and what about those 10+ developers in the team? Well I fought with it a little but finally I think that Typescript might be worth looking into.
  6. Here is the base class that models the ingredient quality thresholds that my algorithm uses. It is immediately easy to spot the type of information I will use to create them and helps me get an idea of the structure of the data I will need. A threshold value defined as an upper or lower limit that will indicate a certain quality grade.
  7. Pretty similar structure in ES 6 - close enough so far.
  8. Now here is how a data loaded as a matrix of numeric values turns into my desired data structure. Here Typescript ugliness comes to light. I have to define this weird structure - a dictionary, because guess what in Typescript I can’t have a map (not yet at least) or an object with an arbitrary properties like the traditional JavaScript object. The code consequentially is quite unreadable too and quite bulky.
  9. ES6 code on the other hand elegantly maps my data in a single line and the code is much shorter and much prettier. Maybe it is slightly mind boggling what exactly this code returns :). It will return a “map” - an object with ingredient types as properties and as values arrays of respective ingredient thresholds. I personally love writing such code and love just working with JavaScript objects to map stuff. But the question, in the context of the problem that I proposed at the beginning of this presentation, is what is more practical in the end. Remember those 10+ developers at one point refactoring the code. Without type safety it could get messy. Additionally Typescript allows you to write Decorators (like Annotations in Java). This could be really helpful in handling a huge code base. Of course I am just scratching the surface here, there’s much more to this discussion but let’s move on to how are we going to deal with the DOM and HTML.
  10. Angular on the left, React on the right. React mixes JavaScript and HTML, though I thought I would hate it, it really gets addictive and it’s messing up your habits when searching for something in the code for a while. Mixing the code requires getting used to some other tweaks like className instead of class, htmlFor instead of for, closing slash for inputs etc. Otherwise, the concept of binding html props and events with staff in your JavaScript code for both libraries is similar. Angular works with expressions, React works with function handles. This distinction might be pointing to the biggest difference between the two. In React everything is a function and the accent is really on turning everything into a function - it has its beauty and elegance but also some drawbacks - a lot of things in this cleaner approach are left to you to take care of, like for example the fact that your function handle is not bound to anything (your component class in this case would be desirable). This means you have to take care of it each time. And wait what is that? React uses types? Well only for input props not in general but it helps a lot. On the other hand Angular does this all over the code with Typescript. Angular code looks shorter so far but we are missing the JavaScript code here.
  11. You need all this boilerplate to make you custom made small component work with a parent form which I will come back to later. Of course here we have types for the fields defined with Typescript.
  12. Simple and similar so far. Let’s see how we will render a list of stuff. Here the list of badges representing the possible grades that a product can get (A - most healthy, E - least healthy) The code on the left in Angular uses some bulky template tag to introduce for-loop to render a list of elements. React code is elegant and seductive with its JSX JavaScript dialect. You can elegantly map your list to elements. This style of writing out your app looks very enjoyable and appealing. In React every component has a render function and we always have to return a component function to compose our components (remember everything is a function). This means we can never return list of things. We will always have to wrap the elements representing the data in our list in one parent element. While in ng I can put the badge and line one under the other in the template loop, in react I have to combine them in a parent component - can’t just leave the div that draws a line hanging out there. I can tell you this is much more important than you might think because for sure it will spoil your reputation with your designers who will have to handle all those extra divs, especially ruins it for flexbox.
  13. Next step is to create a form. So far we’ve been seduced by React’s beauty and thankful for Angular thinking about our designers. Here’s how our forms will look like - pretty similar but there is some funny business going on in the react part - referencing state! But let’s see first how Angular manages our form data before we clarify React’s state. Notice first how form elements are annotated with names.
  14. Angular offers reactive forms. You annotate your fields with form names, bind the form names to form controls. Assign each form control a validator. And subscribe to the changes that you are interested in. The form object does all the work and when done gives you an object without mutating your initial data - hence no state and no state handling. It offers interfaces that allow you to hook up your custom component directly in your form.
  15. Here’s how we create a group of controls to reflect the input for the custom component for amount entry.
  16. … and define a custom validator for it which depends on some input parameters.
  17. React has no concept for handling forms, you have to deal with everything on your own. And without any help of additional libraries like redux you will soon manage state all over the place. React does two way binding yes, because you cannot handle a form without data flowing to and from the user back to your app. The tricky thing is to propagate the change backwards all the way to the parent component that in the end knows what to do with the change.
  18. So for everything you delegate for rendering to child components you will have to provide “handle change” functions that will know how to update the parent state. And at the same time deal with validating it. Forms need mutation, no hiding from it. But you can build a reactive app around them if the framework abstracts away nicely the mutability for you.
  19. Client side routing is indispensable in large frontend apps today. We do not want to go back to the server each time the users wants to navigate. Here is how we go deeper into the UI as the route gets built and more detailed and deeply nested components come to life.
  20. Angular wants you to provide a declarative configuration for your routes. And this is the correct way to do it. Resilient to change, abstracted from component functions. It is easy to define nested routes as children. And you can also modularize your routes and have them being lazy loaded all out of the box.
  21. In your component’s template you need to state where nested route content will go.
  22. In React routes are actually trying to be components. And it is here that the whole concept falls apart, you can’t simply add here other components either built in html elements or your own intermixed with the routes which means they are not in the end just components. The routing concept for React changes so often and it is so different each time but it never really succeeds to hit the target. This is a no go for large apps. It is not so straightforward to split it up in separate components. There is no one best approach on how to implement authentication guards either. Most likely Redux will come again to the rescue, but that’s one opinionated approach that is out of scope for me today.
  23. Yep, that’s the weird way to render nested route content.
  24. Both libraries have advantages and disadvantages and though it seems I’ve made no conclusions and I’m completely undecided, the point is in fact that the process of choosing what tools to use when you build your app depends exactly on what you are trying to achieve. No tool fits all purpose, you have to choose which one is better for you. But to avoid being completely vague, my conclusion is that if you go for big enterprise apps with lots of forms and your team is big and diverse in experience and competencies you should probably go for Angular. If performance on various devices is of essence, if you’re building mobile first or you have a well-adjusted very strong team than you could consider React also for bigger projects. Experts on the library also have decided after a while some of their ideas were leading to overly complex solutions with React (using mixins). Disclaimer: I am not an expert in either, but I am in ng1 and I thought that my process of scrutinizing of the intro to both might be interesting to share.