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)
Related
On a single page, I'd like to display a list of editors and grouped under each editor, the list of books from this editor. Each editor have many books, and I've many editors, and the editors must be ordered using a score derived by the books associated to this editor. E.g by the first editor is the editor is most popular books (and I this is a completely different problem that I suspect I can solve with some mongodb magic aggregation. see link for associated question at the end).
I want to have a button for "more editors", and one for "more books" under each editor. I've two collections, one for editors and one for books with a link back to the editors and I've setup two subscriptions, one for each with limits. Basically my template does
<template name="onepage">
{{#each editor in AllEditors}}
{{editor.name}}
{{> bookList editor }}
{{/each}}
<button>more editors</button>
</template>
<template name="bookList">
{{#each book in AllBooks}}
{{book.name}}
{{/each}
<button>more books</button>
</template>
The problem is that clicking on "more editors" my entire page re-render.
The new editor is correctly added at the end of the list, but the result is not very user friendly. I can see the problem. Requesting one more editor (using a subscription and a limit) I force to recompute the AllEditors variable, that force to recompute each BookList template. When I ask for more books, the new book is correctly added without flickering as I ask blaze to just add one element and it is smart enough to avoid re-rendering the entire template.
How can I restructure these two templates to avoid this problem ?
Update:
this is not the actual code I'm using, but this is the main idea for the onCreated and helpers functions
Template.onepage.onCreated () ->
template = this
template.limit = new ReactiveVar(10)
template.autorun () ->
limit = template.limit.get()
template.subscribe("editors",limit)
Template.onepage.helpers
'allEditors': () ->
template = Template.instance()
limit = template.limit.get()
Editors.find({},{}, {limit:limit})
And similarly for the book template
Associated question : sort mongo collection based on the score of child documents
When I do something like this, I do not separate into different templates. I use a template helper with a parent/child relationship.
Template.page.helpers({
groups() {
return ...
},
items(parent) {
if(parent) {
return ...
} else {
return null;
}
}
});
Simple example in html:
<div>
{{#each groups}}
{{this.groupname}}
{{#each items this}}
{{_id}}
{{/each}}
{{/each}}
</div>
Just make sure the groupname matches a field in items, so they can be grouped together. Start with an initial limit, then with a click event, expand that limit. This will cause the editors list to grow without the page refreshing. I use this method in quite a few areas with success.
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.
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.
On Meteor, I'd like to prefetch data. I know this is normally automatically done. But there is a small wait for some action.
For example, I put a jquery event and change a session variable. Minimongo don't have that datas and have to fetch them.
I'd like to make an animation during which I'm fetching data and at the end of the animation change the templates.
Is it possible? How mould you do that?
Thanks in advance.
The only way minimongo won't have your data is when your subscription changes. So you could do
Edit: Clearing it up
Meteor.autosubscribe(function() {
//ADD LOADING TO PAGE HERE (1)
page = Session.get('paging')
Session.set('Sess1', true)
Meteor.subscribe('testdata', page, function() {
//remove loading to page here (2)
Session.set('Sess1', false)
});
});
For the template it doesn't need to change. You could set a Session at (1) and set another at (2).
So
<template name="test">
{{#if Sess1}}
might be loading here
{{else}}
display the data here
{{/if}}
</template>
Currently meteor supports a limited number of events that we can react to from our template definitions. I would like a way to react to events beyond this predefined list. I want the freedom to add any event, even custom events, to the list of possible events in a template.
One idea I had would be to set up a jquery event handler somewhere that listens for the unsupported event and have it set a session variable:
$(form).submit( ->
Session.set('formSubmitted', true)
And then use that session variable when rendering a template:
Template.confirmation.submitted = ->
return Session.get('formSubmitted')
<template name="confirmation">
{{#if submitted}}
<!-- do whatever -->
{{/if}}
</template>
But this is just a workaround and doesn't really address the issue. Is there a real Meteor-way of doing this? Is this something I can do with the new Spark implementations?
NOTE: Please ignore the fact that I'm using the submit event here. I know I can just bind a click event to the submit button, but that's beside the point.
NOTE 2: The accepted answer to this question is also just a workaround.
The rendered callback is what I use to do this.
http://docs.meteor.com/#template_rendered
The callback gives you template instance you should use to find the dom elements you need: http://docs.meteor.com/#template_inst
Untested example below ;)
Template.foo.rendered = ->
$(this.find("form")).submit ->
Session.set 'formSubmitted', true
Using a Session variable than to switch the view is a matter of taste I think.
I have an app State stored in the Session, that toggles Templates. Additionally the backbone package is very useful to provide some meaningful urls.