What's the correct way to perform DOM manipulation in Meteor? - meteor

I'm using a package called Timeline
https://github.com/VeriteCo/TimelineJS
It works by having a placeholder such as
<div id="my-timeline"></div>
And then ultimately making a jQuery call to manipulate the div. Visually I'm seeing the timeline appear on the screen and then disappearing almost straight away. A similar effect occurs with the following simple example:
<div id="my-temp"></div>
combined with
$(document).ready(function() {
$('#my-temp').html('HELLO');
});
What's the correct way to perform this type of manipulation without having the resultant HTML disappear?

I believe you should use constant regions to prevent Meteor from re-rendering your div.
{{#constant}}
<div id="my-temp"></div>
{{/constant}}

Use Handlebars template, don't use jQuery's dom manipulation. If you have to use javascript dom manipulation, make sure to use Meteor.render().
http://docs.meteor.com/#meteor_render
Also you need to call your timeline creation code inside Template.myTemplate.rendered
http://docs.meteor.com/#template_rendered

Meteor Render is the right way to go as mentioned by Harry. Here's the link to the new documentation
http://blazejs.org/api/templates.html#Template-onRendered

Related

Data-Link to CSS background-image using a helper function not render

I have a template which renders a background image to CSS within a <div> like this
style="background-image: url({{>~facilityImagePathDisplay(#data)}})"
Visual Studio doesn't like this and it's not a big deal but I wanted to see if there was a proper way to do this. I read through the documentation. Other stack questions and the below was created based off of the docs. This specifically links to all the docs Logic in JsViews css-tag
<div class="tile-image" data-link="css-background-image{:~facilityImagePathDisplay(#data)}" >
The above statement outputs exactly this to the div. <div class="tile-image" data-link="css-background-image{:~facilityImagePathDisplay(#data)}"> so it looks like it's not firing the helper function and rendering anything at all. Am I missing something?
It depends if you are running the link() method of JsViews, or simply the render() method of JsRender.
Your first version will works in both cases, but is simply rendering the CSS style.
The second version would only be used if you are running the link() method of JsViews - for data-binding etc. (So the background image could be dynamically updated if you data-binding triggers a new value).
For the data-link version, you need to makes sure the helper is returning a string that is not just "someUrl", but "url(someUrl)" - to provide the correct CSS data url syntax. In that case, the following should work:
data-link="css-background-image{:~facilityImagePathDisplay(#data)}"
Alternatively you can have the helper return just the "someUrl" string, but then include the "url(" and ")" strings in the template:
data-link="css-background-image{:'url(' + ~facilityImagePathDisplay(#data) + ')'}"

How to force Meteor's Blaze to batch changes applied to #each block

I have a template which is mostly equivalent to:
<div class="line">
{{#each part in line}}
<span class="part">{part}</span>
{{/each}}
</div>
where the line: string[] is a partition of a single line into one or more parts. The problem is that when the content of line changes, the template tries to match old elements with new elements (which is fine) and applies changes one by one (which is not fine) to the DOM. In particular if the old value of line was ["Hello","world"] and the new one is ["Hello world"], then there is a short period of time when the user is presented new value of line[0] combined with old value of line[1], which is ["Hello world","world"]. Most of the time this gets unnoticed, but in case when the parts are long enough compared to the screen width, it might happen that <span class="part">Hello world</span><span class="part">world</span> does not fit into a single line, which in turn causes the whole further content to be moved one line lower, only to be later moved again one line higher when line[1] gets finally removed.
One solution which I currently use is to replace the whole #each loop with custom helper {{{ helpMeRenderThisLine line }}} which builds the HTML string manually, but obviously this violates separation of concerns and my code style.
I'm new to Meteor, but these are the directions I would like to investigate:
Is there a way to render a loop non-reactively? I've heard there was
something like #constant block, but it is no longer supported.
Is there a way to "batch" the whole updating process into a single DOM
reflow?
Is there a way to use the momentum package to remove the
flicker?
Is there a way to use a smaller Blaze template to render the
single line in non-reactive fashion?
Is there a way to render a loop non-reactively? I've heard there was something like #constant block, but it is no longer supported.
It is pretty straightforward to make a part of the the DOM non reactive: you just need to use a non reactive helper. E.g. in you template rendered or created function, you can attach your string array to your template instance (not a cursor using this.myStrings = Collection.find() but an array using this.myStrings = Collection.find().fetch(), this being your template instance).
You then return it in your helper using return Template.instance().myStrings
Is there a way to "batch" the whole updating process into a single DOM reflow?
Actually, it depends on how you refresh the original data. The above solution should do that. Keep in mind that you can nest templates to control the granularity of the DOM refreshes. Also make sure you control your subscriptions in case they are overlapping with the same published content. You can wait for every one to be ready using Template.subscribtionReady() (template level) or subscribtion.ready() (sub level)
Is there a way to use the momentum package to remove the flicker?
The flicker should go away if you refresh properly the data.
Is there a way to use a smaller Blaze template to render the single line in non-reactive fashion?
Not sure about what you ask here but a good practice would be to try to make component level templates, it might help in fine tuning the DOM rendering.
<div class="line">
{{#each part in line}}
{{> Part}}
{{/each}}
</div>

How to do page reveal animations with IronRouter?

I'm using meteor & IronRouter, and trying to do some javascript-driven animations when the template is ready/rendered.
However, the IR hook events don't seem to fire when they should.
onBeforeAction and onAfterAction only seem to fire before the new page is rendered.
So there is no updated DOM to animate.
I also tried overriding the action, calling render myself, and then animating.
however, the DOM is still not updated... even using Meteor.defer
action: ->
this.render() # works ok
Meteor.defer ->
Template.SceneView.animateScene()
This was still getting called while the old template was present. I guess since Meteor is still updating async-ly, and so defer() didn't really defer...
So a hack around this is to call the animate function from the template itself, and then also use Meteor.defer to call the animation, so the DOM is updated.
The Blaze rendered() hook only fires once on template creation.
Separately there are some new ui_hooks in Blaze, but these seem to be before insert or deletes happen. I guess I could use this and take over the whole DOM manipulation but that seems like overkill for just playing some animations when a page is ready...
https://github.com/percolatestudio/transition-helper/blob/master/transition-helper.js
UPDATE. I found out about the afterFlush event
So it seems that having reactive templates, somehow makes defer timing not work.
Instead if I use the following, the DOM is all updated in time for the animation trigger.
action: ->
this.render()
Deps.afterFlush ->
Template.SceneView.animateScene()
You could also use plain css, if you have a package like animate.css in your app (http://daneden.github.io/animate.css/)
If you have this class animated bounceIn on the div or element thats at the top of your template it will also display an animation when the template comes into view.

JQuery load() function disables links and :hover effects?

I'm trying to load content from a different file into a div element within the current file using the jQuery load() function. Nothing fancy, just loading it and that's it. However the links that are contained in the loaded file become "disabled", you cannot click them, and pseudo-classes like :hover seem to be left out as well. Is there a solution to this?
$(document).ready(function() {
$("div.content").load("content.html");
});
let's say content.html contains just this line:
xxx
When it is loaded into the <div class="content"> the link is not clickable. It is colored according to the css, however the :hover effect doesn't work, and it behaves like normal text - not a link. This is a problem because the content I'm trying to load has a couple of links, and none of them work after being load()'ed.
I believe your issue is:
You use $('div.content').load('content.html') to send a request for content to (later) be inserted into the DOM.
You then run some code to specify handlers for nodes using $(document).click, $(document).bind etc - but this code runs before the new nodes have been added to the DOM.
New nodes are then added when the .load call completes.
The behaviour that you defined on all the origional nodes isn't being followed on the new nodes.
If that is the issue your're describing - then you need to add all the same bindings to the new nodes once they're created.
i.e. you need to provide a callback to add the bindings to the new elements:
function on_data_loaded() {
$('div.content ...').hover(.....);
// etc.
}
$('div.content').load('content.html', null, onloaded);
(note that's not a particularly clean way of doing it, but it should explain what needs to be done).

UpdatePanel - Any ideas on how to avoid a flicker in UI? - ASP.NET/Jquery

I have rather a complex UI. However, for the purpose of this question, let's say that there is a HTML table that renders UILayout1 by default (say default mode). There is a button that a user can use to toggle between the default mode and a preview mode (UILayout2)
When in preview mode, there are some columns in the table that are invisible and there are reordering of rows. I am using JS (jquery) on load to check the mode and change it accordingly.
The table and the toggle button are in UpdatePanels.
Functionally, everything works as expected. However, when a user toggles between default and preview mode or vice versa, there is this short time interval in which the the table renders in default and then JS runs to make changes.
This results in degraded UI experience. Are there any creative ways to avoid this "flicker"?
you can use DIVs or don't use update panel in your UI generation use any concept else
The problem is likely to be that your code is running on load. I'm assuming that you're doing this using the standard jQuery method of running code on load, and not using the window's onload event. In any case, even using jQuerys $(document).ready(...) will be too slow if you have a lot of other javascript files to load, as the .ready event isn't fired on the document until all javascript includes have loaded.
You should be able to work around the issue by including your code that modifies the table just after the html for the table in your page and not running it on load i.e. make sure you don't wrap it in $(document).ready(...);
For this approach to work, you will need to have all javascript required by the code which is modifying the table included earlier in the page.
If you have other non-essential javascript files included, you should try to include them later in the page.
I'm not 100% sure how being inside an update panel will affect it - you will need to make sure that your code is being re-triggered when the updatepanel updates, but I believe this should all happen automatically.
Presumably your UI is controlled by CSS? You might be able to get rid of the flickering by adding something like this at the start of your JavaScript or in the <head> of your HTML:
if (previewMode) {
document.documentElement.className = 'preview';
}
Then if you modify your CSS rules that apply to your preview mode to reflect the HTML element having the class="preview" to something like:
.preview table .defaultMode {
display:none;
}
hopefully your table should render correctly first time and will not need to be re-drawn.

Resources