Help understanding _uihooks


#1
Items = new Mongo.Collection('items');
if (Meteor.isServer) {
	Items.remove({});
	Items.insert({title: 'foo'});
	Items.insert({title: 'foo'});
	Items.insert({title: 'foo'});
}

if (Meteor.isClient) {
	Template.layout.helpers({
		items: function () {
			return Items.find();
		}
	});

	Template.layout.onRendered(function () {
		$('.container')[0]._uihooks = {
			insertElement: function (node, next) {
				$(node).insertBefore(next)
				console.log('Inserting an item.');
			}
		}
	});
}
<body>
	{{>layout}}
</body>

<template name="layout">
	<div class="container">
		{{#each items}}
			<div class="item">{{title}}</div>
		{{/each}}
	</div>
</template>

In my understanding this code should produce 3 console.logs in line with rendering each .item. But it does nothing…


#2

Try it without using jQuery, f.e.:

this.find('.container')._uihooks

#3

Nope. doesn’t work…


#4

You add all items on the startup right? Just try to add them with some delay, f.e. 5 seconds after template got rendered. I think uihooks works only when you add new items after the rendering process.


#5

in the time onRendered is run, all these 3 items are most probably already rendered.
Just add/remove them with mongol to check if it behaves as you expect when adding.


#6

@XTA, @shock - you are right, hooks work only on elements added after tpl was rendered.
But that’s most unfortunate… how can I delay rendering of initial elements in {{#each}} block?


#7

I dont think there is any logic reason to do it.
Maybe if you say what you are trying to accomplish we can propose some other solution.


#8

Actually, I want to animate all .items. But I need to animate them on initial page load. And I have to call a custom animation function, that’s why I wanted to hook into the #each rendering process.


#9

Is not enough that user have to wait till page loads, now you want him to wait even longer till some animation finish ?
I think animations are good to mask slow data, but not to extend unusability even more.

And you can create template level reactive var, which you will change in onRendered/
If you return in items helper null till that reactive var will be true, you can easily delay it that way.


#10

Maybe it will also work if you create a template for your items, then you can call the onRendered method, f.e.

Template.item.onRendered (function () {....})

#11

Yes, I’ve attached a simple Session to the helper and now _uihooks function runs. But now it doesn’t render the .items themselves! only runs console.log :angry:


#12

Yes, that would be a possible solution. But how to animate Template.item?


#13

That’s expected: https://github.com/meteor/meteor/blob/devel/History.md#blaze-5

Note that when you set one of these functions on a container element, Blaze will not do the actual operation; it’s your responsibility to actually insert, move, or remove the node (by calling $(node).remove(), for example).


#14

Well, I have $(node).insertBefore(next) - isnt that enough?
btw, when I make

insertElement: function (node, next) {
  $(node).insertBefore(next)
  console.log(node)
  console.log(next)
}

it outputs markup for node but null for next - isn’t that strange?


#15

Yes, it should be, but the docs I’ve looked at use this.find()._uihooks as @XTA suggested. Maybe worth another try?


#16

And what would you expect be the next DOM element inside container before which you want to insert it?
When you are adding 1st element, that null sounds quite reasonable.


#17

this undocumented stuff just refuses to work!


#18

@avalanche1 I don’t know which tools you use to animate your items, but I would try it with https://atmospherejs.com/natestrauser/animate-css (Doc here).

Then, in your Template.item.onRendered() function I would add something like this:

$(".item").addClass("animated fadeIn");

#19

I created a new meteor project, took the JS you have and replaced the JS in my new project with it completely. Did the same for the HTML and I get 3 console.logs.