Callback after the DOM was updated in Meteor.js - meteor

I have this Meteor project: https://github.com/jfahrenkrug/code_buddy
It's basically a tool with a big textarea and a pre area that lets you enter source code snippets that automatically get pushed to all connected clients.
I'd like to automatically run the highlightSyntax function when the code was changed, but it doesn't really work.
I've tried query.observe, but that didn't work too well: The syntax highlight flashed up once and then disappeared again.
So my question is: How do I run code after the DOM was updated?

A hacky way to do it is:
foo.html
<template name="mytemplate">
<div id="my-magic-div">
.. stuff goes here ..
{{add_my_special_behavior}}
</div>
</template>
foo.js
Template.mytemplate.add_my_special_behavior = function () {
Meteor.defer(function () {
// find #my-magic-div in the DOM
// do stuff to it
});
// return nothing
};
The function will get called whenever the template is rendered (or re-rendered), so you can use it as a hook to do whatever special DOM manipulation you want to do. You need to use Meteor.defer (which does the same thing as settimeout(f, 0)) because at the time the template is being rendered, it isn't in the DOM yet.
Keep in mind that you can render a template without inserting it in the DOM! For example, it's totally legal to do this:
console.log(Template.mytemplate())
So when a template is rendered, there is not a 100% guarantee that it is going to end up in the DOM. It's up to the user of the template.

Starting with Meteor 0.4.0, Template.myTemplate.rendered provides a callback that
is called once when an instance of Template.myTemplate is rendered into DOM nodes and put into the document for the first time.
More info at http://docs.meteor.com/#template_rendered

As for the current version of Meteor (1.0), we can now use the .afterFlush() function of Tracker.
Tracker.autorun(function(e){
var data = Router.current().data();
if(data.key !== undefined){
//the data is there but dom may not be created yet
Tracker.afterFlush(function(){
//dom is now created.
});
}
});

There is no callback after the DOM is updated, however you can force all pending DOM updates with Tracker.flush().
After you call flush(), you know the DOM has been updated and so you can perform any manual DOM changes you need.

This question is quite old, but the two-year-later solution would be to integrate an operational transformation library with Meteor and use Ace or CodeMirror on the client, which does the syntax highlighting automatically. This has the additional benefit of allowing people to edit at the same time.
I've already done the work for you :)

In Blaze Components (I am one of authors) you have an API which calls methods when DOM is inserted, moved, or removed. You can see here how to make an reactive variable when DOM changes.
The downside with this approach is that it does not change when DOM element attributes change (like class change). Only when DOM elements themselves are changed. This works for most cases, but if you need the second, I suggest you simply use MutationObserver. In this case you will be able to respond also to outside changes.

It seems Template.myTemplate.rendered doesn't work properly or I don't get it...
I need to load TinyMCE inline after a template with all posts are rendered, so I have :
- a Template
<div id="wrapper">
{{#each posts}}
<div class="editable">{{post}}</div>
{{/each}}
</div>
- and a Function
Template.myPosts.rendered = function(){
console.dir($("div"));
tinymce.init({
selector: "div.editable",
inline: true,
plugins: [
"advlist autolink lists link image charmap print preview anchor",
"searchreplace visualblocks code fullscreen",
"insertdatetime media table contextmenu paste"
],
toolbar: "insertfile undo redo | styleselect | bold italic | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | link image"
});
}
However the console logs only the <div id="wrapper"> and not the <div class="editable"> divs, which contain my posts. So, it seems Template.myTemplate.rendered callback occures before the template is rendered, right?
EDIT: I put the Template.myTemplate.rendered code inside a setTimeout() and all seems to work, so I'm sure Template.myTemplate.rendered causes the problem.

I just found a little hack that seems to be working pretty well:
Template.myTemplate.onRendered(function() {
this.autorun(function() {
Meteor.setTimeout(function() {
// DOM has been updated
}, 1);
});
});
I'm not a Meteor expert so it might have some downsides, but I haven't found any for now — except that it's a bit dirty !

I think you might want to pass a callback to
Meteor.startup(callback)
see http://docs.meteor.com/#meteor_startup

Related

How to focus an input on subscriptionsReady using FlowRouter in Meteor

I'm currently getting used to using FlowRouter after a while using Iron Router and trying to set up some best practices. I'm subscribing to my collection at a template level.
Previously I've waited for a template to render using onRendered and then targeted my input field and applied focus(), however I am now trying to only show my template in Blaze when the subscriptions are ready using the following (please excuse the Jade but I think it's pretty clear in this case)
template(name="subjectNew")
unless Template.subscriptionsReady
+spinner
else
form
input(type="text" name="name")
So the basic idea is that until the subscriptions are ready the spinner shows. The issue I'm having is that now even when the template renders, the focus won't apply. I've tried various methods of wrapping it in an autorun call but not sure the best way of trying to target the first field when combined with this approach?
Template.subjectNew.onRendered(function() {
console.log('rendered');
$('input').first().focus();
});
Is it possible?
Many thanks for any ideas.
Your subjectNew is considered rendered even when it is only showing the spinner. Just stick your:
form
input(type="text" name="name")
Into a separate template and then attach your focus code to the onRendered handler of that other template.
template(name="subjectNew")
unless Template.subscriptionsReady
+spinner
else
+myForm
template(name="myForm")
form
input(type="text" name="name")
js:
Template.myForm.onRendered(function(){
$('input').focus()
});
I think using an autorun would be a good approach but then you would have to employ Tracker.afterFlush() to wait to set the focus after the form is rendered.
Something like:
Template.subjectNew.onRendered(function() {
this.autorun(() => {
if (this.subscriptionsReady()) {
Tracker.afterFlush(() => $('input').first().focus());
}
});
});

Calling holder.js in Meteor

I'm new to Meteor and trying to get holder.js to work in the framework. It works on refresh, but when moving from one route to another, it breaks.
The documentation just says "Because Meteor includes scripts at the top of the document by default, the DOM may not be fully available when Holder is called. For this reason, place Holder-related code in a "DOM ready" event listener."
I assume I need a Template.foo.onRendered callback, but unsure how to format it. Here's the HTML:
<img class="holder" src="holder.js/120x120">
And here's the callback I've added in a .js file:
Template.contactSingle.onRendered(function() {
this.$('.holder').Holder.run();
});
Again, the holder.js images appear on refresh, but I can't get them to render when going from one page to another. I'm using FlowRouter for routing.
I'm sure it's something simple. Any help is greatly appreciated!
Change your code from:
Template.contactSingle.onRendered(function() {
this.$('.holder').Holder.run();
});
to:
Template.contactSingle.onRendered(function() {
Holder.run({images: document.querySelectorAll('.holder')});
});
Obviously you do not want to do the costly document.querySelectorAll('.holder'). If you can reduce that to you template using a class from its wrapper.
For example:
Template:
<template name="singlePost">
<div class="single-post">
<h2>This is the singlePost area.</h2>
<img class='holder' src="holder.js/300x200">
</div>
</template>
and onRendered
Template.singlePost.onRendered(function() {
Holder.run({
images: document.querySelectorAll('.single-post .holder')
});
});

Component initialization with Iron Router, Jquery, Materialize

Struggling here with what would otherwise be a simple $( document ).ready().
Not sure what I'm doing wrong.
Materialize needs jquery components to be initialized on DOM ready. Finding a way to initialize components on all views is surprisingly tricky.
Here is the online DEMO
From reading the docs: this should initialize everything the sub-templates require:
Template.layout.rendered = function(){
$('ul.tabs').tabs()
}
}
However, this only works on a hard page refresh, and not with links routing the views.
So instead you would have to initialize on each template that element will be used
Template.x.rendered ...
Template.y.rendered ...
Here is the github code
BTW We've tried iron-router events:
onRun
onBeforeAction
onAfterAction
All of these seem to happen before the route's template content is present. I noticed that onBeforeAction required a call to this.next() to go on, I even tried looking for the DOM content after the next call.
I also tried rewriting our routes like this:
Router.route('someRoute', function() {
this.render('someRoute');
// look for DOM content, still not found
});
Just to be clear, the reason this is happening is because your layout is only firing the rendered hook once. When you switch routes the layout template will not be rerendered, only the templates in the yield region will be. The previous template in that region gets destroyed and the next one rerendered. This means you have to run $('ul.tabs').tabs() again for that Template as the DOM elements it contains are rerendered.
Putting that code in the rendered function of the template that uses it works because that rendered hook gets run every time that particular template gets rendered again.
A way you could get around this could be to create a Template specifically for your tabs, like a control in a way, that calls $('ul.tabs').tabs() in its own rendered function. You could then put this control on a template that needed it and pass the required arguments, like number of tabs and content for each tab etc. It's a bit of work though, and I'd only consider it if I had a really large number of templates that used the tab control.

Meteor: Proper way to add 'confirm delete' modal

I want to create a confirm delete popup with Bootstrap 3. Is there any good comprehensive examples how to build one. I am very new to Meteor.
Use whatever example from Codrops, etc, just remember put the JSCode inside a
Template.nameTemplate.rendered = function() {}
So thats telling meteor to load that jscode, when the template has beed rendered and it can load any modal, etc...
So just follow whatever example you want, and just put whatever jQuery plugin etc, inside Rendered function
Also in some case the rendered its not enough, you need to use too,you can see timer docs here, anyways if you are having bad time, feel free to upload, some meteorPad, free nitrous box o repo on github and i can help you (i have a bad time with those modals on meteor to, they are a little trickys =p)
update answer
try to add meteor add iron:router, and on the client /app.js
Router.route('/', function () {
this.render('leaderboard');
});
And keep the same rendered like this.
Template.deleteBtn.rendered = function(){
$('.open-modal').on('click', function(e){
$('#confirm').modal()
.on('click', '#delete', function (e) {
// Remove selected player
Players.remove(Session.get("selectedPlayer"));
});
});
}
UPDATE
So using the peppelg:bootstrap-3-modalPackage, you can easy do the follow
First Create a template with the modal content
<template name="modal">
<!-- Modal Stuff -->
</template>
and easy call it on a Event handler.
Template.example.events({
'click #exampleButton':function(){
Modal.show('modal')
}
})
Now back to this example check this meteorpad from line 1-23 on app.'s and 41-62 on main.html

Template.myTemplate.rendered in Meteor?

What is the use of Template.myTemplate.rendered in Meteor?. I had studied the document but i didn't understand the exact functionality.The document says Provide a callback when an instance of a template is rendered. What is the meaning of instance of a template is rendered.Can you please suggest me with an example?
When the callback is fired, an instance of the template has been rendered (added to the document). It can be useful if you for example want to manipulate the elements in the template through the use of a JavaScript library, for example CodeMirror, as I used in one of my projects. For example:
<template name="test">
<textarea></textarea>
</template>
Template.test.rendered = function(){
// Make the textarea highlight the code (kind of).
var myCodeMirror = CodeMirror.fromTextArea(this.find('textarea'))
}

Resources