Slide 1

Slide 1 text

Getting Truth Out of the DOM

Slide 2

Slide 2 text

Me

Slide 3

Slide 3 text

jQuery Rails SproutCore

Slide 4

Slide 4 text

Evented jQuery

Slide 5

Slide 5 text

handlers event DOM update Normal jQuery

Slide 6

Slide 6 text

native handlers browser event higher level event Evented Approach

Slide 7

Slide 7 text

native handlers browser event higher level event Evented Approach custom handlers higher level event DOM update

Slide 8

Slide 8 text

Events Reduce Coupling

Slide 9

Slide 9 text

DOM DOM

Slide 10

Slide 10 text

DOM DOM Event Bus Abstract Low-Level Events

Slide 11

Slide 11 text

instead of click DOM update

Slide 12

Slide 12 text

you can do "tab changed" DOM update

Slide 13

Slide 13 text

you can do "tab changed" DOM update low-level browser events "tab changed"

Slide 14

Slide 14 text

Widgets

Slide 15

Slide 15 text

DOM DOM object

Slide 16

Slide 16 text

Encapsulate State of Single View

Slide 17

Slide 17 text

"View" Managing Techniques

Slide 18

Slide 18 text

Data Centric Approach

Slide 19

Slide 19 text

"Reality" Objects Todo DOM "tell me when Todo changes"

Slide 20

Slide 20 text

"Reality" Objects Todo DOM "tell me when Todo changes" isDone=true

Slide 21

Slide 21 text

"Reality" Objects Todo DOM "tell me when Todo changes" isDone=true check off checkbox

Slide 22

Slide 22 text

"Reality" Objects Todo DOM Todo DOM "tell me when DOM changes"

Slide 23

Slide 23 text

"Reality" Objects Todo DOM checkbox checked off Todo DOM "tell me when DOM changes"

Slide 24

Slide 24 text

"Reality" Objects Todo DOM isDone=true checkbox checked off Todo DOM "tell me when DOM changes"

Slide 25

Slide 25 text

This is Better

Slide 26

Slide 26 text

Mix and Match Todo Todo DOM DOM object

Slide 27

Slide 27 text

You: B.set('y', "OMG") A B x y

Slide 28

Slide 28 text

A B x y SC: A.set('x', "OMG")

Slide 29

Slide 29 text

native handlers browser event update data object Data-Centric Approach

Slide 30

Slide 30 text

native handlers browser event update data object data handler data object event DOM update Data-Centric Approach

Slide 31

Slide 31 text

"Models" and "Observers"

Slide 32

Slide 32 text

Implement Remaining Counter

Slide 33

Slide 33 text

Do Something 1 remaining Remaining Counter

Slide 34

Slide 34 text

✔Do Something 0 remaining Remaining Counter

Slide 35

Slide 35 text

Do Something

Slide 36

Slide 36 text

✔Do Something

Slide 37

Slide 37 text

✔Do Something native change handler

Slide 38

Slide 38 text

✔Do Something native change handler set isDone = true on object

Slide 39

Slide 39 text

✔Do Something native change handler set isDone = true on object object change handler

Slide 40

Slide 40 text

✔Do Something native change handler set isDone = true on object object change handler recalculate remaining & update DOM

Slide 41

Slide 41 text

Intermediate State

Slide 42

Slide 42 text

Mark All Done Do Something Something Else More Things Even More!

Slide 43

Slide 43 text

Mark All Done Do Something Something Else More Things Even More! ✔

Slide 44

Slide 44 text

✔ ✔ Mark All Done Do Something Something Else More Things Even More! ✔ ✔ ✔

Slide 45

Slide 45 text

Implementation list.forEach(function(todo) { todo.set('isDone', value); });

Slide 46

Slide 46 text

✔Do Something native change handler set isDone = true on object object change handler recalculate remaining & update DOM

Slide 47

Slide 47 text

native change handler set isDone = true on object object change handler recalculate remaining & update DOM set isDone = true on object object change handler recalculate remaining & update DOM set isDone = true on object object change handler recalculate remaining & update DOM set isDone = true on object object change handler recalculate remaining & update DOM

Slide 48

Slide 48 text

native change handler set isDone = true on object object change handler recalculate remaining & update DOM set isDone = true on object object change handler recalculate remaining & update DOM set isDone = true on object object change handler recalculate remaining & update DOM set isDone = true on object object change handler recalculate remaining & update DOM

Slide 49

Slide 49 text

Solution: Flag

Slide 50

Slide 50 text

list.bind('change', function() { if(!list.bulk) { var remaining = list.remaining(); var text = remaining + ' remaining'; $("#stats").html(text); } });

Slide 51

Slide 51 text

Implementation list.bulk = true; list.forEach(function(todo) { todo.data('isDone', value); }); list.bulk = false; list.trigger('change');

Slide 52

Slide 52 text

Common Cases

Slide 53

Slide 53 text

Recurring Calendar Event

Slide 54

Slide 54 text

Batched Saves to the Server

Slide 55

Slide 55 text

Delete All Contacts

Slide 56

Slide 56 text

Gmail: Select All

Slide 57

Slide 57 text

Answer: Batched Operations

Slide 58

Slide 58 text

triggerLater

Slide 59

Slide 59 text

$(todo).bind('changeData', function(e, name) { if (name === 'isDone') { var todo = $(this); var check = todo.data('element'); check.attr(checked, todo.isDone); var ul = check.closest('ul'); var list = ul.data('list'); list.triggerLater('change'); } });

Slide 60

Slide 60 text

"triggerLater" semantics

Slide 61

Slide 61 text

start native handlers "triggerLater" semantics

Slide 62

Slide 62 text

start native handlers triggerLater calls "triggerLater" semantics

Slide 63

Slide 63 text

start native handlers triggerLater calls end native handlers "triggerLater" semantics

Slide 64

Slide 64 text

start native handlers triggerLater calls end native handlers execute triggerLater once "triggerLater" semantics

Slide 65

Slide 65 text

native change handler set isDone = true on object object change handler triggerLater list change set isDone = true on object object change handler triggerLater list change set isDone = true on object object change handler triggerLater list change set isDone = true on object object change handler triggerLater list change

Slide 66

Slide 66 text

native change handler set isDone = true on object object change handler triggerLater list change set isDone = true on object object change handler triggerLater list change set isDone = true on object object change handler triggerLater list change set isDone = true on object object change handler triggerLater list change

Slide 67

Slide 67 text

Batched Commits

Slide 68

Slide 68 text

todo.bind('changeData', function() { store.push(this); store.triggerLater('save'); });

Slide 69

Slide 69 text

1:1 Model/View Coupling

Slide 70

Slide 70 text

A Data Object "has an element"

Slide 71

Slide 71 text

Another Problem

Slide 72

Slide 72 text

Application State

Slide 73

Slide 73 text

Aggregates: Reverse Engineering This is kind of ok when you have 1:1 model to view

Slide 74

Slide 74 text

Wordpress

Slide 75

Slide 75 text

New Twitter

Slide 76

Slide 76 text

Github Issues

Slide 77

Slide 77 text

Edge Cases It can be easy to forget things in even simple interactions

Slide 78

Slide 78 text

✔ ✔ Mark All Done Evolving User The Server The Client

Slide 79

Slide 79 text

✔ ✔ Mark All Done Evolving User The Server The Client

Slide 80

Slide 80 text

✔ ✔ Mark All Done Evolving User The Server The Client ✔ ✔

Slide 81

Slide 81 text

✔ ✔ Mark All Done Evolving User The Server The Client Closing ✔

Slide 82

Slide 82 text

✔ ✔ Mark All Done Evolving User The Server The Client Closing ✔ X

Slide 83

Slide 83 text

✔ ✔ Mark All Done Evolving User The Server The Client Closing ✔ X

Slide 84

Slide 84 text

✔ ✔ Mark All Done Evolving User The Server The Client ✔ ✔

Slide 85

Slide 85 text

✔ ✔ Mark All Done Evolving User The Server The Client ✔ Clear 3 Items

Slide 86

Slide 86 text

✔ ✔ Mark All Done Evolving User The Server The Client ✔ ✔ Clear 3 Items

Slide 87

Slide 87 text

✔ ✔ Mark All Done Evolving User The Server The Client ✔ ✔ Clear 3 Items

Slide 88

Slide 88 text

Mark All Done Clear 0 Items

Slide 89

Slide 89 text

state of reality state of the application state of the presentation

Slide 90

Slide 90 text

Model Controller View

Slide 91

Slide 91 text

Mark All Done Clear 0 Items application state

Slide 92

Slide 92 text

Selected Item in an Array

Slide 93

Slide 93 text

MVC Calculations are done on real objects, not by reverse engineering DOM

Slide 94

Slide 94 text

✔ Mark All Done Evolving User The Server What do you want to do? 1 item remaining

Slide 95

Slide 95 text

✔ Mark All Done Evolving User The Server What do you want to do? 1 item remaining View

Slide 96

Slide 96 text

✔ Mark All Done Evolving User The Server What do you want to do? 1 item remaining View

Slide 97

Slide 97 text

✔ Mark All Done Evolving User The Server What do you want to do? 1 item remaining Controller (App State)

Slide 98

Slide 98 text

✔ Mark All Done Evolving User The Server What do you want to do? 1 item remaining View

Slide 99

Slide 99 text

✔ Mark All Done Evolving User The Server What do you want to do? 1 item remaining Unchecked (Controller)

Slide 100

Slide 100 text

✔ Mark All Done Evolving User The Server What do you want to do? 1 item remaining View

Slide 101

Slide 101 text

✔ Mark All Done Evolving User The Server What do you want to do? 1 item remaining Checked (Model)

Slide 102

Slide 102 text

✔ Mark All Done Evolving User The Server What do you want to do? 1 item remaining Title (Model)

Slide 103

Slide 103 text

✔ ✔ Mark All Done Evolving User The Server The Client ✔ ✔ allAreDone (controller)

Slide 104

Slide 104 text

ArrayController allAreDone Property allAreDone: function(key, value) { if (value !== undefined) { this.setEach('isDone', value); return value; } else { return this.get('length') && this.everyProperty('isDone', true); } }.property()

Slide 105

Slide 105 text

getter allAreDone: function(key, value) { if (value !== undefined) { this.setEach('isDone', value); return value; } else { return this.get('length') && this.everyProperty('isDone', true); } }.property()

Slide 106

Slide 106 text

setter allAreDone: function(key, value) { if (value !== undefined) { this.setEach('isDone', value); return value; } else { return this.get('length') && this.everyProperty('isDone', true); } }.property()

Slide 107

Slide 107 text

SproutCore MVC: Auto Sync Layers

Slide 108

Slide 108 text

Checkbox {{view SC.Checkbox valueBinding="Todos.todoListController.allAreDone"}} Mark All as Done

Slide 109

Slide 109 text

You: Declare Linkage

Slide 110

Slide 110 text

SproutCore: Keep Layers in Sync

Slide 111

Slide 111 text

Buffered Between Layers "Buffered Between Layers" Changes to the model layer for a particular event can all be made before they are propagated to the controller or view.

Slide 112

Slide 112 text

How Does it Work?

Slide 113

Slide 113 text

Properties

Slide 114

Slide 114 text

Properties ArrayController

Slide 115

Slide 115 text

Properties ArrayController allAreDone

Slide 116

Slide 116 text

While Handling Events ArrayController allAreDone dirty? This flag is automatically set by SproutCore if the property or any of its dependencies change.

Slide 117

Slide 117 text

While Handling Events ArrayController allAreDone dirty? ✔ This flag is automatically set by SproutCore if the property or any of its dependencies change.

Slide 118

Slide 118 text

Later... ArrayController allAreDone dirty? DOM

Slide 119

Slide 119 text

Dependencies allAreDone: function(key, value) { if (value !== undefined) { this.setEach('isDone', value); return value; } else { return this.get('length') && this.everyProperty('isDone', true); } }.property('@each.isDone')

Slide 120

Slide 120 text

Templating

Slide 121

Slide 121 text

Mustache.js

Hello {{name}}

You have just won ${{value}}!

{{#in_ca}}

Well, ${{taxed_value}}, after taxes.

{{/in_ca}}

Slide 122

Slide 122 text

Handlebars.js

Hello {{name}}

You have just won ${{value}}!

{{#if in_ca}}

Well, ${{taxed_value}}, after taxes.

{{/if}}

Slide 123

Slide 123 text

So close! Because these template engines limit arbitrary logic, it should be possible to automatically UPDATE if the objects change

Slide 124

Slide 124 text

Requires: Property Observing

Slide 125

Slide 125 text

Requires: Bindings

Slide 126

Slide 126 text

Enter SproutCore!

Slide 127

Slide 127 text

Todos

{{#view Todos.CreateTodoView}} {{/view}} {{#view Todos.StatsView}}
{{#view Todos.ClearCompletedView}} Clear Completed Todos {{/view}} {{displayRemaining}} remaining.
{{/view}}

Slide 128

Slide 128 text

{{view SC.Checkbox valueBinding= "Todos.todoListController.allAreDone"}} Mark All as Done {{#each Todos.todoListController itemClassBinding="isDone"}} {{view SC.Checkbox valueBinding= "parentView.content.isDone"}}
{{content.title}}
{{/each}}

Slide 129

Slide 129 text

Declare Bindings in Template

Slide 130

Slide 130 text

Handle Events in SC.View

Slide 131

Slide 131 text

Magic

Slide 132

Slide 132 text

Where does jQuery Fit In?

Slide 133

Slide 133 text

Model Controller View

Slide 134

Slide 134 text

Model Controller jQuery

Slide 135

Slide 135 text

Mark All Done ✔ when allAreDone changes in the controller

Slide 136

Slide 136 text

Mark All Done ✔ later...

Slide 137

Slide 137 text

Mark All Done ✔ DOM DOM Todos.AllDoneView view object is notified

Slide 138

Slide 138 text

SC Views Manipulate DOM With jQuery this.$('input').attr('checked', val);

Slide 139

Slide 139 text

SC Views Manipulate DOM With jQuery SC.Checkbox = SC.View.extend({ value: function(key, value) { if(value !== undefined) { this.$('input').attr('checked', value); } else { value = this.$('input').attr('checked'); } return value; }.property() });

Slide 140

Slide 140 text

Define Properties

Slide 141

Slide 141 text

Define Linkage

Slide 142

Slide 142 text

Define a Value Binding {{view SC.Checkbox valueBinding="Todos.todoListController.allAreDone"}} Mark All as Done

Slide 143

Slide 143 text

There is No Step 3

Slide 144

Slide 144 text

To Recap

Slide 145

Slide 145 text

Automatic Sync of App State

Slide 146

Slide 146 text

Batches Intermediate State SproutCore automatically handles batching changes to avoid the intermediate state problem

Slide 147

Slide 147 text

(buffered sync between layers)

Slide 148

Slide 148 text

Uniform Object Model The SproutCore Object Model wraps all of this up in a consistent, well-defined way to declare object relationships

Slide 149

Slide 149 text

Data Store The SproutCore data store takes advantage of the declarative nature of SproutCore to let you lazily populate Arrays asynchronously. API TO BE IMPROVED

Slide 150

Slide 150 text

sproutcore.js

Slide 151

Slide 151 text

Single File

Slide 152

Slide 152 text

1MB?

Slide 153

Slide 153 text

100k?

Slide 154

Slide 154 text

30k!

Slide 155

Slide 155 text

Runtime Handlebars View Layer

Slide 156

Slide 156 text

http://todos20.strobeapp.com

Slide 157

Slide 157 text

https://github.com/ sproutcore/todos

Slide 158

Slide 158 text

Questions? Yehuda Katz @wycats yehudakatz.com