building a single page app: one page at a time

64

Upload: ivayr-farah-netto

Post on 14-Jul-2015

2.769 views

Category:

Software


0 download

TRANSCRIPT

Building a Single Page App

One page at a time

@nettofarah

@nettofarah

[email protected]

full stack developer at IFTTT

http://ifttt.com/jobs

IFTTT

but this talk is also about

the evolution of S.P.A.

DHTML

java applets

Flash / Flex

GWT

ajax

the .js era

toda vez que o Chaves respira surge um framework .js novo

E isso não somos nós que dizemos. São as estatísticas.

</pt-br>

in the rails world

• prototype.js / jquery gem

• rjs (ughhh)

• asset pipeline

• turbo links?

rails asset pipeline

rails + gulp/grunt

I don’t want my app to be bound by page refreshes

the risks

• rework

• way too many spinning wheels

• the blank page effect

• SEO

DOING IT!

reuse!

• templates

• snippets

• scripts

• build system

reusing your rb/js code

server side renderingoptional, but makes a BIG DIFFERENCE!

mustache.(js|rb)

1 def regular_controller_method2 @participants = User.last(10)3 @event = Event.find_by_technology(:ruby)4 end

the normal way

1 <div> 2 <% if Time.now.saturday? && Time.now.hour > 13 %> 3 yay, Netto is talking about Single Page apps 4 <% else %> 5 Can't wait to see Netto's talk! 6 <% end %> 7 8 <% is_tropical_ruby = @event.name.match(/tropical/i) %> 9 <% is_active = @event.end_date =< Date.today %>10 11 <% if is_tropical_ruby && is_active %>12 yay :)13 <% end %>14 15 <% @participants.each ... %>16 # MOAR RUBY!! lol 17 <% end %>18 19 </div>

1 def api_controller_method 2 participants = User.last(10) 3 event = Event.find_by_technology(:ruby) 4 5 is_nettos_talk = Time.now.saturday? && Time.now.hour > 14 6 7 @h = { 8 participants: users, 9 event: {10 is_nettos_talk: is_nettos_talk,11 is_tropical_ruby: event.tropical_rb?,12 is_active: event.active?13 }14 }15 16 respond_to do |format|17 format.html18 format.json { render json: @h.to_json }19 end20 end

1 <div> 2 {{#is_nettos_talk}} 3 yay, Netto is talking about Single Page apps 4 {{/is_nettos_talk}} 5 6 {{^is_nettos_talk}} 7 Can't wait to see Netto's talk! 8 I should follow him on twitter: @nettofarah 9 {{/is_nettos_talk}}10 11 {{#event.is_tropical_ruby}}12 yay13 {{/event.is_tropical_ruby}}14 15 {{#participants}}16 ... SOME COOL MUSTACHE STUFF17 {{/participants}}18 </div>

Presenters?

hogan.js

minify your .mustache templates and serve with asset-pipeline

https://github.com/leshill/hogan_assets

1 define("hgn!templates/recipe" ,["hogan"],function(hogan){ return function render(c,p,i) {2 var t=this;t.b(i=i||"");t.b("<div> ");if(t.s(t.f("is_nettos_talk",c,p,1),c,p,0,25,71,"{{ }}")){t.rs(c,p,function(c,p,t){t.b(" yay, Netto is talking about Single Page apps ");});c.pop();}t.b(" ");if(!t.s(t.f("is_nettos_talk",c,p,1),c,p,1,0,0,"")){t.b(" Can't wait to see Netto's talk! I should follow him on twitter: @nettofarah ");};t.b(" ");if(t.s(t.d("event.is_tropical_ruby",c,p,1),c,p,0,235,240,"{{ }}")){t.rs(c,p,function(c,p,t){t.b(" yay ");});c.pop();}t.b(" ");if(t.s(t.f("participants",c,p,1),c,p,0,285,315,"{{ }}")){t.rs(c,p,function(c,p,t){t.b(" ... SOME COOL MUSTACHE STUFF ");});c.pop();}t.b(" </div>");return t.fl(); } }

require.js rails

https://github.com/jwhitley/requirejs-rails

• small and reusable components

• load only what you need

1 define('recipe_view', ['ingredient', 'recipeTemplate'], 2 function(Ingredient, recipeTemplate) { 3 4 var RecipeView = function() { 5 6 function render() { 7 var ingredient = new Ingredient({..}); 8 var fragment = recipeTemplate.render(ingredient); 9 $(..).html(fragment);10 }11 12 ...13 };14 15 return RecipeView;16 }17 );

legacy routes

config/routes.rb -> app/js/routes.js

https://github.com/pseudomuto/routesjs-rails

reporting

1 // very simplistic example 2 3 window.onerror = function(message, url, lineNumber) { 4 // a rails controller 5 // or some other service 6 $.post('/js-errors', { 7 message: message, 8 url: url, 9 line_number: lineNumber10 ...11 });12 }

testing

the consequences

the positive ones

fast(er) experience

• (potentially) independent deployment strategy

• better loading time and user experience

• decoupled code and test suite

the bad and the ugly

complexity

race conditions

regular web app

initial load

login info

js load event hooks/callbacks

single page (async) app

1 // Regular JS App2 function Session() {3 var currentUser = App.User; // this comes from erb4 function onUserLogIn(callback) {5 if (currentUser != null) {6 callback();7 }8 }9 }

1 // Single Page App Code 2 function Session() { 3 var onLogInCallbacks = []; 4 var currentUser; 5 6 function fetchCurrentUser() { 7 $.ajax(...).done(function() { 8 _.each(onLoginCallbacks, function(callback) { 9 callback(currentUser);10 })11 });12 }13 14 function onUserLogIn(callback) {15 if (currentUser != null) {16 callback(currentUser);17 } else {18 onLoginCallbacks.push(callback);19 }20 }21 }

Save callback for later

run callbacks

memory leaks

3 var onLogInCallbacks = []; 4 var currentUser; 5 6 function fetchCurrentSession() { 7 $.ajax(...).done(function() { 8 _.each(onLoginCallbacks, function(callback) { 9 callback(currentUser);10 });11 12 clear(onLoginCallbacks);13 });14 }15 16 function onUserLogIn(callback) {17 if (currentUser != null) {18 callback(currentUser);19 } else {20 onLoginCallbacks.push(callback);21 }22 }

cleanup after yourself

do not block the main thread

1 function onUserLogIn(callback) {2 if (currentUser != null) {3 setTimeout(callback, 0, currentUser);4 }5 }

instance x prototype

learnings

page refreshes are JS developers best friends

beware of latency

events, callbacks and promises

Don’t just learn a framework. Learn JavaScript.

what’s next?

• ifttt.com next gen!

• react.js

• flight.js