Slide 1

Slide 1 text

Simple JavaScript abstractions

Slide 2

Slide 2 text

I’m Kim I work at BEKK I’m on Twitter: @kimjoar I’m on Github: @kjbekkelund

Slide 3

Slide 3 text

$

Slide 4

Slide 4 text

$ Stop using jQuery directly all over your code!

Slide 5

Slide 5 text

Why? jQuery(function() { $(“.user form”).submit(function(e) { e.preventDefault(); $.ajax({ // ... success: function(data) { var name = data.name ? data.name : “Unknown”; $(“.user .info”).append(“

” + name + “

”); } }); }); });

Slide 6

Slide 6 text

Why? jQuery(function() { $(“.user form”).submit(function(e) { e.preventDefault(); $.ajax({ // ... success: function(data) { var name = data.name ? data.name : “Unknown”; $(“.user .info”).append(“

” + name + “

”); } }); }); }); PAGE EVENT USER EVENT NETWORK I/O NETWORK EVENT TEMPLATING PARSING RESPONSE Slide inspired by https://speakerdeck.com/u/bkeepers/p/the-plight-of-pinocchio

Slide 7

Slide 7 text

This is a mess

Slide 8

Slide 8 text

“Internal quality is what lets us cope with continual and unanticipated change” Steve Freeman, Nat Pryce Growing Object-Oriented Software, Guided by Tests

Slide 9

Slide 9 text

We need decoupling We need structure We need abstractions

Slide 10

Slide 10 text

Abstracting views

Slide 11

Slide 11 text

It owns an HTML element A view’s responsibility:

Slide 12

Slide 12 text

Views can read from the DOM, change it, and listen for events

Slide 13

Slide 13 text

Our view API var el = $(“.user”); var userView = new UserView(el); userView.showUserInfo(); UserView is now responsible for everything related to the DOM within `.user`

Slide 14

Slide 14 text

View API implementation var UserView = function(el) { this.el = el; } UserView.prototype.showUserInfo = function() { this.el.append("

Kim Joar

"); } That’s right — it’s just convention

Slide 15

Slide 15 text

… but how will the view get any data?

Slide 16

Slide 16 text

Events!

Slide 17

Slide 17 text

Events var events = new EventEmitter(); var UserView = function(el) { this.el = el; // Bind events events.addListener(“user:fetched”, this.showUserInfo, this); } UserView.prototype.showUserInfo = function(user) {} // Trigger events events.emit(“user:fetched”, { name: “Kim Joar” });

Slide 18

Slide 18 text

An example jQuery(function() { new UserView($(“.user”)); events.addListener(“user:submit”, getUser()); }); function getUser() { $.ajax({ // ... success: function(data) { events.emit(“user:fetched”, data); } }); }

Slide 19

Slide 19 text

Or, even better jQuery(function() { var user = new User(); // user model! new UserView($(“.user”), user); // view is allowed to }); // call methods on model —

Slide 20

Slide 20 text

Or, even better jQuery(function() { var user = new User(); new UserView($(“.user”), user, ...); }); And similarly we can also pass in other dependencies as arguments (events are not always the answer)

Slide 21

Slide 21 text

Our views ≈ Presenter or Controller

Slide 22

Slide 22 text

We have encapsulated network requests in Models. We have encapsulated the DOM in Views. We communicate using events instead of relying on callbacks. We have code that is far easier to test. And we are on our way to an MV* architecture.

Slide 23

Slide 23 text

Let’s add some sugar

Slide 24

Slide 24 text

Localized lookup // as views are passed `el`, which is a jQuery object, // we can search through the descendants as follows: userView.el.find(“h1”) // … but if we add: UserView.prototype.$ = function(selector) { return this.el.find(selector); } // we can do this instead: userView.$("h1") // less coupled to the DOM!

Slide 25

Slide 25 text

Event delegation UserView.prototype.events = { “submit .user-form”: “validateAndPost”, “click .title”: “editName” }; UserView.prototype.validateAndPost = function() {} // or (using Backbone.js): var UserView = View.extend({ events: { “submit .user-form”: “validateAndPost” }, validateAndPost: function() {} });

Slide 26

Slide 26 text

Local events var UserView = function(el, user) { this.el = el; // Binding events on `user` instead of `events` user.addListener(“fetched”, this.showUserInfo, this); } User.prototype.addListener = function() { if (!this.events) this.events = new EventEmitter(); this.events.apply(this.events, arguments); } // and the same for `emit` user.emit(“fetch”); // triggers the “local” event

Slide 27

Slide 27 text

Add sugar using layers, mixins, or an existing library

Slide 28

Slide 28 text

If you like this, check out

Slide 29

Slide 29 text

“An element’s cohesion is a measure of whether its responsibilities form a meaningful unit” Steve Freeman, Nat Pryce Growing Object-Oriented Software, Guided by Tests

Slide 30

Slide 30 text

$ Don’t use it less, use it smarter!

Slide 31

Slide 31 text

Questions?