Skip to content
This repository has been archived by the owner on Apr 26, 2018. It is now read-only.

Commit

Permalink
Call afterAttach method on views if it is defined instead of trigge…
Browse files Browse the repository at this point in the history
…ring 'attach' events. Always call this method on any attachment, passing true or false to indicate whether the view has been attached to the full DOM.
  • Loading branch information
nathansobo committed Apr 26, 2012
1 parent 00e093a commit 4116e50
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 35 deletions.
68 changes: 39 additions & 29 deletions space-pen-spec.coffee
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -128,21 +128,18 @@ describe "View", ->
expect(-> new BadView).toThrow("View markup must have a single root element") expect(-> new BadView).toThrow("View markup must have a single root element")


describe "when a view is attached to another element via jQuery", -> describe "when a view is attached to another element via jQuery", ->
[content, view2, view3, attachHandler, subviewAttachHandler, view2AttachHandler, view3AttachHandler] = [] [content, view2, view3, view4] = []


beforeEach -> beforeEach ->
view2 = new TestView view2 = new TestView
view3 = new TestView view3 = new TestView
view4 = new TestView


attachHandler = jasmine.createSpy 'attachHandler' view.afterAttach = jasmine.createSpy 'view.afterAttach'
subviewAttachHandler = jasmine.createSpy 'subviewAttachHandler' view.subview.afterAttach = jasmine.createSpy('view.subview.afterAttach')
view2AttachHandler = jasmine.createSpy 'view2AttachHandler' view2.afterAttach = jasmine.createSpy('view2.afterAttach')
view3AttachHandler = jasmine.createSpy 'view3AttachHandler' view3.afterAttach = jasmine.createSpy('view3.afterAttach')

expect(view4.afterAttach).toBeUndefined()
view.on 'attach', attachHandler
view.subview.on 'attach', subviewAttachHandler
view2.on 'attach', view2AttachHandler
view3.on 'attach', view3AttachHandler


describe "when attached to an element that is on the DOM", -> describe "when attached to an element that is on the DOM", ->
beforeEach -> beforeEach ->
Expand All @@ -151,29 +148,42 @@ describe "View", ->
afterEach -> afterEach ->
content.empty() content.empty()


it "triggers an 'attach' event on all appended views and their subviews", -> describe "when $.fn.append is called with a single argument", ->
content.append view, [view2, view3] it "calls afterAttach (if it is present) on the appended view and its subviews, passing true to indicate they are on the DOM", ->
expect(attachHandler).toHaveBeenCalled() content.append view
expect(subviewAttachHandler).toHaveBeenCalled() expect(view.afterAttach).toHaveBeenCalledWith(true)
expect(view2AttachHandler).toHaveBeenCalled() expect(view.subview.afterAttach).toHaveBeenCalledWith(true)
expect(view3AttachHandler).toHaveBeenCalled()

describe "when $.fn.append is called with multiple arguments", ->
view.detach() it "calls afterAttach (if it is present) on all appended views and their subviews, passing true to indicate they are on the DOM", ->
content.empty() content.append view, view2, [view3, view4]
attachHandler.reset() expect(view.afterAttach).toHaveBeenCalledWith(true)
subviewAttachHandler.reset() expect(view.subview.afterAttach).toHaveBeenCalledWith(true)

expect(view2.afterAttach).toHaveBeenCalledWith(true)
otherElt = $('<div>') expect(view3.afterAttach).toHaveBeenCalledWith(true)
content.append(otherElt)
view.insertBefore(otherElt) describe "when $.fn.insertBefore is called on the view", ->
expect(attachHandler).toHaveBeenCalled() it "calls afterAttach on the view and its subviews", ->
expect(subviewAttachHandler).toHaveBeenCalled() otherElt = $('<div>')
content.append(otherElt)
view.insertBefore(otherElt)
expect(view.afterAttach).toHaveBeenCalledWith(true)
expect(view.subview.afterAttach).toHaveBeenCalledWith(true)

describe "when a view is attached as part of a larger dom fragment", ->
it "calls afterAttach on the view and its subviews", ->
otherElt = $('<div>')
otherElt.append(view)
content.append(otherElt)
expect(view.afterAttach).toHaveBeenCalledWith(true)
expect(view.subview.afterAttach).toHaveBeenCalledWith(true)


describe "when attached to an element that is not on the DOM", -> describe "when attached to an element that is not on the DOM", ->
it "does not trigger an attach event", -> it "calls afterAttach (if it is present) on the appended view and its subviews, passing false to indicate they aren't on the DOM", ->
fragment = $('<div>') fragment = $('<div>')
fragment.append view fragment.append view
expect(attachHandler).not.toHaveBeenCalled() expect(view.afterAttach).toHaveBeenCalledWith(false)
expect(view.subview.afterAttach).toHaveBeenCalledWith(false)


it "allows $.fn.append to be called with undefined without raising an exception", -> it "allows $.fn.append to be called with undefined without raising an exception", ->
view.append undefined view.append undefined
Expand Down
17 changes: 11 additions & 6 deletions space-pen.coffee
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ class View extends jQuery
@wireOutlets(this) @wireOutlets(this)
@bindEventHandlers(this) @bindEventHandlers(this)
@find('*').andSelf().data('view', this) @find('*').andSelf().data('view', this)
@attr('triggerAttachEvents', true) @attr('callAttachHooks', true)
step(this) for step in postProcessingSteps step(this) for step in postProcessingSteps
@initialize?(args...) @initialize?(args...)


Expand Down Expand Up @@ -163,25 +163,30 @@ class Builder
jQuery.fn.view = -> this.data('view') jQuery.fn.view = -> this.data('view')


# Trigger attach event when views are added to the DOM # Trigger attach event when views are added to the DOM
triggerAttachEvent = (element) -> callAttachHook = (element) ->
if element?.attr?('triggerAttachEvents') and element.parents('html').length return unless element
element.find('[triggerAttachEvents]').add(element).trigger('attach') elements = element.find?('[callAttachHooks]').toArray() ? []
elements.push(element[0]) if element.attr?('callAttachHooks')
for element in elements
return unless view = $(element).view()
onDom = view.parents('html').length > 0
view.afterAttach?(onDom)


for methodName in ['append', 'prepend', 'after', 'before'] for methodName in ['append', 'prepend', 'after', 'before']
do (methodName) -> do (methodName) ->
originalMethod = $.fn[methodName] originalMethod = $.fn[methodName]
jQuery.fn[methodName] = (args...) -> jQuery.fn[methodName] = (args...) ->
flatArgs = [].concat args... flatArgs = [].concat args...
result = originalMethod.apply(this, flatArgs) result = originalMethod.apply(this, flatArgs)
triggerAttachEvent arg for arg in flatArgs callAttachHook arg for arg in flatArgs
result result


for methodName in ['prependTo', 'appendTo', 'insertAfter', 'insertBefore'] for methodName in ['prependTo', 'appendTo', 'insertAfter', 'insertBefore']
do (methodName) -> do (methodName) ->
originalMethod = $.fn[methodName] originalMethod = $.fn[methodName]
jQuery.fn[methodName] = (args...) -> jQuery.fn[methodName] = (args...) ->
result = originalMethod.apply(this, args) result = originalMethod.apply(this, args)
triggerAttachEvent(this) callAttachHook(this)
result result


(exports ? this).View = View (exports ? this).View = View
Expand Down

0 comments on commit 4116e50

Please sign in to comment.