Keynote
-
business logic
- live much longer than views
- does not belong in handlers
- does not belong in views
-
views
- stateless
- able to change rapidly
- antipattern: tracking the state of the world
- designed for course grain rendering (unlike ember.js)
- tests should not rely on faking clicks, too much coupling to the dom
- should not be computing things that belong in a different abstraction
-
models
- under utilized for business logic
- wrap built-in methods to make methods more semantically meaningful
-
objects consume resources
-
gc uses reference counting
-
models and collections usually outlive views
-
if an instance of a class is around forever, it shouldnt reference other things. those things need to be allowed to die.
- are references
- usually emited by the dom, models, and collections
- views
- usually listen
- die quickly
- should be easy to unbind
# callback held by keg
aKeg.on "empty" griffin.notify
bKeg.on "empty" griffin.notify
cKeg.on "empty" griffin.notify
# later - unbinding griffin requires a reference to all kegs
aKeg.off "empty"
bKeg.off "empty"
cKeg.off "empty"
# callback held by griffin
griffin.listenTo(aKeg, "empty", griffin.notify)
griffin.listenTo(bKeg, "empty", griffin.notify)
griffin.listenTo(cKeg, "empty", griffin.notify)
# later - unbinding griffin only requires a reference to griffin
griffin.stopListening()
-
under-appreaciated place for logic
-
can add class methods too. eg
User.lookup(id)
that returns a promiseclass User @lookup = (id) -> deferred = Q.defer() xhr = new User({id: id}).fetch() xhr.then( (model) -> deferred.resolve(model)) return deferred.promise User.lookup(id).then (user) -> user.doStuff()
- routes and views are your interface
# `this` is collection that is tracking a top level collection
@listenTo("all", target, (ev, model) ->
if ev == 'add' or ev == 'remove'
this[ev](model)
)
# wont get called until you stop calling render
render: _.debounce ->
# called after method hasnt been called for timeout period
# do rendery stuff
, 0
# under the hood, browsers set a minimum timeout of 4ms
# essentially just waiting for the call stack to resolve
# without rendering while the models are still updating
render: (items = @collection.models) ->
toRender = _(items).first(50)
toDefer = _(items).rest(50)
_(toRender).each((model) ->
(new ItemView(item)).render()
)
_.defer(@render, toDefer)
Account = Backbone.Model.extend({...})
Publisher = Account.Model.extend({...})
Reader = Account.Model.extend({...})
GuestReader = Reader.extend({...})
-
avoid overuse
-
'mediator pattern' is not an excuse to publish all events to all things
Backbone.trigger Backbone.on
@on("change", ->
changes.push(this.previousAttributes())
)
undo: ->
undone.push @toJSON()
@set(changes.pop())
redo: ->
@set(undone.pop())
of Rdio
"Little Opinions, Big Possibilities: The tools and patterns for building lorge scale Backbone Apps"
- backplug.io for top Backbone plugins
- Addy Osmani has a blog people should read (Patterns For Larges-Scale JavaScript Application Architectur in particular)
- Two-way databinding with stickit and rivetsjs
of Adobe
"Building Reflow" slides
- Prototype all the things, fail hard, fail fast
- Prototype the things you think are impossible
- Backbone was a big win because of the onboarding process alone, open source reduces ramp-up time
- Don't acquire "pink wheel syndrome" - "No, no, I'm not re-inventing the wheel, I'm just making a pink one."
- Open source libraries beat out custom libraries every time because they have been "used in anger". Battle tested, thoroughly beatten upon.
- Shout-out to the conversion to require.js. Painful, but very useful
- Code coverage - sonar has some really erotic graphs
- Approaches prototyping by making a HTML mockup and then TDD added functionality with JS
of Disqus
"Backbone at Disqus: a Postmortem" slides
Third-party JS - Ben's book on third party applications
-
Various browser policies about secure origins
-
JSONP - loading data from other origins via script tags. More recently Cross-origin resource sharing (CORS) is used
-
postMessage
communication enables Backbone to be embeded in<iframe>
s -
Backbone.UniqueModel for ensuring unique model instances accross an app. Will also sync data between windows/iframes
#####Use a session object to proxy calls to different user models
window.session = new AnonymousUser()
window.session = new User({id: "someId"})
window.session = new SingleSignOnUser()
class UserMenu extends Backbone.View
initialize: () ->
@session = @options.session
@listenTo(session, 'change', @render)
of Kinvey "Enterprise and the Mobile Web"
- Mixins are your friend, but document & test relentlessly
ResourceRoutingMixin = {
fn: ->
thing: ->
}
_.extend(Blogpost.prototype, ResourceRoutingMixin)
- Cacheing resources
IndexedDB to cache data, use a success handler and call it twice. once when data is successfully taken out of cache, again when model syncs. Note websql is no longer a supported initiative. IndexedDB is the way to go. Space is critical
# Note that backbone provides extension points via options hash
fetch: (options) ->
if options.useCache
save: if(!navigator.onLine) db.transaction(this.name, 'readwrite')
Api security by user tokens - AnonymousUser token - can only create accounts - User token - user level access - Admin token - ignore all permissions, never included in client side app
Protip: 'innovation project' translates to 'we dont know what we are doing'
"Test-Driven Backbone Development"
-
Jasmine fails hard on async
-
Karma / Testacular + Selinium (multi-device browser testing)
-
jsDom - fake dom that makes dom tests superfast?
-
Test complexity - complexity grows when stringing together modules. Want to be able to black box the modules. Dont want to have to test what they claim to be implementing
resistance to change is not very salient in a code base
"Dependency Injection for Fun and Profit"
Dependo - visualize CommonJS and AMD dependencies
A way to remove hard-coded dependencies. Helps reduce the amount of code you need to hold in your head while developing. Dependencies become much easier to test or stub.
-
Enforce DI - dont allow a developer to instantiate a class unless all requisite refrences have been created
-
Use this Dunbarism
# trick: pick out of {options}
_.extend Klass, _pick(deps, options)
Often, web developers either forget or dont think to engineer client side javascript with all the OOP basics they would apply to other software applications.
- Single Responsibily Principle - do exactly one thing well
- Open / Closed Principle - "software entities should be open for extension, but closed for modification"
- Libshivtz Substitution Principle - replacing a class with a subclass should not create unexpected states or behavior
- Interface Segregation - no client should be forced to depend on methods it does not use
- Dependency Injection - depend on abstracions not implementations
A class should only ever have one reason to change.
require 'spec_helper'
# One responsibility
# Changes when endpoint changes
describe SomeController
let(:user){ create(:user) }
# Controller specs ...
end
# Two responsibilities
# Changes when endpoint changes
# Changes when user model changes
describe SomeController
let(:user) do
User.new :id => "5734", :required_field1 => "value"
end
# Controller specs ...
end
Eg in Backbone, you never override the Backbone.Model
constructor
method, instead, Backbone provided you with an extension point initialize
.
// Model constructor function
var Model = Backbone.Model = function(attributes, options){
// cid assigned
// url root set
// etc ...
this.initialize.apply(this, arguments)
}
A subclass should be able to replace an instance of its parent type without sacrificing correctness or any of the desireable properties of the parent class.
- TODO method arguments
- TODO method return types
- TODO preconditions / postconditions
- TODO invariants
- TODO history constraint - objs modifyable only through their methods (encapsulation)
No client should be forced to depend on methods it does not use. Rather than requiring an instance of a dependency (as seen above in the SRP example), stub out the methods that are depended on.
require 'spec_helper'
# One responsibility
# Changes when endpoint changes
describe SomeController
context "when authorized" do
let(:user){ create(:user) }
# Controller specs ...
end
end
Depend on abstractions - not implementations
Eg. View a collection of members.
# Decouple objects from their dependencies by passing them in
# at initialization
class MembersView extends Backbone.View
initialize: (opts) ->
@members = opts.membersCollection
@memberView = opts.memberView
# Less flexible way to wire dependencies
# Not as easy to reuse view elsewhere, esp. with
# different member view classes
class MembersView extends Backbone.View
# Cannot change the way individual members are displayed
memberView: Some.Hardwired.ViewClass
# Collection restricted to this particular group of users
members: Members.Of.OneParticular.App
- Stubbing out hardwired dependencies is much more difficult
-
An application has two dependency graphs
- instance graphs - set up mainly through dependency injection
- constructor graph - loaded by module patterns
-
Recommends exposing a development variable to give you access to running application - for debugging
-
Tests enforce the contract of dependencies
Addy Osmani - aura.js Dont rely on service, rely on events generated by the service
- Components are the future
- Modules - dominic denecola `-> "Fight for this"
"The Geologic Time Scale of Gilt.com's View Layer"
git log --reverse play framework status code in json -> bad, mmmkay? scandlebars can render on server side or client side - sites should be robot friendly - what are the trade-offs?
flexibility and maitainability are trade-offs that need to be made Prediction that we will see more chaplainjs and marrionette type things More backplugs
is the DOM good enough? where is jQuery's place? "Why mobile web apps are slow" `-> READ it
TypeScipt?
window.history.pushstate
AMD +++ ViewModels
"Domain-Driven Web Applications Using Backbone"
Domain model is conceptual, in the head of an expert Need to establish a ubiqutous language - well understood longuage accross teams - business, engineering, designers, etc. Should be priority number 1 for designing a system
Strictly separate lagers
Downwards relations may have references Up stack must use events
| --------------- | | interface | | --------------- | | app logic | | --------------- | | domain | | --------------- | | infrastructure | | --------------- |
Real world domain is infinitely complex Entities need to have ids Value objects - commodity objects (do not need ids) Factory Aggregate route - single point of entry into a bundle of logic reduces complexity
"I Like my jQuery Plugins warm and Toasty"
_.defaults {options}, {}
$.data?
Introduces some new code smells that identify change-sensitive code.
Aka "Don't talk to strangers" or "One dot rule". A prescription for loosly coupled OOD code. Reduces an objects required knowledge about the world, making it more resistant to change.
Spicifically, any given method should only talk to:
- the object that owns it (
this
,@
) - the parameters it was passed
- any object the method creates
- the component objects its parent object
- a global variable (global access is not recommended)
class Courtyard
cook: (food) ->
# ### Yes ###
# method -> parent object
@unlock()
# method -> param
food.prep()
# method -> global variable
beer = new Beer
# method -> component of parent
@grill.start()
# method -> object created in method
beer.open()
# ### No ###
@grill.propane.check()
gas = @grill.getPropane()
gas.check()
Only expose the functionality you need
jQuery patterns list
- Recommends reading through old issues
- Also 'rays in the sun' - peach street in atlanta
"Beyond jQuery Widgets: JS UI Library Design"
tip: when google searching for recent code bugs, use 'last year' or 'last month' as search restriction
"Gist is a great way to open source" "(When reading code) if you puke a little in your mouth, probably don't use that code" "I found a method on your class and i called it" - if private methods at all accissible from other classes, they will be treated by devs as public her solution:
_private.method1 = function(){}
# module method
You're not really paying attention to everything in life Keep people from making stupid mistakes How to document all events fired by a module DOM/Data function Hand it a dollar
LuccidJS - light weight event jsBin data attributes Nyan cat reporter "How to unit test private functions"
Documentation -- please include 'why does this exist' -- please also include 'how can i use this'
of LinkedIn "Becoming a Control Freak with Controllers and Promises"
# authenticate will call success, not permitted will call error condition
Authintication
onRouteToPlace: ->
userCanGoToPlace(Deferred)
goToPlace: (deferred) ->
deferred.then -> @openPrivateView()
deferred.fail -> openUnauthorizedNoticeView()
- single branch, merging suck
- feature flags ftw
- train release mentality - jump on if you are ready
- ViewModel` - presenterish representation of a model
- there is space between the objects backbone provides - some additional legwork required for well structured code
|---------------------------| | | | Model | resolve | | |-------.-------------------| / \ | then(process NewRecord) | |---------------------------| | | | Controller | setCurrentRecord(record) | | |-------.-------------------| / \ | | onSaveClicked |---------------------------| | | | View | | | *click event* |---------------------------| Presentation layer
-
context is everything in communication "'Short asian guy' doesnt help" or at least not until you start describing his desk location or team
-
code is communication
-
be concise and explicit
-
pass in the parent object
class Child extends Backbone.View
initialize: (opts) ->
@parent = opts.parent
@listenTo(@parent, 'render', @delegateEvents)
- promises
A promise represents a value that may not be available yet. The primary method for interacting with a promise is its
then
method. - A+ spec
- neat function
all
- given an array of promises, return a new promise that resolve after all members resolve
# resolves after all syncs complete
Q.all([
a.sync()
b.sync()
c.sync()
]) # returns a deferred
-
everything should belong somewhere, and if it is not obviaus, then your work is establishing that place and making it obvious
-
controllers are finite
- can be paused
- monogomous - should only operate on 1 application layer (model layer)
-
unified codebase - jsHint
-
control by making extension easy
-
use helpers, use mixins
-
create and document hooks esp pre-render and post render
-
linkedin uses many analytic tools in there application, many tied to these sorts of hooks. they are a part of the framework they've built
-
linked in devs highlight the importanco of seeing backbone as a tool suite for constructing an application architecture - it is not an application architecture.
-
big things are hard to control
-
think slowly, it takes time to move large application code bases
- linked in converted to AMD step by step - cant imagine not converting
- shim as needed
-
page controllers help a lot - especially with metrics
-
fire (analytic) events for all the things
-
encourage you to check out the dependency graphs
-
Fiber - exteding components
-
Q - promises library
- provides long stack traces for debugging
- a number of helpful promise fns
- jQuery compatible
-
Fastclick - touch events - dont wait to see if it was a dbl click
-
Venusjs - runs your entire js testing system
"Advanced Architectural Patterns with Backbone and cujoJS"
CommonJS ++ Dependency injection - pass options into initialize Backbone - "Infrastructure / essential" Dependencies AOP - cause mixins are from hell
Pushing state into css ... ?
OOCSS
"Growing Up with Backbone"
CommonJS syntax, compile to require AMD is weighty
Universal module definition is U.G.L.Y. ES6 modules are a thing Decouple apprunning from config loading import template form "root!comp/thing/place.html" see transpile task
careful with insertRejuire[thing] to start app compress file source maps
web components Templates, Decorators, Custom elements, imports, shadow dom
MDV Element hosting the module @host {
} tbranden scope css
Factory methods rather than constructors