Skip to content

Adding support for a fully-rendered notification on views? #30

@chikamichi

Description

@chikamichi

Hi,

When using Giraffe, a common pattern emerged: I use afterRender hooks a lot to create subviews. I don't know if its the recommended way of doing it, but it feels smooth enough that I keep doing it without any issues.

Current implementation of render in Giraffe.View is as follow:

render: (options) =>
    #
    @afterRender.apply @, arguments
    @trigger 'rendered', @, options

The "rendered" event will thus fire without waiting for any sub-rendering that may occur within afterRender (for instance, rendering of subviews). It thus prevents from doing some DOM manipulation that require the subview's DOM to exist in the document DOM (because subviews' DOM may not be available yet for it they have not been attached yet) or that require any sort of asynchronous post-processing for that matter (waiting for data from an API to fill in, waiting for animations to run, waiting for a semaphore to unlock, waiting for another subview to render/get its data etc.)

I got around this by having my subviews expose a "rendering promise", and by having their common parent view's afterRender hook (where the subviews are created) monitor those promises, so as to fire a "fully_rendered" event when all subviews are completely rendered. The callback to this event is then a good place to perform any DOM manipulation on the parent view and its subviews.

I wonder whether Giraffe could add built-in support for such a logic. Given the piece of code above, it could look like this:

render: (options) =>
    #
    afterRendering = @afterRender.apply @, arguments
    afterRendering.done => @trigger 'fully_rendered', @, options
    @trigger 'rendered', @, options

with afterRender having a new default implementation (currently: ->):

afterRender: ->
    @fullyRendered.resolve()
    return @fullyRendered.promise()

where @fullyRendered is a new $.Deferred() stored on the view.

Someone willing to create subviews inside an afterRender hook could then do something like this in a view:

# ParentView:
afterRender: ->
    @subview1 = new SubView1()
    @subview2 = new SubView2()

    $.when(@subview1.fullyRendered, @subview2.fullyRendered).done =>
      @manipulateDOM()
      @fullyRendered.resolve()

    return @fullyRendered.promise()

manipulateDOM: ->
  @subview1.$('.tool.delete').popup
    content: 'Delete this subview'
  # whatever…

And later on, somewhere else I'd rather use events than binding to a promise:

@parentView = new ParentView()
@listenTo @parentView, 'fully_rendered', @doStuff

One could use @parentView.fullyRendered.done -> …, but somehow, I feel like promises are more of an internal (yet very useful) trick. YMMV 🌌

Obviously, the public API could be simplified to its bare minimum by wrapping afterRender inside some wrapper that would return @fullyRendered.promise(), while @fullyRendered.resolve() could also be hidden behind something like @nowFullyRendered(), so the end user may only ever need to return @nowFullyRendered() to gain access to the feature (if he needs to!).

What do you think?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions