luca 0.9.2 → 0.9.4
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/.rvmrc +1 -1
- data/CHANGELOG +46 -2
- data/Gemfile +1 -1
- data/Gemfile.lock +2 -2
- data/Guardfile +1 -1
- data/README.md +64 -27
- data/ROADMAP +17 -2
- data/Rakefile +49 -1
- data/app.rb +38 -2
- data/assets/javascripts/luca-ui-base.coffee +1 -20
- data/assets/javascripts/luca-ui-full.js +3 -0
- data/assets/javascripts/luca-ui.coffee +0 -5
- data/assets/javascripts/sandbox/application.coffee +24 -18
- data/assets/javascripts/sandbox/router.coffee +16 -6
- data/assets/javascripts/sandbox/templates/builder/component_list.luca +1 -0
- data/assets/javascripts/sandbox/templates/builder.luca +2 -0
- data/assets/javascripts/sandbox/templates/main.luca +4 -3
- data/assets/javascripts/sandbox/templates/sandbox/docs_index.luca +1 -0
- data/assets/javascripts/sandbox/templates/sandbox/navigation.luca +6 -1
- data/assets/javascripts/sandbox/templates/sandbox/readme.luca +30 -0
- data/assets/javascripts/sandbox/views/builder/builder_canvas.coffee +3 -0
- data/assets/javascripts/sandbox/views/builder/builder_editor.coffee +6 -0
- data/assets/javascripts/sandbox/views/builder/component_list.coffee +38 -0
- data/assets/javascripts/sandbox/views/builder/project_browser.coffee +14 -0
- data/assets/javascripts/sandbox/views/builder.coffee +133 -0
- data/assets/javascripts/sandbox/views/docs_controller.coffee +7 -0
- data/assets/javascripts/sandbox/views/inspector/instance_filter.coffee +18 -0
- data/assets/javascripts/sandbox/{collections/sample.coffee → views/inspector/instance_list.coffee} +0 -0
- data/assets/javascripts/sandbox/views/inspector.coffee +11 -0
- data/assets/javascripts/sandbox.coffee +2 -0
- data/assets/stylesheets/luca-ui-full.css +3 -0
- data/assets/stylesheets/sandbox/builder.scss +79 -0
- data/assets/stylesheets/sandbox/sandbox.scss +2 -1
- data/docs/application.md +41 -0
- data/docs/collection.md +79 -0
- data/docs/collection_manager.md +76 -0
- data/docs/container_philosophy.md +122 -0
- data/docs/event_binding_helpers.md +164 -0
- data/docs/method_caching_and_computed_properties.md +77 -0
- data/docs/view.md +119 -0
- data/lib/luca/rails/version.rb +1 -1
- data/lib/luca/template.rb +9 -9
- data/site/assets/bootstrap.min.js +7 -0
- data/site/assets/luca-ui-bootstrap.css +19 -1
- data/site/assets/luca-ui-development-tools.css +10 -0
- data/site/assets/luca-ui-development-tools.min.js +15 -0
- data/site/assets/luca-ui-full.min.js +8 -0
- data/site/assets/luca-ui.min.js +4 -0
- data/site/assets/sandbox.css +52 -4
- data/site/assets/sandbox.js +368 -30
- data/site/docs/application.html +41 -0
- data/site/docs/caching.html +43 -0
- data/site/docs/collection.html +75 -0
- data/site/docs/collection_manager.html +71 -0
- data/site/docs/containers.html +118 -0
- data/site/docs/events.html +153 -0
- data/site/docs/view.html +128 -0
- data/site/img/glyphicons-halflings-white.png +0 -0
- data/site/img/glyphicons-halflings.png +0 -0
- data/site/source-map.js +1 -0
- data/spec/core/view_spec.coffee +5 -17
- data/spec/managers/collection_manager_spec.coffee +4 -7
- data/src/components/application.coffee +202 -77
- data/src/components/base_toolbar.coffee +1 -1
- data/src/components/collection_view.coffee +38 -10
- data/src/components/controller.coffee +24 -1
- data/src/components/fields/checkbox_field.coffee +9 -12
- data/src/components/fields/label_field.coffee +14 -0
- data/src/components/fields/select_field.coffee +2 -2
- data/src/components/fields/text_field.coffee +12 -7
- data/src/components/fields/type_ahead_field.coffee +1 -0
- data/src/components/form_view.coffee +44 -25
- data/src/components/page_controller.coffee +2 -0
- data/src/containers/card_view.coffee +4 -1
- data/src/containers/column_view.coffee +2 -1
- data/src/containers/modal_view.coffee +6 -2
- data/src/containers/page_view.coffee +2 -0
- data/src/containers/panel_toolbar.coffee +0 -5
- data/src/containers/viewport.coffee +28 -10
- data/src/core/collection.coffee +7 -1
- data/src/core/container.coffee +57 -30
- data/src/core/core.coffee +0 -186
- data/src/core/field.coffee +11 -3
- data/src/core/model.coffee +31 -16
- data/src/core/panel.coffee +6 -46
- data/src/core/registry.coffee +19 -2
- data/src/core/script_loader.coffee +32 -0
- data/src/core/view.coffee +112 -139
- data/src/define.coffee +110 -0
- data/src/framework.coffee +8 -2
- data/src/luca.coffee +22 -0
- data/src/managers/collection_manager.coffee +65 -31
- data/src/modules/load_mask.coffee +47 -0
- data/src/plugins/development_tool_helpers.coffee +21 -0
- data/src/plugins/events.coffee +54 -0
- data/src/stylesheets/components/viewport.scss +15 -0
- data/src/stylesheets/containers/container.scss +1 -4
- data/src/stylesheets/tools/component_tester.scss +18 -0
- data/src/templates/fields/select_field.luca +6 -5
- data/src/templates/fields/text_field.luca +10 -9
- data/src/tools/application_inspector.coffee +2 -0
- data/src/tools/coffee_script_editor.coffee +28 -6
- data/src/tools/collections/components.coffee +59 -0
- data/src/tools/collections/instances.coffee +15 -0
- data/src/tools/component_tester.coffee +12 -22
- data/src/tools/console.coffee +22 -4
- data/src/tools/models/components.coffee +16 -54
- data/src/tools/models/instance.coffee +2 -0
- data/src/{core/util.coffee → util.coffee} +10 -1
- data/vendor/assets/javascripts/luca-ui-base.js +132 -137
- data/vendor/assets/javascripts/luca-ui-development-tools.js +191 -219
- data/vendor/assets/javascripts/luca-ui-development-tools.min.js +2 -2
- data/vendor/assets/javascripts/luca-ui-full.js +4680 -0
- data/vendor/assets/javascripts/luca-ui-full.min.js +8 -0
- data/vendor/assets/javascripts/luca-ui-spec.js +291 -225
- data/vendor/assets/javascripts/luca-ui.js +1001 -724
- data/vendor/assets/javascripts/luca-ui.min.js +4 -4
- data/vendor/assets/stylesheets/luca-ui-bootstrap.css +19 -1
- data/vendor/assets/stylesheets/luca-ui-development-tools.css +10 -0
- data/vendor/assets/stylesheets/luca-ui-full.css +1334 -0
- data/vendor/assets/stylesheets/luca-ui-spec.css +19 -1
- data/vendor/assets/stylesheets/luca-ui.css +19 -1
- data/views/index.erb +2 -5
- metadata +58 -9
- data/lib/sprockets/luca_template.rb +0 -49
- data/src/tools/class_browser.coffee +0 -39
- data/src/tools/components/class_browser_detail.coffee +0 -10
- data/src/tools/components/class_browser_list.coffee +0 -74
@@ -0,0 +1,71 @@
|
|
1
|
+
<h1>The Luca Collection Manager</h1>
|
2
|
+
<p>The CollectionManager is a single instance which acts as a gateway to
|
3
|
+
the instances of Luca.Collection created in your app. The intention is
|
4
|
+
to provide a central place for creating one, and only one instance of a
|
5
|
+
given collection type.
|
6
|
+
|
7
|
+
</p>
|
8
|
+
<p>You can use CollectionManager independently, or you will get one by default
|
9
|
+
when you use a Luca.Application with the default configuration.
|
10
|
+
|
11
|
+
</p>
|
12
|
+
<p>A CollectionManager has a name property which is 'primary' by default. If
|
13
|
+
you call <code>Luca.CollectionManager.get()</code> it will return the CollectionManager
|
14
|
+
named 'primary' or the first one ever created. Attempting to create an additional
|
15
|
+
CollectionManager instance with a name that is already used, will throw an error.
|
16
|
+
|
17
|
+
</p>
|
18
|
+
<h2>Named Collections and Auto-Registering</h2>
|
19
|
+
<p>You can configure your Luca.Collection classes to have their instances automatically
|
20
|
+
register with the collection manager. By specifying a <code>@name</code> property on your collection prototypes, they will automatically attempt to register with the running collection manager instance ( via <code>Luca.CollectionManager.get()</code> ) as soon as they are initialized.
|
21
|
+
|
22
|
+
</p>
|
23
|
+
<p>You can specify which manager you want a collection to register with by specifying a <code>@manager</code> property on your collection. This can either be a string, which will get resolved when needed to a variable, or a direct reference to the collection manager. The string is useful since, when declaring your Luca.Collection prototypes, the collection manager will most likely not be instantiated.
|
24
|
+
|
25
|
+
</p>
|
26
|
+
<pre><code class="lang-coffeescript"> _.def("MyCollection").extends("Luca.Collection").with
|
27
|
+
name: "my_collection"
|
28
|
+
manager: "AppInstance.collectionManager"</code></pre>
|
29
|
+
<h2>Private Collections</h2>
|
30
|
+
<p>You may not always want to use the global, single authoritative instance of a collection. In this case, you can specify a <code>@private</code> or <code>@anonymous</code> property on your collection, and it will skip registering with the collection manager.
|
31
|
+
|
32
|
+
</p>
|
33
|
+
<h2>Collection Class Naming</h2>
|
34
|
+
<p>Your custom Luca.Collection classes get named like MyApp.collections.SampleCollection. Through some string magic "SampleCollection" will get turned into "sample_collection". If you try to call collectionManager.getOrCreate("sample_collection") it will attempt to get a collection named "sample_collection", and if it fails, will create a new instance of MyApp.collections.SampleCollection. If you want to force your CollectionManager to look in a specific namespace, set a reference to MyApp.collections on Luca.Collection.namespace, otherwise it will look in all of the namespaces it knows about in the Luca.registry and find an appropriate collection.
|
35
|
+
|
36
|
+
</p>
|
37
|
+
<h2>Initial Collections</h2>
|
38
|
+
<p>The CollectionManager can be configured with an @initialCollections property, which is an array of names of collection classes, similar to "sample_collection", or actual references to Collection Classes, or strings with their names. The CollectionManager will create instances of the collection for you, and call fetch() on all of them.
|
39
|
+
|
40
|
+
</p>
|
41
|
+
<pre><code class="lang-coffeescript"> _.def("App.collections.SampleCollection").extends("Luca.Collection").with
|
42
|
+
name: "sample_collection"
|
43
|
+
|
44
|
+
_.def("App.collections.ExampleCollection").extends("Luca.Collection").with
|
45
|
+
name: "example_collection"
|
46
|
+
|
47
|
+
class App.CollectionManager extends Luca.CollectionManager
|
48
|
+
initialCollections:[
|
49
|
+
"sample_collection"
|
50
|
+
"example_collection"
|
51
|
+
]
|
52
|
+
|
53
|
+
# this will create instances of both of the above collections
|
54
|
+
# and call fetch() on all of them
|
55
|
+
collectionManager = new App.CollectionManager()</code></pre>
|
56
|
+
<h2>Event Relaying</h2>
|
57
|
+
<p>By default <code>@relayEvents</code> is set to true on the CollectionManager. This means that
|
58
|
+
any event that is triggered by a collection that is managed by the collection manager will be bubbled up to the manager. This feature is used by the collectionEvents configuration API used by Luca.View, but can also be used in custom situations as well. Simply bind to the CollectionManager instance.
|
59
|
+
|
60
|
+
</p>
|
61
|
+
<p>Event triggers will look like <code>collection_name event</code>:
|
62
|
+
|
63
|
+
|
64
|
+
</p>
|
65
|
+
<pre><code class="lang-coffeescript"> collection = new App.collections.SampleCollection([],name:"sample_collection")
|
66
|
+
manager = new Luca.CollectionManager(collectionNamespace:App.collections)
|
67
|
+
|
68
|
+
manager.on "sample_collection reset", ()=> @doSomething()
|
69
|
+
|
70
|
+
# will trigger 'reset' and call doSomething()
|
71
|
+
collection.fetch()</code></pre>
|
@@ -0,0 +1,118 @@
|
|
1
|
+
<h1>Container Views in Luca.js</h1>
|
2
|
+
<p>Containers are types of views which are made up of one or more components. A component
|
3
|
+
is simply another Backbone.View, Luca.View, or one of their descendants.
|
4
|
+
|
5
|
+
</p>
|
6
|
+
<p>The purpose of a Container is to faciliate the communication between the components.
|
7
|
+
|
8
|
+
</p>
|
9
|
+
<p>The classic example is a FormView. A FormView is a component which inherits from
|
10
|
+
Luca.core.Container and is made up of many Field components, and facilitates the communication between
|
11
|
+
the fields and a Backbone.Model. The internal implementation of the model and field classes should never know
|
12
|
+
or reference any other component. This is the job of the FormView.
|
13
|
+
|
14
|
+
</p>
|
15
|
+
<h2>Containers are meant to generate your structural DOM elements</h2>
|
16
|
+
<p>Containers generate the structural DOM elements which wrap the individual components, and the container renders these components to the DOM element
|
17
|
+
that is assigned to it. The various types of containers you use will each
|
18
|
+
have their own internal logic for the way these DOM elements are laid out, displayed, hidden, showed, etc.
|
19
|
+
|
20
|
+
</p>
|
21
|
+
<p>For example, a ColumnView will show two components side by side and assign
|
22
|
+
each one to its own DIV element and use css to lay those columns out as configured.
|
23
|
+
A CardView will assign each component to a DIV element, show the active card, and hide the rest.
|
24
|
+
|
25
|
+
</p>
|
26
|
+
<h2>Layout and Rendering Customization</h2>
|
27
|
+
<p>The call to <code>render()</code> on a container will start a rendering chain on all of the nested components. You can customize this to your hearts content by tapping into
|
28
|
+
the method chain.
|
29
|
+
|
30
|
+
</p>
|
31
|
+
<p>All render() methods on Luca.View are wrapped and will trigger <code>before:render</code> and <code>after:render</code> events, as well as call any beforeRender or afterRender methods defined on your component. For more about this, see the section about hooks on Luca.View.
|
32
|
+
|
33
|
+
</p>
|
34
|
+
<p>The chain started by a call to <code>container.render()</code> is as follows:
|
35
|
+
|
36
|
+
</p>
|
37
|
+
<pre><code class="lang-coffeescript"> beforeRender()
|
38
|
+
|
39
|
+
# layout functions
|
40
|
+
@trigger "before:layout" # => or run beforeLayout() if it exists
|
41
|
+
@prepareLayout()
|
42
|
+
@trigger "after:layout" # => or run afterLayout() if it exists</code></pre>
|
43
|
+
<p>prepareLayout is an internal method on Luca.core.Container which will iterate
|
44
|
+
over each of your components and call applyDOMconfig passing your components
|
45
|
+
configuration to this function. This will create a DOM element and apply
|
46
|
+
any configured inline style declarations, assign a DOM id, css class, as well
|
47
|
+
as some data attributes to the element.
|
48
|
+
|
49
|
+
</p>
|
50
|
+
<p>It will put each DOM container elemement in a @componentContainers property on
|
51
|
+
your container object.
|
52
|
+
|
53
|
+
</p>
|
54
|
+
<p>After prepareLayout is the components cycle:
|
55
|
+
|
56
|
+
</p>
|
57
|
+
<pre><code class="lang-coffeescript"> @trigger "before:components" # => or run beforeComponents() if it exists
|
58
|
+
@prepareComponents()
|
59
|
+
@createComponents()
|
60
|
+
@trigger "before:render:components"
|
61
|
+
@renderComponents()
|
62
|
+
@trigger "after:components" # => or run afterComponents() if it exists</code></pre>
|
63
|
+
<h2>A Note on Container inheritance</h2>
|
64
|
+
<p>If you end up customizing the methods above in the render chain, you may
|
65
|
+
want to call the same method on the component you are inheriting from. Luca
|
66
|
+
provides some syntactic sugar for this:
|
67
|
+
|
68
|
+
</p>
|
69
|
+
<pre><code class="lang-coffeescript"> _.def("MyContainer").extends("Luca.core.Container").with
|
70
|
+
prepareLayout: ()->
|
71
|
+
# This is the normal way you would do this
|
72
|
+
Luca.core.Container::prepareLayout.apply(@, arguments)
|
73
|
+
|
74
|
+
# This is the sugary version which you get if you
|
75
|
+
# use the _.def or Luca.define method for declaring
|
76
|
+
# your prototype definitions
|
77
|
+
@_super("prepareLayout", @, arguments)
|
78
|
+
|
79
|
+
@applyMyOwnLayoutCustomizations()</code></pre>
|
80
|
+
<h2>The <code>ctype</code> property</h2>
|
81
|
+
<p>Every Luca.View which gets registered through the Luca.registry will have a ctype value associated with it. The
|
82
|
+
ctype property is used when adding components to a Container.
|
83
|
+
|
84
|
+
</p>
|
85
|
+
<pre><code class="lang-coffeescript"> _.def("ComponentOne").extends("Luca.View").with()
|
86
|
+
|
87
|
+
_.def("ComponentTwo").extends("Luca.View").with()
|
88
|
+
|
89
|
+
_.def("ContainerOne").extends("Luca.core.Container").with
|
90
|
+
components:[
|
91
|
+
ctype: "component_one"
|
92
|
+
overriddenValue: "customValue"
|
93
|
+
,
|
94
|
+
ctype: "component_two"
|
95
|
+
thisGetsPassedToInitialize: "yep"
|
96
|
+
]</code></pre>
|
97
|
+
<p>In the above example, a View class of ContainerOne will be available, and
|
98
|
+
any time you create an instance of it and call render on it, it will create
|
99
|
+
instances of ComponentOne and ComponentTwo.
|
100
|
+
|
101
|
+
</p>
|
102
|
+
<p>Note, if you do not need to customize any of the properties on the component
|
103
|
+
views, you can just pass an array of ctype strings.
|
104
|
+
|
105
|
+
</p>
|
106
|
+
<pre><code class="lang-coffeescript"> _.def("ContainerTwo").extends("Luca.core.Container").with
|
107
|
+
components:["component_one","component_two"]</code></pre>
|
108
|
+
<h2>Convenience Methods on the Container</h2>
|
109
|
+
<p>You have access to several methods which work on the components which belong to your views. These methods are:
|
110
|
+
|
111
|
+
</p>
|
112
|
+
<ul>
|
113
|
+
<li>pluck : plucks an attribute for each component</li>
|
114
|
+
<li>invoke: invokes a method for each component</li>
|
115
|
+
<li>each: run the passed iterator on eachComponent, recursively. You can turn off the recursion by passing false as your second argument.</li>
|
116
|
+
<li>indexOf: get the index of a component by it's name property</li>
|
117
|
+
<li>selectByAttribute: selects all components whose attribute matches a given value</li>
|
118
|
+
</ul>
|
@@ -0,0 +1,153 @@
|
|
1
|
+
|
2
|
+
<h2>Event Binding Syntactic Sugar</h2>
|
3
|
+
<p><code>Luca.Events</code> provides you with some additional event binding sugar.
|
4
|
+
|
5
|
+
</p>
|
6
|
+
<p><strong>once</strong>
|
7
|
+
|
8
|
+
</p>
|
9
|
+
<p><code>once</code> is how you would run one function in response to an event, but only once.
|
10
|
+
|
11
|
+
</p>
|
12
|
+
<pre><code class="lang-coffeescript"> view = new Luca.View()
|
13
|
+
|
14
|
+
view.once "event:gets:triggered", ()->
|
15
|
+
alert('sup baby')
|
16
|
+
|
17
|
+
view.trigger("event:gets:triggered")</code></pre>
|
18
|
+
<p><strong>defer until</strong>
|
19
|
+
|
20
|
+
</p>
|
21
|
+
<p><code>defer</code> is similar to `once', but with syntax I like a little better:
|
22
|
+
|
23
|
+
</p>
|
24
|
+
<pre><code class="lang-coffeescript"> _.def("MyView").extends("Luca.View").with
|
25
|
+
initialize: ()->
|
26
|
+
@defer(@setup).until("event:gets:triggered")</code></pre>
|
27
|
+
<p>If you want to defer a callback until an event gets triggered on some other object:
|
28
|
+
|
29
|
+
</p>
|
30
|
+
<pre><code class="lang-coffeescript">
|
31
|
+
_.def("MyView").extends("Luca.View").with
|
32
|
+
|
33
|
+
initialize:()->
|
34
|
+
@defer(@setup).until(@someObject,"triggers:an:event")
|
35
|
+
|
36
|
+
setup: ()-></code></pre>
|
37
|
+
<h2>Component Bindings</h2>
|
38
|
+
<p>Luca provides a number of configuration API for its components
|
39
|
+
which facilitate the binding of a component's methods to events that
|
40
|
+
occur on instances of the CollectionManager and Application objects.
|
41
|
+
|
42
|
+
</p>
|
43
|
+
<p>For Luca.core.Container classes there is also a component events
|
44
|
+
binding API that allows you to declare in your container which events
|
45
|
+
to listen for on that container's components
|
46
|
+
|
47
|
+
</p>
|
48
|
+
<h2>Auto Context Binding For Event Handlers</h2>
|
49
|
+
<p>By setting the @bindAllEvents property to true on your prototype definitions,
|
50
|
+
all event handler methods on your view will automatically be bound to the context
|
51
|
+
of the view.
|
52
|
+
|
53
|
+
</p>
|
54
|
+
<pre><code class="lang-coffeescript"> _.def('MyApp.views.AutoBoundView').extends('Luca.View').with
|
55
|
+
|
56
|
+
bindAllEvents: true
|
57
|
+
|
58
|
+
events:
|
59
|
+
"click a.btn" : "clickHandler"
|
60
|
+
"click a.btn.btn-danger" : "dangerHandler"
|
61
|
+
|
62
|
+
initialize:()->
|
63
|
+
# You no longer need to do this
|
64
|
+
# if you want to have these handlers run
|
65
|
+
# in the context of this view
|
66
|
+
_.bindAll @, "clickHandler", "dangerHandler"</code></pre>
|
67
|
+
<h2>Collection Manager Event Binding</h2>
|
68
|
+
<p>Luca Applications which use the Luca.CollectionManager have the benefit of
|
69
|
+
a declarative event binding syntax which allows you bind to events on collections
|
70
|
+
by their name. This saves you from having to create a reference to the collection
|
71
|
+
in some method, and setup a callback binding. By simply providing a @collectionEvents
|
72
|
+
configuration property on your views, you can eliminate a lot of boilerplate in your components.
|
73
|
+
|
74
|
+
</p>
|
75
|
+
<p>The format of the @collectionEvents hash is a key which is made up of the collection's name and the event
|
76
|
+
separated by a space, and either a function or a name of a method on your view.
|
77
|
+
|
78
|
+
</p>
|
79
|
+
<pre><code class="lang-coffeescript"> SamplesCollection = Backbone.Collection.extend
|
80
|
+
name: "samples"
|
81
|
+
url: "/api/v1/samples"
|
82
|
+
|
83
|
+
_.def("MyView").extends("Luca.View").with
|
84
|
+
# NOTE: you may omit this property and
|
85
|
+
# it will use Luca.CollectionManager.get() to
|
86
|
+
# get the main instance.
|
87
|
+
collectionManager: "main"
|
88
|
+
|
89
|
+
collectionEvents:
|
90
|
+
"samples reset" : "samplesResetHandler"
|
91
|
+
|
92
|
+
samplesResetHandler: (collection)->
|
93
|
+
if collection.length > 1
|
94
|
+
@doSomething()</code></pre>
|
95
|
+
<h2>Application Event Binding</h2>
|
96
|
+
<p>Similar to the CollectionManager event binding API, there is a similar API for binding to the global application
|
97
|
+
object. Most applications will have a single application instance, that is either available on the global object
|
98
|
+
or through a call to <code>Luca.getApplication()</code>.
|
99
|
+
|
100
|
+
</p>
|
101
|
+
<p>It is in the Application instance that global state tracking should occur. Should your views want to respond to changes
|
102
|
+
in global application state, you can provide an @applicationEvents configuration property. The format is a key value
|
103
|
+
pair, where the key represents the event being triggered by the application, and the value is a name of a method on
|
104
|
+
your view or an anonymous function.
|
105
|
+
|
106
|
+
</p>
|
107
|
+
<pre><code class="lang-coffeescript"> App = new Luca.Application
|
108
|
+
name: "main"
|
109
|
+
|
110
|
+
defaultState:
|
111
|
+
currentMode: "solid"
|
112
|
+
|
113
|
+
_.def("AppBoundView").extends("Luca.View").with
|
114
|
+
# NOTE: you may omit this and it will use Luca.getApplication()
|
115
|
+
app: "main"
|
116
|
+
|
117
|
+
applicationEvents:
|
118
|
+
"change:currentMode" : "modeChangeHandler"
|
119
|
+
|
120
|
+
modeChangeHandler: ()->
|
121
|
+
@doSomething()</code></pre>
|
122
|
+
<h2>Luca.core.Container Component Events</h2>
|
123
|
+
<p>Containers are special views whose only purpose is to render multiple components in a specified configuration, and handle
|
124
|
+
all of the communication between the components. This is what allows Luca components to be extremely re-usable, because they
|
125
|
+
never know about views that exist outside of them.
|
126
|
+
|
127
|
+
</p>
|
128
|
+
<p>By providing a @componentEvents configuration property on your container, you can bind to events on the components in your container
|
129
|
+
and relay information about them to other members of the container. The format is a key value pair where the key is a string which
|
130
|
+
contains the name of the component and the event it triggers, separated by a space. The value is a name of a method on your view or an anonymous function.
|
131
|
+
|
132
|
+
</p>
|
133
|
+
<pre><code class="lang-coffeescript"> # ctype = component_one
|
134
|
+
_.def("ComponentOne").extends("Luca.View").with
|
135
|
+
name:"one"
|
136
|
+
eventHandler: ()->
|
137
|
+
@trigger "custom:event"
|
138
|
+
|
139
|
+
# ctype = component_two
|
140
|
+
_.def("ComponentTwo").extends("ComponentOne").with
|
141
|
+
name:"two"
|
142
|
+
eventHandler: ()->
|
143
|
+
@trigger "some:other:event"
|
144
|
+
|
145
|
+
_.def("MyContainer").extends("Luca.core.Container").with
|
146
|
+
components:["component_two","component_one"]
|
147
|
+
componentEvents:
|
148
|
+
"one custom:event" : "customEventHandler"
|
149
|
+
|
150
|
+
# when component named one fires an event
|
151
|
+
# we can handle it here, pass it to two, whatever
|
152
|
+
customEventHandler: ()->
|
153
|
+
Luca('two').eventHandler()</code></pre>
|
data/site/docs/view.html
ADDED
@@ -0,0 +1,128 @@
|
|
1
|
+
<h1>Luca.View</h1>
|
2
|
+
<p>The <code>Luca.View</code> class is the base class for Luca components. A number of patterns and optimizations that are helpful in your view classes have been extracted into the base class.
|
3
|
+
|
4
|
+
</p>
|
5
|
+
<h2>Hooks</h2>
|
6
|
+
<p>The concept of hooks in Luca is that components can trigger events, and we can bind to them as normally, and that this is good. However, where it is more useful or cleaner to just define methods that represent some part of the component lifecycle, we provide a configuration API for doing that.
|
7
|
+
|
8
|
+
</p>
|
9
|
+
<pre><code class="lang-coffeescript"> _.def("Luca.View").extends("Backbone.View").with
|
10
|
+
hooks:[
|
11
|
+
"after:initialize" # => @afterInitialize
|
12
|
+
"before:render" # => @beforeRender
|
13
|
+
"after:render" # => @afterRender
|
14
|
+
"first:activation" # => @firstActivation
|
15
|
+
"activation" # => @activation
|
16
|
+
"deactivation" # => @deactivation
|
17
|
+
]</code></pre>
|
18
|
+
<p>Any Luca.View which defines an <code>@afterInitialize</code> method, or a <code>@beforeRender</code> method, will automatically call that method when the corresponding event is triggered.
|
19
|
+
|
20
|
+
</p>
|
21
|
+
<p><strong>Note on extending hook methods</strong>
|
22
|
+
|
23
|
+
</p>
|
24
|
+
<p>If you want to maintain the functionality of the component you are extending from, you will have to remember to call the prototype method like such:
|
25
|
+
|
26
|
+
</p>
|
27
|
+
<pre><code class="lang-coffeescript"> _.def("MyView").extends("Luca.View").with
|
28
|
+
beforeRender: ()->
|
29
|
+
@_super("beforeRender", @, arguments)
|
30
|
+
|
31
|
+
# or, if you prefer
|
32
|
+
Luca.View::beforeRender?.apply(@, arguments)</code></pre>
|
33
|
+
<h2>The @render() method</h2>
|
34
|
+
<p>The default implementation of @render() simply appends the view's <code>@$el</code> to the DOM element represented by the <code>$(@container)</code> property on the view.
|
35
|
+
|
36
|
+
</p>
|
37
|
+
<p>Whatever method you choose to implement for your <code>@render()</code> call should behave similar to how the Backbone.View::render() expects, in that it should return an instance of the view.
|
38
|
+
|
39
|
+
</p>
|
40
|
+
<p>Additionally, the call to <code>@render()</code> will trigger <code>before:render</code> and <code>after:render</code> as which, on a Luca.View is configured as a hook. So any <code>@beforeRender()</code> and <code>@afterRender()</code> method will get called as well, if they exist.
|
41
|
+
|
42
|
+
</p>
|
43
|
+
<h2>Before Render</h2>
|
44
|
+
<p>Since in Luca, the actual render method just attaches the view to its container, setup related methods for building your view's content are best put in the <code>beforeRender()</code> method. In addition to this, there are other options available for filling the content of the view, like <code>@bodyTemplate</code>.
|
45
|
+
|
46
|
+
</p>
|
47
|
+
<h2>Luca.template helper</h2>
|
48
|
+
<p><code>Luca.template()</code> is a util function which allows you reference your client side template system. It accepts a name of a template ( which, if not found, it will attempt to match one for you ) and an object of interpolations to pass to the template function
|
49
|
+
|
50
|
+
</p>
|
51
|
+
<p><code>Luca.available_templates()</code> is a util function, useful for debugging, to see which templates are available to you.
|
52
|
+
|
53
|
+
</p>
|
54
|
+
<h2>Configuration Options</h2>
|
55
|
+
<ul>
|
56
|
+
<li><p><code>@additionalClassNames</code> - an array of CSS classes to apply to the view's <code>@$el</code>. This is helpful for inheritance of views.</p>
|
57
|
+
</li>
|
58
|
+
<li><p><code>@name</code> - Setting a name property on your view, will allow you to reference the instance of that view later.</p>
|
59
|
+
</li>
|
60
|
+
</ul>
|
61
|
+
<pre><code class="lang-coffeescript"> view = new Luca.View(name:"my_view")
|
62
|
+
|
63
|
+
Luca("my_view") is view # => true</code></pre>
|
64
|
+
<ul>
|
65
|
+
<li><p><code>@wrapperClass</code> - automatically wraps the view with a div with this as the CSS class.</p>
|
66
|
+
</li>
|
67
|
+
<li><p><code>@bodyTemplate</code> - will apply the content of the template to your view </p>
|
68
|
+
</li>
|
69
|
+
<li><p><code>@bindAllEvents</code> - true or false automatically bind all event handler methods to the context of your view's instance</p>
|
70
|
+
</li>
|
71
|
+
<li><p><code>@applicationEvents</code> - configuration similar to the DOM <code>@events</code> configuration on Backbone.View. Used to bind to events triggered by the <code>Luca.Application.get()</code> object. You can customize which application you use by setting <code>@app</code> to either reference the app, or to the name of a given application.</p>
|
72
|
+
</li>
|
73
|
+
<li><p><code>@collectionEvents</code> - configuration similar to the DOM <code>@events</code> configuration on Backbone.View. Used to bind to events triggered by the <code>Luca.CollectionManager.get()</code> object. </p>
|
74
|
+
</li>
|
75
|
+
</ul>
|
76
|
+
<h2>Luca.View::$bodyEl()</h2>
|
77
|
+
<p>In your <code>Luca.View</code> definitions, If you set the <code>@bodyTemplate</code> property to one of the available templates, then on <code>initialize</code> the view will set the HTML of its DOM element to the contents of the template.
|
78
|
+
|
79
|
+
</p>
|
80
|
+
<p>This is useful in cases where there is a fair amount of structural, or otherwise static DOM content in your view, and one of the standard <code>Luca.core.Container</code> components is not suited for what you want to do. The <code>Luca.components.Panel</code> view is basically just a <code>Luca.View</code> which has additional DOM containers for footers and headers. It accomplishes this through the use of the <code>@bodyClassName</code> and <code>@bodyTagName</code> properties.
|
81
|
+
|
82
|
+
</p>
|
83
|
+
<p><code>@bodyClassName</code> and <code>@bodyTagName</code> work the same way the <code>@className</code> and <code>@tagName</code> properties work on standard Backbone views. They are used to create a DOM element via <code>Backbone.View.prototype.make(@bodyTagName,class:@bodyClassName,id:@bodyId</code>
|
84
|
+
|
85
|
+
</p>
|
86
|
+
<p>If you use <code>view.$bodyEl()</code> instead of the standard <code>view.$el()</code> that ships with Backbone, all of the standard DOM manipulation methods available will be scoped to the CSS selector that corresponds to the actual body element of your view.
|
87
|
+
|
88
|
+
</p>
|
89
|
+
<h2>Deferrable Rendering</h2>
|
90
|
+
<p>The jury is still out as to whether or not deferrable rendering is a useful pattern, or whether it is too complex. The use case it was trying to optimize is for views which can only be rendered in response to an event being fired on another object. Such as <code>Backbone.Collection::fetch</code>.
|
91
|
+
|
92
|
+
</p>
|
93
|
+
<p>If this is what you are doing, then this feature is for you.
|
94
|
+
|
95
|
+
</p>
|
96
|
+
<p>The options available for views which use the <code>@deferrable</code> property are as follows:
|
97
|
+
|
98
|
+
</p>
|
99
|
+
<ul>
|
100
|
+
<li><code>@deferrable</code> - if you set a reference to an object, such as a collection, on the @deferrable property, then the call to <code>view.render()</code> will actually just set up an event binding to the <code>reset</code> event of your collection, and it will automatically call <code>fetch</code> for you on that collection. </li>
|
101
|
+
</ul>
|
102
|
+
<p>If you set <code>@deferrable</code> to true then the view will expect a <code>@collection</code> property.
|
103
|
+
|
104
|
+
</p>
|
105
|
+
<ul>
|
106
|
+
<li><p><code>@deferrable_method</code> - a call to <code>@render()</code> on a <code>@deferrable</code> view will automatically call this method on the <code>@deferrable</code> object.</p>
|
107
|
+
</li>
|
108
|
+
<li><p><code>@deferrable_trigger</code> - if you use the deferrable system , by default, it will automatically call the <code>@deferrable_method</code> on your <code>@deferrable</code> object when you call <code>@render()</code>. However, if you want to defer this method being fired even later, just set the <code>@deferrable_trigger</code> property to whatever trigger your view will listen for.</p>
|
109
|
+
</li>
|
110
|
+
</ul>
|
111
|
+
<p>A useful example would be for views which get rendered hidden, and activated if and only if the user does a specific action. ( For example, a TabView activating a secondary tab ). If that action triggers an event, and you want to delay the render process if and only if that event is triggered.
|
112
|
+
|
113
|
+
</p>
|
114
|
+
<h2>Helpers</h2>
|
115
|
+
<ul>
|
116
|
+
<li><p><code>view.$template</code> calls <code>view.$.html()</code> on your view, with whatever is returned from the template. Delegates to <code>Luca.template(templateName, customizationHash)</code></p>
|
117
|
+
</li>
|
118
|
+
<li><p><code>view.$wrap</code> the same as <code>view.$el.wrap()</code> -- accepts a CSS class name string, or a DOM element</p>
|
119
|
+
</li>
|
120
|
+
<li><p><code>view.$append</code> the same as <code>view.$el.append()</code></p>
|
121
|
+
</li>
|
122
|
+
<li><p><code>view.$container</code> - references <code>$( view.container )</code>. Note: the @container property is set on a view when it belongs to the <code>@components</code> property of a <code>Luca.core.Container</code> instance. It is just a standard CSS selector.</p>
|
123
|
+
</li>
|
124
|
+
<li><p><code>view.registerEvent()</code> manipulates your <code>@events</code> configuration on your Backbone.View and then calls <code>@delegateEvents</code> to make sure they are live.</p>
|
125
|
+
</li>
|
126
|
+
</ul>
|
127
|
+
<h2>Backbone Component Helpers</h2>
|
128
|
+
<p>Views which have properties on them referencing other views, models, or collections, can access those objects by calling <code>view.models()</code> or <code>view.views()</code> or <code>view.collections()</code>. This is mainly useful for introspection, debugging, or what not.</p>
|
Binary file
|
Binary file
|