I have some templates that look roughly like this:
<template name="items">
<div class="item-list">
{{#each items}}
{{> item}}
{{/each}}
<div>
{{> loadMore}}
</template>
<template name="item">
<div class="item" id="{{unique_string}}">
<!-- stuff here -->
</div>
</template>
<template name="loadMore">
Load more...
</template>
With associated javascript:
Template.items.items = function() {
return Items.find({}, {limit: Session.get("itemCount")});
}
Template.loadMore.events({
"click": function() {
Session.set("itemCount", Session.get("itemCount") + 10);
}
})
All that together more-or-less gives me something that pretty much works like an infinite scrolling section. (The actual code has a few more moving parts, but this is the important bit.)
Whenever I click on loadMore, though, it both pulls more data down and scrolls me back to the top of the page, rather defeating the purpose of infinite scroll. I can throw in some javascript to scroll back down to where it should be, but that leaves a nasty flicker as the page hops around quicly.
I've tried using preserve on the entire list as well as on each item div to keep them from getting updated, but that didn't seem to stop the scrolling. I've also tried putting {{#isolate}} blocks around just about any and everything, without any luck.
Is there something I can do here to make the page not scroll around while it re-renders? Composing templates differently? Some aspect of preserve or {{#isolate}} that I've missed?
The page scrolls to top because your
Load more... will make the page scroll to top. When your href links to "#" the page will scroll to the DOM element with #"element id". Clicking a link with only "#" will scroll to top.
You have two options:
Prevent the default behaviour on the click event (easy option):
Template.loadMore.events({
"click": function(event) {
event.preventDefault();
Session.set("itemCount", Session.get("itemCount") + 10);
} })
This will stop the page reload
Even better: make the Load more... link to "#{{_id}}" then the page will automatically scroll to the element with the id you provided. This will require some restructuring of the templates and maybe a helper method in the template to give you the id of the last item. But it will make your page load exactly where you want.
Related
I have included the page page loader script into my project with cakephp.I can see that it is loading as in body tag I can see the 'pace-running' and 'pace-done' class names.
As soon as I add this
$(function() {
Pace.on("done", function(){
alert('done');
// $("#pageToLoad").fadeIn(1000);
});
});
I get the alert and I can see that the div added from pace is active:
<div class="pace pace-active">
<div class="pace-progress" style="transform: translate3d(100%, 0px, 0px);" data-progress-text="100%" data-progress="99">
<div class="pace-progress-inner"></div>
</div>
<div class="pace-activity"></div>
</div>
which shoes me, everything is included well and works well.
But for some reason, I can't see the loading animation on my page.
I changed the z-index within the css template file of pace to '99999999' but the progress bar will not display :-(
There is a bug report regarding this. The recommended solution is:
$(document).ajaxStart(function() { Pace.restart(); });
I need something along these lines in one of my projects, but not in another, and I'm not sure what the difference is. :-(
I actually looking for a good way to play animations on my app context with Blaze.
To be more explicit, I wrote this super simple example:
<template name="global">
<h1>Hi guys!</h1>
{{> foo}}
</template>
<template name="foo">
<h2>I'm a foo!</h2>
<ul>
{{#each elements}}
{{> bar}}
}}
</ul>
<button name="btnAdd">Add new elem</button>
<button name="btnDel">Delete an elem</button>
</template>
<template name="bar">
<li>{{name}}</li>
</template>
Let's assume we got an Iron-router route which render the global Template.
On this particular render (from "navigate") I want each templates to render with fadeIn.
When click on btnAdd button, a new element created. I wish it would render with SlideInLeft effect.
When click on btnDel button, an element is deleted. I wish it would be destroyed with SlideOutRight effect.
When user navigate to another route, I want all template disappear with fadeOut effect.
Every of my attempt so far wouldn't allow me to do this kind of distinction... I couldn't find any package resolving this problem neither.
I'm actually just playing animation by adding/removing Animate.css class (pretty simple to use and good looking!)
To resume, I want a different animation played depending on the source of the rendering.
Does someone had already face this problem?
BONUS QUESTION: Do you know how to chain animations, like:
render global with fadeIn Effect >> then >> render foo with rotateIn Effect >> then >> render every bar with bounceIn effect
For timing, you can use Meteor.setInterval. For example, you can do $('.elementClass').hide('fast') outside setInterval. It will run first and setInterval will run when you want it to.
For the initial effects, you can use:
Template.templateName.onRendered(function(){
$('.elementClass').fadeIn('fast') //note that element is initially hidden (display:none in CSS). you can use effects from jquery and jquery-ui for more effects. You have to add jquery-ui additionally
})
You can also use jQuery in your router.js, using iron:router.
Router.route('/the-url', function() {
this.render('templateName', {
data: function () {
$('.htmlElement').toggle('slide', {direction:'right'}, 200); //note that the element is initially invisible
}
});
}, {
name: 'routeName'
});
I have list of ~150 entries with various filters/sorts.
By default when I have 10 shown response times are OK (it is not slow JS code, as the limiting to 10 is done as last step).
But when I hit expand button it takes 2s to show all entries.
And same time to apply filters/sort etc.
The helper used in {{#each}} is ReactiveVar which returns array.
I want to show spinner before re-rendering of #each starts and turn it off after it finishes.
Something like this pseudocode if it waits for every step to finish and also update DOM.
Tracker.autorun ->
spinner.set(true)
showFeed.set(resultFeed.get())
spinner.set(false)
But if I dont use Meteor.setTimeout to defer that showFeed.set, the spinner is never shown. I dont want to rely on setTimeout. But I dont understand whole flush() afterFlush() enough to pull this off. How can I time it that it waits to finish every of these steps (also with DOM update) before proceeding to next ?
I'm not sure about how to show spinner while DOM is updating, but I would like to suggest you another option: you can improve DOM updates performance by hiding/showing DOM nodes instead of creating/destroying them.
Let me show an example:
Expensive DOM updates (create/destroy):
HTML:
<template name="items">
{{#each filteredItems as item}}
<div class="item">
{{item.content}}
</div>
{{/each}}
</template>
COFFEE:
Template.helpers.items
filteredItems: -> Session.get('filteredItems')
onFiltering ->
Session.set('filteredItems', computeFilteredItems());
Why is this expensieve? Because everytime you do filtering, Blaze will create/destroy DOM nodes and this operation is rather expensieve.
Cheap DOM updates (show/hide):
HTML:
<template name="items">
{{#each allItems as item}}
<div class="item {{#if isItemVisible item}}item--visible{{/if}}">
{{item.content}}
</div>
{{/each}}
</template>
COFFEE:
Template.helpers.items
allItems: -> allItems
isItemVisible: -> Session.get('filteredItemsIds').indexOf(item._id) !== -1
onFiltering ->
Session.set('filteredItemsIds', computeFilteredItems().map((item) -> item._id))
CSS:
.item {
display: none;
}
.item--visible {
display: block;
}
Why is it cheap? Because no DOM nodes will be created/destroyed dynamically, all required nodes will be created on the first rendering. We just add/remove CSS classes dynamically and this operation is way more cheaper than create/destroy.
P.S. I have not perfomed any tests, so I'm not 100% sure, but theretically it should work.
UPDATE:
Here's a fiddle. It works a little better than my own real app, but still highlights a potential bug (or my misunderstanding). Notice that the "outro" transition doesn't work.
http://jsfiddle.net/k4a81fza/1/
Original:
This is a partial I'm using inside of a parent Ractive:
<script id="session_tpl" type="text/ractive">
<div>
<a href="#" on-tap="showDetail">
{{#if p.project !== null}}
<p intro-outro="fly">
{{project}}
</p>
{{/if}}
</a>
</div>
</script>
Here's how I'm trying to then update the data (which is changed from a different ractive that represents a detail view):
daysRac.set('days[1].sessions[2].project', null);
The <p> tag in in the template successfully disappears, but without the transition. I've tried other transitions and tweaked duration and delay, but it always is just instantly removed.
Ideally I want different outro and intro transitions, which I thought I could achieve with something like this:
daysRac.set(keypathToProject, null, function(){
daysRac.set(keypathToProject, "The New Value");
});
Again, that works to update the project value displayed in the <p>, but without transitions.
Is there a way to accomplish what I'm after?
This is probably a bug, I submitted an issue on GitHub.
The problem is that Ractive updates {{description}} to null before the transition starts. It works correctly if you don't use an expression, i.e. if you change {{#if description !== null}} to {{#if describtion}}.
Meteor seems to skip CSS transitions when these are triggered through a template helper.
Is there a way to work around this?
Example:
<template name="example-template">
<div class="example {{myhelper}}"></div>
</template>
Then, "myhelper" would get assigned, through a template helper, a classname that triggers a css transition. But, for some reason, the class is applied but skipping the transition.
I assume this conflicts with Meteor's auto-rendering when the template data sources change, but I don't know how to get around it (I'd like to avoid using jquery for this).
CSS transitions after rendering a new template aren't yet supported by Meteor. The reason is that when rendering the template example-template again, the new HTML is just appended to the DOM with the new classname. Since the DOM changes, the transition doesn't happen.
Your best bet is to use the rendered event in combination with a loading classname:
<template name="example-template">
<div class="example loading"></div>
</template>
Template['example-template'].rendered = function() {
// remove the loading classname here, and have that trigger a transition
}
This is supposed to get easier after new Meteor UI lands (see http://www.youtube.com/watch?v=pGQ-ax5cFnk), but until then you can do this with a preserve directive for your template:
HTML:
<template name="example">
<div id="example-div" class="example {{myhelper}}"></div>
</template>
JS:
Template.example.preserve(['#example-div']);
See http://docs.meteor.com/#template_preserve for more info.