Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Living Immutably with Ember

Living Immutably with Ember

From idea to action, using immutability in Ember applications.

Charles Lowell

May 21, 2016
Tweet

More Decks by Charles Lowell

Other Decks in Programming

Transcript

  1. I Said • Don’t use a component’s properties in its

    template • Don’t use computed properties • Don’t use Ember.Object at all
  2. A Wild File Object Appears { isStarted: false, isEnded: false,

    file: null, progress: 0 } selectFile(File) Start
  3. 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 ???
  4. 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)
  5. 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
  6. 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
  7. Start the JavaScripts! class Start {
 constructor() {
 this.isStarted =

    false;
 this.isEnded = false;
 this.file = null;
 this.progress = 0;
 }
 }

  8. JavaScript representation class Start {
 constructor() {
 this.isStarted = false;


    this.isEnded = false;
 this.file = null;
 this.progress = 0;
 }
 }

  9. Transition method class Start {
 constructor() {
 this.isStarted = false;


    this.isEnded = false;
 this.file = null;
 this.progress = 0;
 }
 
 selectFile(file) {
 return new Uploading(file);
 }
 }
  10. Transition method class Start {
 constructor() {
 this.isStarted = false;


    this.isEnded = false;
 this.file = null;
 this.progress = 0;
 }
 
 selectFile(file) {
 return new Uploading(file);
 }
 }
  11. Uploading representation class Uploading {
 constructor(file, progress = 0) {


    this.isStarted = true;
 this.isEnded = false;
 this.file = file;
 this.progress = progress;
 }
 }
  12. 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);
 }
 }
  13. A Player • tracks the current frame of content •

    maps events from the user into state transitions • maps events from external sources like the network
  14. 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
  15. 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
  16. 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
  17. 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);
 }
 }