darwinjs-rails 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +145 -0
- data/Rakefile +1 -0
- data/app/assets/javascripts/darwin/base.coffee +75 -0
- data/app/assets/javascripts/darwin/controller.coffee +125 -0
- data/app/assets/javascripts/darwin/loader.coffee +54 -0
- data/app/assets/javascripts/darwin/template.coffee +43 -0
- data/app/assets/javascripts/darwin/view.coffee +108 -0
- data/app/assets/javascripts/darwin.coffee +16 -0
- data/darwinjs-rails.gemspec +26 -0
- data/doc/base.md +147 -0
- data/doc/controller.md +266 -0
- data/doc/introduction.md +228 -0
- data/doc/loader.md +116 -0
- data/doc/view.md +282 -0
- data/lib/darwinjs/rails/engine.rb +9 -0
- data/lib/darwinjs/rails/version.rb +5 -0
- data/lib/darwinjs-rails.rb +2 -0
- data/lib/generators/darwinjs/assets/assets_generator.rb +78 -0
- data/lib/generators/darwinjs/assets/templates/controllers/controller.coffee +5 -0
- data/lib/generators/darwinjs/assets/templates/controllers/namespace.coffee +1 -0
- data/lib/generators/darwinjs/assets/templates/views/namespace.coffee +2 -0
- data/lib/generators/darwinjs/assets/templates/views/view.coffee +5 -0
- metadata +153 -0
data/doc/base.md
ADDED
@@ -0,0 +1,147 @@
|
|
1
|
+
Darwin.Base is the common parent of all Darwin classes.
|
2
|
+
|
3
|
+
You can use it as well in any of your lib classes to
|
4
|
+
benefits of its features :
|
5
|
+
|
6
|
+
* option pattern
|
7
|
+
* class events
|
8
|
+
* mixins
|
9
|
+
|
10
|
+
|
11
|
+
# Option pattern
|
12
|
+
|
13
|
+
Any class inheriting from Darwin.Base can declare options
|
14
|
+
that propagate to children classes, but not to parent classes,
|
15
|
+
much like rails' `#class_attribute`. Observe the following :
|
16
|
+
|
17
|
+
```coffee
|
18
|
+
class MyParentClass extends Darwin.Base
|
19
|
+
@options {
|
20
|
+
foo: 'Foo'
|
21
|
+
}
|
22
|
+
|
23
|
+
class MyChildClass extends MyParentClass
|
24
|
+
@options {
|
25
|
+
foo: 'overriden foo'
|
26
|
+
bar: 'bar'
|
27
|
+
}
|
28
|
+
|
29
|
+
class MyOtherChildClass extends MyParentClass
|
30
|
+
@options {
|
31
|
+
bar: 'other bar'
|
32
|
+
}
|
33
|
+
|
34
|
+
new MyParentClass().options.foo # => 'Foo'
|
35
|
+
new MyChildClass().options.foo # => 'overriden foo'
|
36
|
+
new MyOtherChildClass().options.foo # => 'Foo'
|
37
|
+
new MyChildClass().options.bar # => 'bar'
|
38
|
+
new MyParentClass().options.bar # => undefined
|
39
|
+
```
|
40
|
+
|
41
|
+
Additionnaly, options can be overriden at initialization time :
|
42
|
+
|
43
|
+
```coffee
|
44
|
+
new MyChildClass( bar: 'hello' ).options.bar # => 'hello'
|
45
|
+
```
|
46
|
+
|
47
|
+
If you override the constructor methods, don't forget to accept
|
48
|
+
options parameter and call super with it :
|
49
|
+
|
50
|
+
```coffee
|
51
|
+
class MyClass extends Darwin.Base
|
52
|
+
constructor: ( options ) ->
|
53
|
+
super( options )
|
54
|
+
# do stuff
|
55
|
+
```
|
56
|
+
|
57
|
+
|
58
|
+
# Class events
|
59
|
+
|
60
|
+
With jQuery, you can only bind and fire events on DOM elements.
|
61
|
+
|
62
|
+
Darwin.Base provides a mean to attach events on classes :
|
63
|
+
|
64
|
+
```coffee
|
65
|
+
class MyClass extends Darwin.Base
|
66
|
+
@options {
|
67
|
+
my_name: 'Joe'
|
68
|
+
}
|
69
|
+
|
70
|
+
say_hi: ->
|
71
|
+
@trigger( 'said', "Hi, my name is #{@options.my_name}." )
|
72
|
+
|
73
|
+
my_instance = new MyClass()
|
74
|
+
my_instance.on( 'said', ( message ) -> ( console.log( message ) ) )
|
75
|
+
my_instance.say_hi() # logs : Hi, my name is Joe.
|
76
|
+
```
|
77
|
+
|
78
|
+
This allows to easily encapsulate features in classes and have them
|
79
|
+
to communicate.
|
80
|
+
|
81
|
+
|
82
|
+
# Mixins
|
83
|
+
|
84
|
+
A feature that coffeescript lacks of is the ability to include or
|
85
|
+
extend mixins. Darwin.Base provides that :
|
86
|
+
|
87
|
+
```coffee
|
88
|
+
InstanceMethods =
|
89
|
+
do_something: ->
|
90
|
+
console.log 'I did something.'
|
91
|
+
|
92
|
+
do_nothing: ->
|
93
|
+
console.log 'I swear I did nothing.'
|
94
|
+
|
95
|
+
|
96
|
+
ClassMethods =
|
97
|
+
foo: ->
|
98
|
+
console.log 'foo'
|
99
|
+
|
100
|
+
bar: ->
|
101
|
+
console.log 'bar'
|
102
|
+
|
103
|
+
|
104
|
+
class MyClass extends Darwin.Base
|
105
|
+
@include InstanceMethods
|
106
|
+
@extend ClassMethods
|
107
|
+
|
108
|
+
my_instance = new MyClass()
|
109
|
+
my_instance.do_something() # logs : 'I did something.'
|
110
|
+
MyClass.foo() # logs : 'foo'
|
111
|
+
```
|
112
|
+
|
113
|
+
Just like ruby modules, special methods allow you to
|
114
|
+
operate after module is included or extended :
|
115
|
+
|
116
|
+
```coffee
|
117
|
+
InstanceMethods =
|
118
|
+
included: ->
|
119
|
+
@extend ClassMethods
|
120
|
+
|
121
|
+
do_something: ->
|
122
|
+
console.log 'I did something.'
|
123
|
+
|
124
|
+
do_nothing: ->
|
125
|
+
console.log 'I swear I did nothing.'
|
126
|
+
|
127
|
+
|
128
|
+
ClassMethods =
|
129
|
+
extended: ->
|
130
|
+
console.log 'extended'
|
131
|
+
|
132
|
+
foo: ->
|
133
|
+
console.log 'foo'
|
134
|
+
|
135
|
+
bar: ->
|
136
|
+
console.log 'bar'
|
137
|
+
|
138
|
+
|
139
|
+
class MyClass extends Darwin.Base
|
140
|
+
@include InstanceMethods
|
141
|
+
|
142
|
+
my_instance = new MyClass() # logs : 'extended'
|
143
|
+
my_instance.do_something() # logs : 'I did something.'
|
144
|
+
MyClass.foo() # logs : 'foo'
|
145
|
+
```
|
146
|
+
|
147
|
+
|
data/doc/controller.md
ADDED
@@ -0,0 +1,266 @@
|
|
1
|
+
Darwin.Controller handles everything event or request related.
|
2
|
+
|
3
|
+
This is the main entry point for all feature. A Controller
|
4
|
+
typically initialize a View and bind events to its selectors
|
5
|
+
(though you may use a controller without any View if you need
|
6
|
+
to).
|
7
|
+
|
8
|
+
# Basic usage
|
9
|
+
|
10
|
+
Here is a typical controller example :
|
11
|
+
|
12
|
+
```coffee
|
13
|
+
class App.Controllers.Users.Index extends Darwin.Controller
|
14
|
+
@options {
|
15
|
+
View: App.Views.Users.Index
|
16
|
+
|
17
|
+
events:
|
18
|
+
'Toggle user block': { el: 'user_trigger', type: 'click' }
|
19
|
+
'Delete user': { el: 'user_block', delegate: 'delete_user', type: 'click' }
|
20
|
+
}
|
21
|
+
|
22
|
+
|
23
|
+
user_trigger_clicked: ->
|
24
|
+
if @view.users_shown()
|
25
|
+
@view.hide_users()
|
26
|
+
else
|
27
|
+
@view.show_users()
|
28
|
+
|
29
|
+
|
30
|
+
delete_user_clicked: ( $link ) ->
|
31
|
+
if confirm( 'Really delete user ?' )
|
32
|
+
$.get( $link.attr( 'href' ), =>
|
33
|
+
@view.remove_user( $link )
|
34
|
+
)
|
35
|
+
```
|
36
|
+
|
37
|
+
All you need to add in your html to use it is a data-module attribute :
|
38
|
+
|
39
|
+
```html
|
40
|
+
<div id="users_index" data-module="Users.Index">
|
41
|
+
<-- ... -->
|
42
|
+
</div>
|
43
|
+
```
|
44
|
+
|
45
|
+
`options.View` declares which view you will use. View is automatically
|
46
|
+
initialized at controller initialization.
|
47
|
+
|
48
|
+
`options.events` declares which events are handled by the controller.
|
49
|
+
You should use this rather than binding events manually for x reasons :
|
50
|
+
|
51
|
+
* It offers a central configuration point, so other developers can see
|
52
|
+
at a glance what controller do.
|
53
|
+
* It plays well with view selectors, so that you don't have to change
|
54
|
+
every single file if your DOM structure change.
|
55
|
+
* Most important, callbacks are wrapped so they are disabled if an error
|
56
|
+
occurs on page.
|
57
|
+
|
58
|
+
Event declaration keys are arbitrary strings. This should act as a
|
59
|
+
documentation for your controller. Most of the time, knowning the element
|
60
|
+
on which the callback is bound and the event type is useless to understand
|
61
|
+
what it's supposed to do :
|
62
|
+
|
63
|
+
```coffee
|
64
|
+
{ el: 'user_trigger', type: 'click' }
|
65
|
+
```
|
66
|
+
|
67
|
+
Ok, something happens on user_trigger's click, but what does it do ?
|
68
|
+
|
69
|
+
```coffee
|
70
|
+
'Toggle user block': { el: 'user_trigger', type: 'click' }
|
71
|
+
```
|
72
|
+
|
73
|
+
Now, it's clearer.
|
74
|
+
|
75
|
+
Callback method names are automatically computed after element and
|
76
|
+
event type, so `{ el: 'user_trigger', type: 'click' }` will call the
|
77
|
+
`user_trigger_clicked` method. Every callback is passed the element
|
78
|
+
as first parameter and the event object as second parameter :
|
79
|
+
|
80
|
+
```coffee
|
81
|
+
user_trigger_clicked: ( $trigger, event ) ->
|
82
|
+
```
|
83
|
+
|
84
|
+
Note that if you use delegation, delegated element name will be used
|
85
|
+
instead of element name :
|
86
|
+
|
87
|
+
```coffee
|
88
|
+
class App.Controllers.Users.Index extends Darwin.Controller
|
89
|
+
@options {
|
90
|
+
View: App.Views.Users.Index
|
91
|
+
|
92
|
+
events:
|
93
|
+
'Delete user': { el: 'user_block', delegate: 'delete_user', type: 'click' }
|
94
|
+
}
|
95
|
+
|
96
|
+
|
97
|
+
delete_user_clicked: ( $link ) ->
|
98
|
+
```
|
99
|
+
|
100
|
+
# Event declaration options
|
101
|
+
|
102
|
+
Here are all options allowed in event declaration :
|
103
|
+
|
104
|
+
## el
|
105
|
+
|
106
|
+
A view selector name. Element will be retrieved using `@view.get( <name> )`.
|
107
|
+
|
108
|
+
## type
|
109
|
+
|
110
|
+
The event on which to bind the callback. This can be any jQuery event the
|
111
|
+
element responds to. So, if you have a plugin that trigger a custom `display`
|
112
|
+
event on element, you can use it there.
|
113
|
+
|
114
|
+
## delegate
|
115
|
+
|
116
|
+
Use delegation instead of static binding. The selector name
|
117
|
+
is retrieved from view, just as `el`.
|
118
|
+
|
119
|
+
Delegation not only let bind events on element that are not
|
120
|
+
in the page when callback is bound, but also let bind a single
|
121
|
+
event instead of hundredth. You probably should use it as much
|
122
|
+
as possible.
|
123
|
+
|
124
|
+
```coffee
|
125
|
+
class App.Controllers.Users.Index extends Darwin.Controller
|
126
|
+
@options {
|
127
|
+
View: App.Views.Users.Index
|
128
|
+
|
129
|
+
events:
|
130
|
+
'Delete user': { el: 'user_block', delegate: 'delete_user', type: 'click' }
|
131
|
+
}
|
132
|
+
|
133
|
+
|
134
|
+
delete_user_clicked: ( $link ) ->
|
135
|
+
```
|
136
|
+
|
137
|
+
This binds a single callback to handle as many users you want,
|
138
|
+
and also handle users you've added in the list after controller
|
139
|
+
initialization.
|
140
|
+
|
141
|
+
## controller_method
|
142
|
+
|
143
|
+
Use a custom method name rather than automatically computed one.
|
144
|
+
You may use this, for example, if you want to use the same callback
|
145
|
+
for two events.
|
146
|
+
|
147
|
+
Sometime, automatically computed name will just not do it. For example,
|
148
|
+
Darwin.Controller does not use any special inflection other than
|
149
|
+
adding a 'd' rather than 'ed' for words that end with 'e'. So,
|
150
|
+
here are the computed method names :
|
151
|
+
|
152
|
+
```coffee
|
153
|
+
{ el: 'foo', type: 'click' } # => foo_clicked
|
154
|
+
{ el: 'foo', type: 'change' } # => foo_changed
|
155
|
+
{ el: 'foo', type: 'show' } # => foo_showed
|
156
|
+
```
|
157
|
+
|
158
|
+
For proper english, you can use `controller_method` :
|
159
|
+
|
160
|
+
```coffee
|
161
|
+
'Update page title': { el: 'foo', type: 'show', controller_method: 'foo_shown' }
|
162
|
+
```
|
163
|
+
|
164
|
+
## view_method
|
165
|
+
|
166
|
+
If your callback simply call a view method, you don't need
|
167
|
+
to create a controller method for that :
|
168
|
+
|
169
|
+
```coffee
|
170
|
+
foo_clicked: ->
|
171
|
+
@view.show_something()
|
172
|
+
```
|
173
|
+
|
174
|
+
Instead, you can declare this method in event definition :
|
175
|
+
|
176
|
+
```coffee
|
177
|
+
'Show something': { el: 'foo', type: 'click', view_method: 'show_something' }
|
178
|
+
```
|
179
|
+
|
180
|
+
`controller_method` and `view_method` are mutually exclusive.
|
181
|
+
|
182
|
+
## stop
|
183
|
+
|
184
|
+
By default, any click event calls `event.preventDefault()` and
|
185
|
+
`event.stopPropagation()`, because it's what we need most of the time.
|
186
|
+
|
187
|
+
You may not want that, for example, when you want to ask a confirmation
|
188
|
+
to user. In that case, you can declare `stop: false` :
|
189
|
+
|
190
|
+
```coffee
|
191
|
+
class App.Controllers.Users.Index extends Darwin.Controller
|
192
|
+
@options {
|
193
|
+
View: App.Views.Users.Index
|
194
|
+
|
195
|
+
events:
|
196
|
+
'Delete user': { el: 'delete_user', type: 'click', stop: false }
|
197
|
+
}
|
198
|
+
|
199
|
+
|
200
|
+
delete_user_clicked: ( $link, event ) ->
|
201
|
+
event.preventDefault() unless confirm( 'seriously ?' )
|
202
|
+
```
|
203
|
+
|
204
|
+
Reversively, you can force stopping on other events :
|
205
|
+
|
206
|
+
```coffee
|
207
|
+
'Troll ie users': { el: 'user_checkbox', type: 'change', stop: true }
|
208
|
+
```
|
209
|
+
|
210
|
+
## cancel_delay
|
211
|
+
|
212
|
+
Sometime, you want to wait for a short time on an event before
|
213
|
+
executing its callback, and potentialy cancel it if the same
|
214
|
+
event occurs.
|
215
|
+
|
216
|
+
You will use that typically on an ajax autocomplete search box :
|
217
|
+
when user press a key, you wait for 500ms before firing the request
|
218
|
+
to see if she did not added an other letter. You can use cancel_delay
|
219
|
+
for that :
|
220
|
+
|
221
|
+
```coffee
|
222
|
+
'Autocomplete search': { el: 'search_input', type: 'change', cancel_delay: 500 }
|
223
|
+
```
|
224
|
+
|
225
|
+
cancel_delay value is in milliseconds.
|
226
|
+
|
227
|
+
|
228
|
+
## ensure_element
|
229
|
+
|
230
|
+
To preserve `this` context as being the controller, event binding
|
231
|
+
use event.target to retrieve element on which event occurs. This
|
232
|
+
has implications. If you have :
|
233
|
+
|
234
|
+
```html
|
235
|
+
<a href="/do_stuff"><img src="my_image.jpg"></a>
|
236
|
+
```
|
237
|
+
|
238
|
+
and you bind event on the link, `event.target` may be the image
|
239
|
+
element if you clicked on it. Darwin is aware of that on will
|
240
|
+
always retrieve the element you asked when passing it to callback.
|
241
|
+
|
242
|
+
But sometime, you may just want to have that target element.
|
243
|
+
You can achieve this using `ensure_element` :
|
244
|
+
|
245
|
+
```coffee
|
246
|
+
'Remove clicked element': { el: 'my_block', type: 'click', ensure_element: false }
|
247
|
+
```
|
248
|
+
|
249
|
+
You probably should use delegation to filter what you want
|
250
|
+
more precisely, though.
|
251
|
+
|
252
|
+
|
253
|
+
# Progressive enhancement and graceful degradation
|
254
|
+
|
255
|
+
Like Darwin.View, Darwin.Controller has a `run()` and
|
256
|
+
a `destructor()` method.
|
257
|
+
|
258
|
+
You may, for example, use `run()` to instantiate plugins.
|
259
|
+
Put their teardown calls in `destructor()`.
|
260
|
+
|
261
|
+
If you override `destructor()`, don't forget to call `super`,
|
262
|
+
as controller destruction already handle a lot of stuff under
|
263
|
+
the hood, like calling its view destructor, unbinding events
|
264
|
+
and removing references to controller.
|
265
|
+
|
266
|
+
|
data/doc/introduction.md
ADDED
@@ -0,0 +1,228 @@
|
|
1
|
+
Darwin is a javascript framework for people that take error
|
2
|
+
handling seriously and want to achieve it through progressive
|
3
|
+
enhancement and graceful degradation.
|
4
|
+
|
5
|
+
Darwin will also let developer write clean and encapsulated
|
6
|
+
code that encourages self documentation.
|
7
|
+
|
8
|
+
|
9
|
+
# Module
|
10
|
+
|
11
|
+
At the core of darwin is the concept of module : a module
|
12
|
+
is a specific feature, bound to a specific block of your
|
13
|
+
page. This block will be the root of your feature and will
|
14
|
+
act as a sandbox. Of course, you may decide to have the whole
|
15
|
+
page content except layout as root, or you can choose to
|
16
|
+
have a lot of modules which will speak to each other via
|
17
|
+
events and callbacks. You can even have modules inside
|
18
|
+
modules : that's up to you, darwin will support you anyway.
|
19
|
+
|
20
|
+
A module is made of a controller and an optional (but almost
|
21
|
+
always present) view. Controllers are declared on
|
22
|
+
`App.Controllers` and views are declared on `App.Views`. You
|
23
|
+
can (and are expected to) create namespaces within those.
|
24
|
+
|
25
|
+
The suggested file structure is this one :
|
26
|
+
|
27
|
+
```
|
28
|
+
app/assets/javascripts/
|
29
|
+
controllers/
|
30
|
+
users.coffee # declare namespace : App.Controllers.Users = {}
|
31
|
+
users/
|
32
|
+
index.coffee # Controller for index action
|
33
|
+
show.cofee # Controller for show action
|
34
|
+
followers.coffee # Controller for the "followers" block
|
35
|
+
views/
|
36
|
+
users.coffee # declare namespace : App.Views.Users = {}
|
37
|
+
users/
|
38
|
+
index.coffee # View for index action
|
39
|
+
show.cofee # View for show action
|
40
|
+
followers.coffee # View for the "followers" block
|
41
|
+
```
|
42
|
+
|
43
|
+
Your application.coffee file should `require_tree` views/ before
|
44
|
+
controllers/, as controllers depend on views.
|
45
|
+
|
46
|
+
You can easily create a module using the generator provided :
|
47
|
+
|
48
|
+
```
|
49
|
+
$ rails generate darwin admin_area/users/index
|
50
|
+
|
51
|
+
create app/assets/javascripts/controllers/admin_area.coffee
|
52
|
+
create app/assets/javascripts/views/admin_area.coffee
|
53
|
+
create app/assets/javascripts/controllers/admin_area/users.coffee
|
54
|
+
create app/assets/javascripts/views/admin_area/users.coffee
|
55
|
+
create app/assets/javascripts/controllers/admin_area/users/index.coffee
|
56
|
+
create app/assets/javascripts/views/admin_area/users/index.coffee
|
57
|
+
```
|
58
|
+
|
59
|
+
You can also create modules for an whole CRUD resource using camelcase
|
60
|
+
form :
|
61
|
+
|
62
|
+
```
|
63
|
+
$ rails g darwinjs:assets AdminArea::Contact
|
64
|
+
create app/assets/javascripts/controllers/admin_area.coffee
|
65
|
+
create app/assets/javascripts/views/admin_area.coffee
|
66
|
+
create app/assets/javascripts/controllers/admin_area/contacts.coffee
|
67
|
+
create app/assets/javascripts/views/admin_area/contacts.coffee
|
68
|
+
create app/assets/javascripts/controllers/admin_area/contacts/index.coffee
|
69
|
+
create app/assets/javascripts/views/admin_area/contacts/index.coffee
|
70
|
+
create app/assets/javascripts/controllers/admin_area/contacts/edit.coffee
|
71
|
+
create app/assets/javascripts/views/admin_area/contacts/edit.coffee
|
72
|
+
create app/assets/javascripts/controllers/admin_area/contacts/show.coffee
|
73
|
+
create app/assets/javascripts/views/admin_area/contacts/show.coffee
|
74
|
+
create app/assets/javascripts/controllers/admin_area/contacts/new.coffee
|
75
|
+
create app/assets/javascripts/views/admin_area/contacts/new.coffee
|
76
|
+
create app/assets/javascripts/controllers/admin_area/contacts/form.coffee
|
77
|
+
create app/assets/javascripts/views/admin_area/contacts/form.coffee
|
78
|
+
```
|
79
|
+
|
80
|
+
Module can be autoloaded, using the `data-module` attribute :
|
81
|
+
|
82
|
+
```
|
83
|
+
<div id="users" data-module="Users.Index">
|
84
|
+
...
|
85
|
+
</div>
|
86
|
+
```
|
87
|
+
|
88
|
+
This will instantiate `App.Controllers.Users.Index` (which in turn
|
89
|
+
instantiates its view).
|
90
|
+
|
91
|
+
To learn more about autoloader, see [Loader](loader.md).
|
92
|
+
|
93
|
+
|
94
|
+
# Controller
|
95
|
+
|
96
|
+
A controller will handle anything event or request specific.
|
97
|
+
An option set let you define all your events so you can see
|
98
|
+
at one glance what the controller is about. See the
|
99
|
+
options.events block here :
|
100
|
+
|
101
|
+
```coffee
|
102
|
+
class App.Controllers.Users.Index extends Darwin.Controller
|
103
|
+
@options {
|
104
|
+
View: App.Views.Users.Index
|
105
|
+
|
106
|
+
events:
|
107
|
+
'Toggle user block': { el: 'user_trigger', type: 'click' }
|
108
|
+
'Show user info': { el: 'user_more_trigger', type: 'click' }
|
109
|
+
'Delete user on server': { el: 'user_delete', type: 'click' }
|
110
|
+
}
|
111
|
+
|
112
|
+
|
113
|
+
user_trigger_clicked: ->
|
114
|
+
if @view.users_shown()
|
115
|
+
@view.hide_users()
|
116
|
+
else
|
117
|
+
@view.show_users()
|
118
|
+
|
119
|
+
|
120
|
+
user_more_trigger_clicked: ( $link ) ->
|
121
|
+
@view.show_info_for( $link )
|
122
|
+
|
123
|
+
|
124
|
+
user_delete_clicked: ( $link ) ->
|
125
|
+
if confirm( 'Really delete user ?' )
|
126
|
+
$.get( $link.attr( 'href' ), =>
|
127
|
+
@view.remove_user( $link )
|
128
|
+
)
|
129
|
+
```
|
130
|
+
|
131
|
+
Such self documentation will make sure any developer coming after
|
132
|
+
you will understand immediately what the controller is about.
|
133
|
+
|
134
|
+
As you probably noticed, callback names are automatically computed
|
135
|
+
to match your event declaration, so `{ el: user_trigger, type: click}`
|
136
|
+
call a `user_trigger_clicked` callback. Two parameters are passed
|
137
|
+
to your callback : the jquery extended element and the event object.
|
138
|
+
|
139
|
+
As most of the time, you want to stop an event after a click, this
|
140
|
+
also is the default. So :
|
141
|
+
|
142
|
+
```coffee
|
143
|
+
class App.Controllers.Users.Index extends Darwin.Controller
|
144
|
+
@options {
|
145
|
+
View: App.Views.Users.Index
|
146
|
+
|
147
|
+
events:
|
148
|
+
'Toggle user block': { el: 'user_trigger', type: 'click' }
|
149
|
+
}
|
150
|
+
|
151
|
+
user_trigger_clicked: ( $link )->
|
152
|
+
@view.show_user( $link )
|
153
|
+
```
|
154
|
+
|
155
|
+
Is equivalent to, without darwin :
|
156
|
+
|
157
|
+
```
|
158
|
+
$('.user_trigger' ).click( ( event ) ->
|
159
|
+
$link = $(this)
|
160
|
+
event.preventDefault()
|
161
|
+
show_user( $link )
|
162
|
+
)
|
163
|
+
```
|
164
|
+
|
165
|
+
Read about [Controller](controller.md) to learn more.
|
166
|
+
|
167
|
+
# View
|
168
|
+
|
169
|
+
A view handle everything DOM related. Its main purpose is
|
170
|
+
to act as central point of configuration for selectors and
|
171
|
+
to do DOM manipulation :
|
172
|
+
|
173
|
+
```coffee
|
174
|
+
class App.Controllers.Users.Index extends Darwin.Controller
|
175
|
+
@options {
|
176
|
+
selectors:
|
177
|
+
close: 'a.close'
|
178
|
+
show_more: '.content .more a'
|
179
|
+
users:
|
180
|
+
'sel': '#users'
|
181
|
+
delete: 'a[data-method="delete"]'
|
182
|
+
}
|
183
|
+
```
|
184
|
+
|
185
|
+
Now, everywhere in your view methods, you can use `@get( 'close' )`
|
186
|
+
to retrieve your close element. In controllers, you can use
|
187
|
+
`@view.get( 'close' )` as well. This has two benefits :
|
188
|
+
|
189
|
+
1. You have a single point of configuration. If you decide to
|
190
|
+
change your DOM, you only have to change configuration here and
|
191
|
+
it is reflected on your whole module.
|
192
|
+
2. All elements are cached by default. Don't worry anymore about
|
193
|
+
DOM hitting performances.
|
194
|
+
|
195
|
+
A view is also responsible for setup / teardown your DOM, via
|
196
|
+
its `run()` and `destructor()` method. `run()` is called when
|
197
|
+
module is initialized ; `destructor()` is called when an error
|
198
|
+
occurs (or you manually destruct the module).
|
199
|
+
|
200
|
+
```coffee
|
201
|
+
class App.Controllers.Users.Index extends Darwin.Controller
|
202
|
+
@options {
|
203
|
+
selectors:
|
204
|
+
submits: 'input[type="submit"]'
|
205
|
+
}
|
206
|
+
|
207
|
+
run: ->
|
208
|
+
@get( 'submits' ).hide()
|
209
|
+
|
210
|
+
|
211
|
+
destructor: ->
|
212
|
+
@get( 'submits' ).show()
|
213
|
+
```
|
214
|
+
|
215
|
+
So, `run()` is for progressive enhancement, and `destructor()` is
|
216
|
+
for graceful degradation.
|
217
|
+
|
218
|
+
This will ensure your featureis still # usable even if a
|
219
|
+
javascript error occurs.
|
220
|
+
|
221
|
+
To learn more about views, see [View](view.md).
|
222
|
+
|
223
|
+
|
224
|
+
Finaly, Darwin add some sugar to coffeescript (namely: options pattern,
|
225
|
+
mixins and class events). See [Base](base.md), darwin's base class to
|
226
|
+
learn more.
|
227
|
+
|
228
|
+
|