Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Living Immutably with Ember
Search
Charles Lowell
May 21, 2016
Programming
1
110
Living Immutably with Ember
From idea to action, using immutability in Ember applications.
Charles Lowell
May 21, 2016
Tweet
Share
More Decks by Charles Lowell
See All by Charles Lowell
State Of Enjoyment
cowboyd
1
420
Microstates
cowboyd
1
84
Typeclasses in JavaScript
cowboyd
0
240
Big Testing in React
cowboyd
0
110
Wheel of Type
cowboyd
0
78
Immutability is for UI, You and I
cowboyd
3
830
Other Decks in Programming
See All in Programming
Server Side Kotlin Meetup vol.16: 内部動作を理解して ハイパフォーマンスなサーバサイド Kotlin アプリケーションを書こう
ternbusty
1
120
Cursorハンズオン実践!
eltociear
2
890
いま中途半端なSwift 6対応をするより、Default ActorやApproachable Concurrencyを有効にしてからでいいんじゃない?
yimajo
2
400
技術的負債の正体を知って向き合う / Facing Technical Debt
irof
0
150
デミカツ切り抜きで面倒くさいことはPythonにやらせよう
aokswork3
0
220
Railsだからできる 例外業務に禍根を残さない 設定設計パターン
ei_ei_eiichi
0
440
クラシルを支える技術と組織
rakutek
0
200
私達はmodernize packageに夢を見るか feat. go/analysis, go/ast / Go Conference 2025
kaorumuta
2
520
理論と実務のギャップを超える
eycjur
0
120
Conquering Massive Traffic Spikes in Ruby Applications with Pitchfork
riseshia
0
160
複雑化したリポジトリをなんとかした話 pipenvからuvによるモノレポ構成への移行
satoshi256kbyte
1
1k
Web Components で実現する Hotwire とフロントエンドフレームワークの橋渡し / Bridging with Web Components
da1chi
3
2k
Featured
See All Featured
XXLCSS - How to scale CSS and keep your sanity
sugarenia
248
1.3M
Large-scale JavaScript Application Architecture
addyosmani
514
110k
Refactoring Trust on Your Teams (GOTO; Chicago 2020)
rmw
35
3.2k
10 Git Anti Patterns You Should be Aware of
lemiorhan
PRO
657
61k
JavaScript: Past, Present, and Future - NDC Porto 2020
reverentgeek
52
5.6k
The Pragmatic Product Professional
lauravandoore
36
6.9k
I Don’t Have Time: Getting Over the Fear to Launch Your Podcast
jcasabona
33
2.5k
Git: the NoSQL Database
bkeepers
PRO
431
66k
Raft: Consensus for Rubyists
vanstee
139
7.1k
Responsive Adventures: Dirty Tricks From The Dark Corners of Front-End
smashingmag
252
21k
Visualization
eitanlees
148
16k
Imperfection Machines: The Place of Print at Facebook
scottboms
269
13k
Transcript
None
Code
Handwaving
Codewaving
I Said • Don’t use a component’s properties in its
template • Don’t use computed properties • Don’t use Ember.Object at all
But the Devil is in the Details Code
Living Immutably @cowboyd GEMConf SF 2016
Why vs How
So What’s the Big Idea?
Content First
Content as Stream
Model Content as Stream
Independent Frames
One Experience
Going Practical Implementing a File Upload
UX the movie
UX the movie
UX the content
An Upload at Rest
Start State isStarted: false isEnded: false file: null progress: 0
UX the state { isStarted: false, isEnded: false, file: null,
progress: 0 } ??? Start
A Wild File Object Appears { isStarted: false, isEnded: false,
file: null, progress: 0 } selectFile(File) Start
New Target Acquired
Uploading State isStarted: true, isEnded: false, file: File(‘puppies.gif’), progress: 0
UX the state { isStarted: false, isEnded: false, file: null,
progress: 0 } selectFile(File) Start { isStarted: true, isEnded: false, file: File(‘puppies.gif’), progress: 0 } Uploading ???
Progress is Made isStarted: true, isEnded: false, file: File(‘puppies.gif’), progress:
0 Uploading progress(Event)
Uploading Pt 2 isStarted: true, isEnded: false, file: File(‘puppies.gif’), progress:
0.75
UX the state { isStarted: false, isEnded: false, file: null,
progress: 0 } selectFile(File) Start { isStarted: true, isEnded: false, file: File(‘puppies.gif’), progress: 0 } Uploading { isStarted: true, isEnded: false, file: File(‘puppies.gif’), progress: 0.75 } Uploading ??? progress(Event)
Final Count Down isStarted: true, isEnded: false, file: File(‘puppies.gif’), progress:
0.75 Uploading loadend()
Success isStarted: true, isEnded: true, file: File(‘puppies.gif’), progress: 1
UX the state { isStarted: false, isEnded: false, file: null,
progress: 0 } selectFile(File) Start { isStarted: true, isEnded: false, file: File(‘puppies.gif’), progress: 0 } Uploading { isStarted: true, isEnded: false, file: File(‘puppies.gif’), progress: 0.75 } Uploading progress(Event) { isStarted: true, isEnded: true, file: File(), progress: 1 } loadend() Success
Code Timeout (a moment for us)
Content First
UX the state { isStarted: false, isEnded: false, file: null,
progress: 0 } selectFile(File) Start { isStarted: true, isEnded: false, file: File(‘puppies.gif’), progress: 0 } Uploading { isStarted: true, isEnded: false, file: File(‘puppies.gif’), progress: 0.75 } Uploading progress(Event) { isStarted: true, isEnded: true, file: File(), progress: 1 } loadend() Success
Start Uploading Success selectFile(File) progress(Event) loadend() UX the state machine
A Radical New Framework for describing immutable states
Ecma Script 6
Start Uploading Success selectFile(File) progress(Event) loadend() Turn this into ES6
Start Start at the beginning
Start the JavaScripts! class Start { constructor() { this.isStarted =
false; this.isEnded = false; this.file = null; this.progress = 0; } }
Start State isStarted: false isEnded: false file: null progress: 0
JavaScript representation class Start { constructor() { this.isStarted = false;
this.isEnded = false; this.file = null; this.progress = 0; } }
Start Uploading selectFile(File) Where do we go from here?
Transition method class Start { constructor() { this.isStarted = false;
this.isEnded = false; this.file = null; this.progress = 0; } selectFile(file) { return new Uploading(file); } }
Transition method • Never updates existing state • Always returns
a new state
Start Uploading selectFile(File) Transition to Uploading
Transition method class Start { constructor() { this.isStarted = false;
this.isEnded = false; this.file = null; this.progress = 0; } selectFile(file) { return new Uploading(file); } }
Uploading representation class Uploading { constructor(file, progress = 0) {
this.isStarted = true; this.isEnded = false; this.file = file; this.progress = progress; } }
Uploading State isStarted: true, isEnded: false, file: File(‘puppies.gif’), progress: 0
Uploading Success progress(Event) loadend() Uploading two ways out
Uploading representation class Uploading { constructor(file, progress = 0) {
this.isStarted = true; this.isEnded = false; this.file = file; this.progress = progress; } loaded() { return new Success(this.file); } progress(event) { let ratio = event.loaded / event.total) return new Uploading(this.file, ratio); } }
That’s some Code
Start Uploading Success selectFile(File) progress(Event) loadend() Your JavaScript
Building a Player
A Player • tracks the current frame of content •
maps events from the user into state transitions • maps events from external sources like the network
Playing with Ember
There are a lot of options right now
export default Ember.Component.extend({ url: 'https://posttestserver.com', model: new Start(), actions: {
fileChosen(file) { this.set('model', this.get('model').selectFile(file)); let xhr = this.xhr = new XMLHttpRequest(); xhr.upload.onprogress = (event)=> { this.set('model', this.get('model').progress(event)); }; xhr.onload = ()=> { this.set('model', this.get('model').sucess()); }; xhr.open('POST', this.get('url'), true); xhr.send(file); } } }); A Player Component
export default Ember.Component.extend({ url: 'https://posttestserver.com', model: new Start(), actions: {
fileChosen(file) { this.set('model', this.get('model').selectFile(file)); let xhr = this.xhr = new XMLHttpRequest(); xhr.upload.onprogress = (event)=> { this.set('model', this.get('model').progress(event)); }; xhr.onload = ()=> { this.set('model', this.get('model').sucess()); }; xhr.open('POST', this.get('url'), true); xhr.send(file); } } }); A Player Component
Explore Techniques
Content is King — Bill Gates
UX the movie
UX the movie
UX the state { isStarted: false, isEnded: false, file: null,
progress: 0 } selectFile(File) Start { isStarted: true, isEnded: false, file: File(‘puppies.gif’), progress: 0 } Uploading { isStarted: true, isEnded: false, file: File(‘puppies.gif’), progress: 0.75 } Uploading progress(Event) { isStarted: true, isEnded: true, file: File(), progress: 1 } loadend() Success
Start Uploading Success selectFile(File) progress(Event) loadend() UX the state machine
ES6 Classes class Uploading { constructor(file, progress = 0) {
this.isStarted = true; this.isEnded = false; this.file = file; this.progress = progress; } loaded() { return new Success(this.file); } progress(event) { let ratio = event.loaded / event.total) return new Uploading(this.file, ratio); } }
Content is King — Bill Gates
Thanks