Move the component view initialization into it's own binding so that we can have…
… it bind, unbind and update with it's parent view.
Showing
1 changed file
with
52 additions
and
22 deletions
... | @@ -155,6 +155,56 @@ class Rivets.Binding | ... | @@ -155,6 +155,56 @@ class Rivets.Binding |
155 | 155 | ||
156 | @binder.update?.call @, models | 156 | @binder.update?.call @, models |
157 | 157 | ||
158 | # Rivets.ComponentBinding | ||
159 | # ----------------------- | ||
160 | |||
161 | # A component view encapsulated in a binding within it's parent view. | ||
162 | class Rivets.ComponentBinding extends Rivets.Binding | ||
163 | # Initializes a component binding for the specified view. The raw component | ||
164 | # element is passed in along with the component type. Attributes and scope | ||
165 | # inflections are determined based on the components defined attributes. | ||
166 | constructor: (@view, @el, @type) -> | ||
167 | @component = Rivets.components[@type] | ||
168 | @attributes = {} | ||
169 | @inflections = {} | ||
170 | |||
171 | for attribute in @el.attributes or [] | ||
172 | if attribute.name in @component.attributes | ||
173 | @attributes[attribute.name] = attribute.value | ||
174 | else | ||
175 | @inflections[attribute.name] = attribute.value | ||
176 | |||
177 | # Intercepts `Rivets.View::sync` since component bindings are not bound to a | ||
178 | # particular model to update it's value. | ||
179 | sync: -> | ||
180 | |||
181 | # Returns an object map using the component's scope inflections. | ||
182 | locals: (models = @view.models) => | ||
183 | result = {} | ||
184 | result[key] = models[inverse] for key, inverse of @inflections | ||
185 | result[key] ?= model for key, model of models | ||
186 | |||
187 | result | ||
188 | |||
189 | # Intercepts `Rivets.View::update` to be called on `@componentView` with a | ||
190 | # localized map of the models. | ||
191 | update: (models) => | ||
192 | @componentView?.update @locals models | ||
193 | |||
194 | # Intercepts `Rivets.View::bind` to build `@componentView` with a localized | ||
195 | # map of models from the root view. Bind `@componentView` on subsequent calls. | ||
196 | bind: => | ||
197 | if @componentView? | ||
198 | @componentView?.bind() | ||
199 | else | ||
200 | el = @component.build.call @attributes | ||
201 | (@componentView = new Rivets.View(el, @locals(), @view.options)).bind() | ||
202 | @el.parentNode.replaceChild el, @el | ||
203 | |||
204 | # Intercept `Rivets.View::unbind` to be called on `@componentView`. | ||
205 | unbind: => | ||
206 | @componentView?.unbind() | ||
207 | |||
158 | # Rivets.View | 208 | # Rivets.View |
159 | # ----------- | 209 | # ----------- |
160 | 210 | ||
... | @@ -183,8 +233,7 @@ class Rivets.View | ... | @@ -183,8 +233,7 @@ class Rivets.View |
183 | new RegExp "^#{@config.prefix?.toUpperCase() ? 'RV'}-" | 233 | new RegExp "^#{@config.prefix?.toUpperCase() ? 'RV'}-" |
184 | 234 | ||
185 | # Parses the DOM tree and builds `Rivets.Binding` instances for every matched | 235 | # Parses the DOM tree and builds `Rivets.Binding` instances for every matched |
186 | # binding declaration. Subsequent calls to build will replace the previous | 236 | # binding declaration. |
187 | # `Rivets.Binding` instances with new ones, so be sure to unbind them first. | ||
188 | build: => | 237 | build: => |
189 | @bindings = [] | 238 | @bindings = [] |
190 | skipNodes = [] | 239 | skipNodes = [] |
... | @@ -235,27 +284,8 @@ class Rivets.View | ... | @@ -235,27 +284,8 @@ class Rivets.View |
235 | buildBinding text, 'textNode', token.value if token.type is 1 | 284 | buildBinding text, 'textNode', token.value if token.type is 1 |
236 | else if componentRegExp.test node.tagName | 285 | else if componentRegExp.test node.tagName |
237 | type = node.tagName.replace(componentRegExp, '').toLowerCase() | 286 | type = node.tagName.replace(componentRegExp, '').toLowerCase() |
287 | @bindings.push new Rivets.ComponentBinding @, node, type | ||
238 | 288 | ||
239 | if component = Rivets.components[type] | ||
240 | attributes = {} | ||
241 | inflections = {} | ||
242 | |||
243 | for attribute in node.attributes or [] | ||
244 | if attribute.name in component.attributes | ||
245 | attributes[attribute.name] = attribute.value | ||
246 | else | ||
247 | model = @models | ||
248 | model = model[key] for key in attribute.value.split('.') | ||
249 | inflections[attribute.name] = model | ||
250 | |||
251 | el = component.build.call(attributes) | ||
252 | |||
253 | models = {} | ||
254 | models[key] = model for key, model of inflections | ||
255 | models[key] ?= model for key, model of @models | ||
256 | |||
257 | (view = new Rivets.View(el, models, @options)).bind() | ||
258 | node.parentNode.replaceChild el, node | ||
259 | else if node.attributes? | 289 | else if node.attributes? |
260 | for attribute in node.attributes | 290 | for attribute in node.attributes |
261 | if bindingRegExp.test attribute.name | 291 | if bindingRegExp.test attribute.name | ... | ... |
-
Please register or sign in to post a comment