I'm implementing an infinite scroll on Meteor in order to display a grid of pictures linked to a large collection.
When the user is at the end of the page I subscribe to more elements and I increase the number of pictures displayed (through my template.helper).
//SERVER
Meteor.publish('generalMusics', function(limit){
return Musics.find({}, {limit: limit});
});
//CLIENT: when the template is created and when the limit of data increases
//it subscribes again
Template.t_elementSubHeader.onCreated( function() {
Session.set('reqLimit',50);
var self = this;
//Everytime reqLimit changes I redo the subscribe
this.autorun(function(){
self.subscribe('generalMusics', Session.get('reqLimit'));
});
//CLIENT: more elements are sent to the template when reqLimit increases
Template.t_elementSubHeader.helpers({
data: function() {
return Musics.find({}, {limit : Session.get('reqLimit')});
}
});
//Also on client, when the user reach the bottom of the page
Session.set('reqLimit',Session.get('reqLimit')+50);
It works well but all the template elements are re-rendering and it also takes some time to do so. It's very inconvenient for the user, I think it takes time because I'm displaying pictures and not text (we already compress the pictures to a minimum size).
The problem is due to the subscribe that rerender all the template elements.
How can I do to just add the new elements and prevent the re-rendering of the already displayed elements when I subscribe ?
My app will be on mobile devices so I can't subscribe to a lot of elements and then just increase the limit in the template helper.
Finally I got it, i've added some code in the html to wait the subscription to be ready and I forgot about it.
I removed:
{{#if Template.subscriptionsReady}}
{{> Template.dynamic template="t_elementList" data=tabData}}
{{/if}}
Infinite scroll is working like a charm.
Related
I am using polymer and firebase to create a web app. To get a list in the database, I am using the "firebase-query" element of polymerfire.
Now, the list of items I am getting from the database is posts that people are posting on my app. Now I have the firebase-query element like this:
<firebase-query
id="query"
path="/posts"
limit-to-last="15"
data="{{posts}}">
</firebase-query>
and I have dom-repeat template tag to display the posts like this
<template is="dom-repeat" items="[[posts]]" as="post">
<div>[[post.content]]</div>
</template>
Now, if I have bunch of people posting a lot of contents at once, this list would be updating every second, and it would be hard to read.
Is there a way to prevent firebase-query element to stop updating once the initial load is complete, and possibly updating again when I call a certain function?
I tried to add a disabled attribute, but that just deletes whatever is inside the data attribute ({{posts}} in my case), so when disabled is set, nothing shows up on the screen.
I am not familiar with firebase-query, but you can do simple trick. On property posts set observer function.
posts: {
Type: Array,
value: function(){return []},
observer: "debouncePosts"
}
then in js you can define debounce function where I set, for example, 10 000 miliseconds (10 seconds) and called some callback function where I set this.posts to currentPosts property which will be rendered in template.
debouncePosts: function(data) {
this.debounce("debouncePost", function(){
this.set("currentPosts", this.posts);
}.bind(this), 10000)
}
and html template will be like:
<template is="dom-repeat" items="[[currentPosts]]" as="post">
<div>[[post.content]]</div>
</template>
documentation to debounce: https://www.polymer-project.org/1.0/docs/api/Polymer.Base#method-debounce
If you like to do it in different way like update data whenever user presses update, it's really easy now for you. Just set on-tap on some button and then in function you will do something like this.set("currentPosts", this.posts)
Trying to do an infinite scroll page that displays elements as the user scrolls.
So each time I detect that the scroll reaches the end of the page I call
this.recordLimit += 10;
this.subscribe('movements', {limit: this.recordLimit});
and that triggeres (autorun)
this.autorun(h => {
if (this.ready()) {
this.items = Items.find(<potential limit filter here too>);
}
All right. That is working. However each time this.items = Items.find(); is called, the user's browser is scrolled back up to the top.
Potentially this is because the dom elements are removed, the scroll is reset, and then the elements are added again without restoring the previous scroll position.
What am I doing wrong ?
Examples where it is 'apparenty' working:
https://github.com/barbatus/ng-infinite-scroll/blob/master/scroll-controller.js
https://github.com/abecks/meteor-infinite-scroll
http://meteorpedia.com/read/Infinite_Scrolling
############ Edit #############
Actually, I noticed that, putting after the Items.find() a h.stop() to stop the subscription, this works... I guess the previous mango cursor is updated with the last subscription limit.
However I am still not able to understand why this was repainting everything in the initial case..
I believe the problem is you're finding the documents again as you guessed. You should only subscribe to your publication in the autorun. Check this from Angular2 & Meteor tutorial which explains pub/sub pretty well.
In the autorun, it will rerun the find() and re-render all the documents which is why you need to rerun only the subscription in autorun for your case. Because of how pub/sub and observers work, since the only thing you change is the "limit" in your function and the rest is the same, your publish will only return the new documents, and keep the previously returned ones. The find() query on the client side will fetch the documents returned from the pub/sub and it won't rerender the already fetched documents when the amount of documents change.
I am following this example https://kadira.io/academy/meteor-routing-guide/content/rendering-blaze-templates
When I click on my links the whole page is being reloaded. Is there any way to load only the template part that is needed and not the whole page?
Edit: Also I noted another problem. Everything that is outside {{> Template.dynamic}} is being rendered twice.
Here is my project sample. https://github.com/hayk94/UbMvp/tree/routing
EDIT: Putting the contents in the mainLayout template and starting the rendering from there fixed the double render problems. However the reload problems happen because of this code
Template.mainLayout.events({
"click *": function(event, template){
event.stopPropagation();
console.log('body all click log');
// console.log(c0nnIp);
var clickedOne = $(event.target).html().toString();
console.log('This click ' + clickedOne);
//getting the connID
var clientIp = null // headers.getClientIP(); // no need for this anymore
var clientConnId = Meteor.connection._lastSessionId;
console.log(clientIp);
console.log(clientConnId);
Meteor.call("updateDB", {clientIp,clientConnId,clickedOne}, function(error, result){
if(error){
console.log("error", error);
}
if(result){
}
});
}, // click *
});//events
Without this event attached to the template the routing works without any reloads, however as soon as I attach it the problem persists.
Do you have any ideas why this code causes such problems?
EDIT 2 following question Rev 3:
event.stopPropagation() on "click *" event probably prevents the router from intercepting the click on link.
Then your browser performs the default behaviour, i.e. navigates to that link, reloading the whole page.
EDIT following question Rev 2:
Not sure you can directly use your body as BlazeLayout target layout.
Notice in the first code sample of BlazeLayout Usage that they use an actual template as layout (<template name="layout1">), targeted in JS as BlazeLayout.render('layout1', {});.
In the tutorial you mention, they similarly use <template name="mainLayout">.
That layout template is then appended to your page's body and filled accordingly. You can also change the placeholder for that layout with BlazeLayout.setRoot() by the way.
But strange things may happen if you try to directly target the body? In particular, that may explain why you have content rendered twice.
Original answer:
If your page is actually reloaded, then your router might not be configured properly, as your link is not being intercepted and your browser makes you actually navigate to that page. In that case, we would need to see your actual code if you need further help.
In case your page does not actually reload, but only your whole content is changed (whereas you wanted to change just a part of it), then you should make sure you properly point your dynamic templates.
You can refer to kadira:blaze-layout package doc to see how you set up different dynamic template targets in your layout, and how you can change each of them separately (or several of them simultaneously).
You should have something similar in case you use kadira:react-layout package.
I am currently using EasySearch for my search solution for my app and I have a question on pub/sub of the search results.
Basically, the way the search works is that users will put into a search and a number of posts will be returned (I have a collections called Posts where I implemented the EasySearch).
I assumed EasySearch probably will automatically publish or subscribe depending on the results of the search queries, so I don't think I should worry about sending too many data to the client? (Correct if I am wrong).
However, the problem that I am having at the moment is that each posts is associated with a image from a collections called Images where my pub/sub is simply publish all and subscribe all from client and server and not related to the search at all.
I am just wondering does that mean I won't able to scale since I will be publishing every images to client regardless of the searches?
Post collection
Posts = new Mongo.Collection('posts');
Posts.initEasySearch(['firstName', 'lastName', 'degreeStudy', 'tags'], {
'limit' : 20,
'use' : 'mongo-db'
});
Posts.allow({
update: function(userId, post) { return ownsDocument(userId, post); },
remove: function(userId, post) { return ownsDocument(userId, post); },
});
Image collection
Images = new FS.Collection("images", {
stores: [new FS.Store.GridFS("images")]
});
Template page for showing Search results
<template name="postPage">
<div class="container">
{{#ifEsHasNoResults index="posts"}}
<div class="jumbotron no-results"> <h1>No results found!</h1></div>
{{/ifEsHasNoResults}}
{{#ifEsIsSearching index="posts"}}
{{>loading}}
{{else}}
{{#esEach index="posts"}}
{{> postItem}}
{{/esEach}}
{{> esLoadMoreButton index="posts"}}
{{/ifEsIsSearching}}
</div>
</template>
In short, how can I make my pub and sub of the images collections related to the easySearch result?
I think you are confusing the cursor returned by your publication with the data itself. The cursor is like a way to make dynamic queries without having to load all your data (unless you do load all of them using for example an {{# each images}}). Read that for more info
This being said (no scaling issues ahead), I assume that inside your {{> postItem}} template, you have an image helper fetching the related image in your image collection.
This will work but keep this in mind: your user can open his console and get any image in your Images collection simply by doing an Images.find() related command. So it should be ok as long as you don't have private content. However if you do, you may want to use a method to fetch your image instead of publishing/exposing the whole collection.
I have a Meteor app for storing my bookmarks. To do that there is a Links collection with a count attribute. Every time I click the bookmark, the external site must be visited, but also the count must be increased with 1, so I can sort the bookmarks with the most visited at the top.
So now I have a link with the info in it:
{{title}}
Should I use a template instead, update the count and do a Router.go to the external site?
A little modification:
{{title}}
Then you could use:
Template.body.events({
'click a.tracked' : function(e) {
var href = $(e.currentTarget).attr('href');
ClickedLinks.insert({href: href, when: new Date()});
}
});
Where ClickedLinks is a collection. The main idea is that we track all click events on meteor body as a catch-all since everything is rendered in the body anyway even if it's in another template.. everything still winds up getting rendered there. Then we try to depend on events propagation to do our magic.
Here it is in action:
http://meteorpad.com/pad/Wtz6autqgCDEaJTLw/Leaderboard