beyond dom manipulations: building stateful modules with events and promises

Post on 01-Nov-2014

2.651 Views

Category:

Technology

1 Downloads

Preview:

Click to see full reader

DESCRIPTION

Presented at jQuery Conf Portland 2013

TRANSCRIPT

Building Stateful Modules with Events and PromisesDOM Manipulation

patrickCAMACHO

Beyond

Friday, June 14, 13

Crashlytics for Android & iOS

Friday, June 14, 13

Friday, June 14, 13

Friday, June 14, 13

Friday, June 14, 13

Friday, June 14, 13

Rails to Backbone.

Friday, June 14, 13

What did we have?

ModelBackbone.Model

Backbone.Collection

RoutingBackbone.Router,Backbone.History

ViewsBackbone.Views

EventsBackbone.Events

MV* components

Friday, June 14, 13

What’s missing?

Friday, June 14, 13

Transitioning Pages to States.

Friday, June 14, 13

Piecemeal.

App

State

Friday, June 14, 13

Piecemeal.

App

Router

State

Friday, June 14, 13

Piecemeal.

App

Router Directors

State

Friday, June 14, 13

Piecemeal.

App

Router Directors

State

Friday, June 14, 13

Adding a modal.

Friday, June 14, 13

Friday, June 14, 13

Needed better structure.

• Built on single flow and

states

• Modal didn’t fit flow

• Back to the

drawing board

App

Router Directors

State

Friday, June 14, 13

Needed better structure.

• Built on single flow and

states

• Modal didn’t fit flow

• Back to the

drawing board

App

Router Directors

State

Settings

Friday, June 14, 13

The birth of the “module”.

• Entirely independent pieces of functionality

• It could accept events and start / stop itself

State

this.$('.settings').click(function(){ CLS.Components.Settings.trigger('start');});

Settings

this.$('.overlay').click((function(){ this.trigger('stop');}).bind(this));

Friday, June 14, 13

Async behavior.

Friday, June 14, 13

Async behavior in states.

• Fetching data, animations, etc

• Want to shut anything down when stopping

Settings

Server

(rendering)

Friday, June 14, 13

Promises.

• $.Promises and $.Deferreds

• .done, .fail, .always

• .resolve, .reject

fetch1 = $.get('data.json');fetch2 = $.get('data2.json');

fetch1.done(function(){ console.log(‘success!’); } fetch2.always(function(){ console.log(‘complete!’); }

$.when(fetch1, fetch2).fail(function(){ console.log(‘fail!’); });

Friday, June 14, 13

Using with Components.

Settings.start = function() { this.stopDeferred = $.Deferred();

fetch1 = $.get('data.json'); this.stopDeferred.done(fetch1.abort);

fetch2 = $.get('data2.json'); this.stopDeferred.done(fetch2.abort);

$.when(fetch1, fetch2).done(this.render.bind(this));}

Settings.stop = function() { this.stopDeferred.resolve();}

Friday, June 14, 13

Good in the short run.

• Only had one application

• Components lived

forever

• Singletons hid the

problems

Settings Alert Center

Real Time Analytics

Friday, June 14, 13

Multiple applications.

Friday, June 14, 13

Distinct functionality.

Friday, June 14, 13

Distinct functionality.

Friday, June 14, 13

Multiple applications.

• Lost core assumption of a page-long app

• Apps began to look more and more like modules

Onboarding

Onboarding.start : function(){ if(this._isActive) return; ... this._isActive = true;}

Onboarding.stop : function(){ if(!this._isActive) return; ... this._isActive = false;}

Friday, June 14, 13

Multiple applications.

Apps

Router Directors

State

Friday, June 14, 13

Persistent functionalty.

• Components needed to be started / stopped by apps on

start / stop

• Not all should be started or stopped

• Back to heavy conditionals

if(nextApp === 'onboard') { CLS.Components.Settings.trigger('stop'); CLS.Components.AlertCenter.trigger('stop');} else if(nextApp === 'logout') { CLS.Components.Settings.trigger('stop'); CLS.Components.AlertCenter.trigger('stop'); CLS.Components.RealTime.trigger('stop');} else if...

Friday, June 14, 13

Finding a pattern.

Friday, June 14, 13

Same problem, different levels.

• Eventing

• Start

• Stop

• Dependencies

App

State State

Component Component Component

Friday, June 14, 13

Isolating knowledge.

Friday, June 14, 13

Too many direct references.

• Don’t know outside information

• Clearest in stopping dependencies

if(nextApp === 'onboard') { CLS.Components.Settings.trigger('stop'); CLS.Components.AlertCenter.trigger('stop');} else if(nextApp === 'logout') { CLS.Components.Settings.trigger('stop'); CLS.Components.AlertCenter.trigger('stop'); CLS.Components.RealTime.trigger('stop');} else if...

Friday, June 14, 13

Eventing with arguments.

Onboarding Dashboard

Dashboard.start = function() { Onboarding.trigger('stop', this.dependencies);}

Onboarding.stop = function(dependencies) { if(dependencies == null) { dependencies = [] } this.dependencies.forEach(function(dependencies){ if(dependencies.indexOf(dependency) < 0) { dependency.trigger('stop', dependencies); } });}

Friday, June 14, 13

• Wanted events, but not the knowledge

Still tightly coupled.

Dashboard.start = function() { Onboarding.trigger('stop', this.dependencies); LoggedOut.trigger('stop', this.dependencies);}

Friday, June 14, 13

Simplify functionality.

Friday, June 14, 13

Using a vent.

• All of these pieces are using events

• Isolate that functionality to a single unit

Vent

Vent = function() {...}Vent.prototype.on = function() {...}Vent.prototype.one = function() {...}Vent.prototype.off = function() {...}Vent.prototype.trigger = function() {...}

Friday, June 14, 13

Sharing a vent.

OnboardingDashboard Vent

Onboarding.start = function() { this.vent.trigger('app:dashboard:stop', this.dependencies); this.vent.trigger('app:loggedOut:stop', this.dependencies);}

Friday, June 14, 13

Smart subscriptions.

Dashboard.start = function(vent) { this.vent.trigger('app:onBeforeStart', this.dependencies); this.vent.one('app:onBeforeStart', this.stop.bind(this));}

Onboarding.start = function(vent) { this.vent.trigger('app:onBeforeStart', this.dependencies); this.vent.one('app:onBeforeStart', this.stop.bind(this));}

OnboardingDashboard Vent

Friday, June 14, 13

Sharing information.

Friday, June 14, 13

• Share data between modules

• Use vent to register responses and request

Synchronous data returns.

VentDashboard Settings

Friday, June 14, 13

Dashboard.start = function() { this.currentApplication = ‘foo bar’ this.vent.setResponse( 'current_application', (function(){ return this.currentApplication; }).bind(this); );}

Settings.start = function() { app = this.vent.requestResponse('current_application');}

Synchronous data returns.

Friday, June 14, 13

Tying it all together.

Friday, June 14, 13

Modularize all the things!

• Isolated functionality

• Start / stop

• Managing dependencies

• Eventing

• Async behavior

Friday, June 14, 13

Modularize all the things!

Component

App

State

• Isolated functionality

• Start / stop

• Managing dependencies

• Eventing

• Async behavior

Friday, June 14, 13

Vent

Rethinking the flow.

Friday, June 14, 13

Vent

Rethinking the flow.

Router

Friday, June 14, 13

ComponentsVent

Rethinking the flow.

Apps

Router

Friday, June 14, 13

Vent

ComponentsVent

Rethinking the flow.

Apps

Router

States

Friday, June 14, 13

Vent

Vent

ComponentsVent

Rethinking the flow.

Apps

Router

States

States

Friday, June 14, 13

Making it yours

• Manage complexity and scale

• Isolate functionality into modules

• Manage dependencies

• Allow modules to communicate through a vent

Friday, June 14, 13

YOUpatrickCAMACHO

Thank

try.crashlytics.com/jobs

Friday, June 14, 13

top related