rivets icon indicating copy to clipboard operation
rivets copied to clipboard

Is there any event or callback to know when the DOM has finished updating?

Open PierBover opened this issue 9 years ago • 15 comments

I'm making a grid of elements and after the DOM has been populated with rv-each I need to dynamically set the width and height of the grid elements using jQuery.

How would this be done?

PierBover avatar Apr 01 '15 22:04 PierBover

This can be done. But...I hope someone else chimes in with a better solution, cause mine is a little dirty I think.

Check out this fiddle: http://jsfiddle.net/af8qwh8j/

The gist of it is to make a new each binder, I called mine evented-each-* and call the regular each binder while wrapping all its methods in your custom events.

rivets.binders['evented-each-*'] = {
    block : true,
    priority : 4000,
    bind : function() {
        $('#fireEventsHere').trigger('bind:begining');
        rivets.binders['each-*'].bind.apply(this, arguments);                       
        $('#fireEventsHere').trigger('bind:complete');
    },
    unbind : function() {
        $('#fireEventsHere').trigger('unbind:begining');
        rivets.binders['each-*'].unbind.apply(this, arguments);
        $('#fireEventsHere').trigger('unbind:complete');
    },
    routine : function() {
        $('#fireEventsHere').trigger('routine:begining');
        rivets.binders['each-*'].routine.apply(this, arguments);
        $('#fireEventsHere').trigger('routine:complete');
    },
    update : function() {
        $('#fireEventsHere').trigger('update:begining');
        rivets.binders['each-*'].update.apply(this, arguments);
        $('#fireEventsHere').trigger('update:complete');
    }
};

As you remove names from the list, you will see new events fire. Pretty cool stuff.

Hope this helps.

Duder-onomy avatar Apr 02 '15 00:04 Duder-onomy

Thanks again for your help!

When does the update callback get triggered?

PierBover avatar Apr 02 '15 02:04 PierBover

It is an internal method inside the each-* binder.

It looks like it gets called when, after initially looping though the collection and building the initial DOM, one of the items in the collections model changes.

Duder-onomy avatar Apr 02 '15 17:04 Duder-onomy

It would be great if those callbacks or events were included by default on Rivets.

Can you mark it as a feature request?

PierBover avatar Apr 02 '15 18:04 PierBover

@PierBover I am not a collab on this repo, I cannot mark stuff as bug etc.
Though, from lurking on this Repo pretty hard for the last year or so, my feeling is that the community would rather you just write your own binders. If you really feel strongly about it you should ping mikeric.

I am currently working on a each-* binder that will have full event support bove and will also keep the DOM in order. It will fire beforeEnter afterEnter and beforeExit events on the iterated models so you can easily animate the iterated items in and out of the DOM like,

<ul>
    <li rv-each-item='model.collection' rv-on-beforeEnter='set height to zero' rv-on-afterEnter='animate into DOM ' rv-on-beforeExit='animate out of DOM' ></li>
</ul>

The only problem with this right now is that Rivets does not keep the actual DOM elements in order.

For example, if you splice the center item out of an array of 3 items that is bound via each-*. You would expect it to just remove the middle element from the DOM. But, it actually removes the last item from the DOM and then re-binds the second item in the DOM with the model of the previous third item in the collection. Anyhow, I will keep a note to Ping you when I finish my each-* binder. It sounds like it could be useful to you.

Duder-onomy avatar Apr 02 '15 19:04 Duder-onomy

This is interesting. Yes please, ping me when it's finished.

PierBover avatar Apr 02 '15 19:04 PierBover

Agree, I think there should be some way to get notified when a model get bound and rendered, so further DOM manipulations can be made without hacks (setTimeout or something).

ilyashubin avatar Apr 05 '15 21:04 ilyashubin

Can someone add the "feature request" label, please?

PierBover avatar Apr 05 '15 23:04 PierBover

@NVO and @PierBover Here is a previous issue thread talking about something similar. Ammo for your arsenal?

I personally feel that (since rivets does not currently send any special events) that maybe you should make a rivets plugin? evented-rivets or something like that. Then you can monkey patch events into all the binders you want? In the apps I work on, I am usually only concerned about when the items are entering and exiting the DOM. rv-if rv-show etc. So that I might animate them without attaching animation logic to the model informing the DOM.

I believe rivets.bind is sync. So you know rivets is done binding then that method is finished.

What exactly are you looking for rivets to support? just on-bind or other events as well?

Duder-onomy avatar Apr 06 '15 17:04 Duder-onomy

What exactly are you looking for rivets to support? just on-bind or other events as well?

I'm new to Rivets so I can't really say a general case, but my particular problem was that I needed a callback whenever some piece of the DOM was updated by Rivets.

What I ended up doing any time I changed the model was to unbind() Rivets, update the objects Rivets is reading, bind(), and then do my DOM stuff.

PierBover avatar Apr 06 '15 17:04 PierBover

:+1:

davekiss avatar Jun 04 '15 16:06 davekiss

+1 for a callback that gets called after some piece of the DOM is updated by rivets. It would allow our parent views to listen for subview changes and potentially run DOM post-processing across the entire view.

Example use case: say you want to make all inputs and buttons have disabled = true if some top level view attribute called "readOnly" is true, but these inputs and buttons are getting rendered by subviews. To solve this, you could observe rivets to know when a subview changed, and then apply disable=true to all DOM elements in the DOM subtree of the parent view.

ksikka avatar Aug 05 '15 23:08 ksikka

Looking for the same thing but on top of a component.

kojilab avatar Oct 27 '16 01:10 kojilab

Actually my example doesn't work :(

kojilab avatar Oct 27 '16 15:10 kojilab

You could try this if you want to know when your componente was rendered:


rivets.components['your-component'] = {
  template: function() {
    [...]
  },
  initialize: function(el, data) {
    var controller = this;  
    var $el = $(el);

    var ready = function() {
      // do your stuff after view is rendered here
    }

    /*
     * Detect DOM changes
     * 
     * Note: This event has been deprecated in favor of the Mutation Observer API
     * 
     * Be very careful with this event it is easy to cause an infinite loop if you decide to change the DOM inside the event handler,
     * so it is better to bind the event only once with ` $el.one('DOMSubtreeModified', ready);` instead of `$el.on('DOMSubtreeModified', ready);`
     * 
     * @see https://developer.mozilla.org/en-US/docs/Web/Events/DOMSubtreeModified
     */
    $el.one('DOMSubtreeModified', ready);
    return controller;
  }
};

or this:

rivets.components['your-component'] = {
  template: function() {
    [...]
  },
  initialize: function(el, data) {
    var controller = this;  
    var observer;
    var ready = function(mutationsList) {
      // do your stuff after view is rendered here

      observer.disconnect(); // if you want just detect the first update
    }

    /*
     * Create an observer instance linked to the callback function
     * @see https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver
     */
    observer = new MutationObserver(ready);

    // Create an observer instance linked to the callback function
    observer.observe(el, {
      attributes: true,
      childList: true
    });

    return controller;
  }
};

Both work for me

JumpLink avatar Mar 06 '18 11:03 JumpLink