I have a note taking app which allows users to add notes. I am trying to render newly created notes on the home page, but when I go to the add note and add a note, then go back to the home page, it shows the new notes(limited to 10) but the new note that was just created is at the bottom of the list, instead of the top. When I refresh the page, it goes to the top like I want it to. How would I get it so that I don't have to refresh the page to get the newly created note at the top?
I try to sort by using a timestamp, (using Date.parse(new Date())), and then publishing from server using this,
Meteor.publish('notes-newest', function () {
return Notes.find({},{sort: {createdAt: -1}, limit: 10});
});
Here is where I am trying to render it:
componentDidMount() {
Meteor.subscribe('notes-newest')
this.tracker = Tracker.autorun(() => {
const notes = Notes.find().fetch()
if(notes == null || notes == undefined){
return;
}
this.setState({ notes });
console.log(notes);
})
}
You need to do the sort on the client as well.
this.tracker = Tracker.autorun(() => {
const notes = Notes.find({},{sort: {createdAt: -1}).fetch()
If still you dont get the data as you want then you can try below code
this.tracker = Tracker.autorun(() => {
const notes = Notes.find({},{sort: {createdAt: -1}).fetch()
var data = notes.reverse()
You will get your data in descending order while displaying
Related
I try to use reactive vars to filter the published data, on a data list page, users can pickup 2 dates to show the data created during the dates; all works great until i got few computers to access this page at same time; the data changes would not push to every computers automatically- only the computer which makes the change has the new data listed. other computers have to refresh the page manually to see the new data or updated data.
if i remove the reactive vars in the sub/pub, all are good - if one computer changes the data, all computers get the new data immediately and automatically.
i even put the date filter to the helper - still same - no DDP push, same as in the sub/pub.
any ideas? any input are very appreciated.v
sub
Template.TestRV.onCreated(function () {
this.startDate = new ReactiveVar({});
this.endDate =new ReactiveVar({});
var sDate = new Date(new Date().setDate(new Date().getDate() - 30));
var eDate = new Date();
//show last 30 days data by default
this.startDate.set(sDate);
this.endDate.set(eDate);
this.autorun(() => {
this.subscribe('shipAllRV',this.startDate.get(),this.endDate.get());
});
//this.autorun(() => {
//this.subscribe('shipAll');
//});
});
Template.TestRV.helpers({
testListRV: function () {
let start = Template.instance().startDate.get();
let end = Template.instance().endDate.get();
return SHIP.find(
{createdAt: { $lte: end, $gte: start }},
{ sort: { createdAt: -1 } }
);
},
testList: function(){
return SHIP.find({},{ sort: { createdAt:-1} });
}
});
pub -SHIP is my collection
Meteor.publish('shipAll', function() {
return SHIP.find({});
});
Meteor.publish('shipAllRV', function(startDate,endDate) {
return SHIP.find({createdAt:{$lte:endDate,$gte:startDate}},{ sort: { createdAt: -1 } });
});
BTW,
1. i tried session variable is same;
2. if i don’t update the createdAt field in my SHIP.update method, it seems all good even the reactive vars in sub/pub
how can i do it correctly? i need the date filter and DDP push. thanks
Robin
I've got what should be a relatively simple issue. I set a session, then a subscribe to a collection using the string stored in the session. But when that session changes, I need to clear the subscription data and start again.
My code is as follows:
let subscriptionReady;
let filteredResults = [];
let rawResults = [];
let county = Session.get('county');
let type = Session.get('type');
This is mostly just prep work to create some empty objects to populate later. This all gets set on a click event. After we set these placeholder objects we go and subscribe by those sessions:
if (county && !type) {
return function() {
if (subscriptionReady) {
subscriptionReady.stop();
}
filteredResults = [];
rawResults = [];
subscriptionReady = Meteor.subscribe('resourcesearch', county, {
onReady: () => {
rawResults = resourceCollection.find({}, { sort: {score: -1} }).fetch();
rawResults.forEach((result) => {
if (result.score) {
filteredResults.push(result);
}
});
}
});
}
At the third line I run a check to see if subscriptionReady exists, then it will have the stop method available. So then I run it. But, it doesn't actually stop anything.
What am I missing?
After trial and error, I've got it solved. The issue was the placement of the stop call. I no longer have to check if subscriptionReady exists, instead I stop the subscription inside of the onReady method:
return function() {
filteredResults = [];
rawResults = [];
subscriptionReady = Meteor.subscribe('resourcesearch', county, {
onReady: () => {
rawResults = resourceCollection.find({}, { sort: {score: -1} }).fetch();
rawResults.forEach((result) => {
if (result.score) {
filteredResults.push(result);
}
});
subscriptionReady.stop();
}
});
It's .stop() not .stop docs
Also you can probably avoid your filtering loop by including score in your query. Are you looking for documents where the score key exists {score: {$exists: true}} or just where it is non zero {$score: {$ne: 0}}?
Also you shouldn't need to clear the subscription and start again. If you make your subscription parameter resourcesearch a reactive data source then the subscription will automatically update to give you the documents you need. Starting/stopping a subscription in response to a search would be an anti-pattern.
I've got one view displaying some pictures published by users with some data (let's image Instagram).
I already have these pictures as non-reactive data (otherwise you could see many updates) but these images have one button to like the picture. If I have this as non-reactive data I can't see when I click on "Like" the filled heart (I need to refresh).
This is my subscribe function:
this.subscribe('food', () => [{
limit: parseInt(this.getReactively('perPage')),
//skip: parseInt((this.getReactively('page') - 1) * this.perPage),
sort: this.getReactively('sort')
}, this.getReactively('filters'), this.getReactively('searchText'), this.getReactively('user.following')
]);
And this is my helper:
food() {
const food = Food.find({}, {reactive: true}, {
sort: this.sort
}).fetch().map(food => {
const owner = Meteor.users.findOne(food.owner, {fields: {username: 1, avatarS: 1, following: 1}});
food.avatarS = owner && owner.avatarS;
food.username = owner && owner.username;
if (food.likes.indexOf(Meteor.userId()) == -1) {
// user did not like this plate
food.liked = false;
} else {
// user liked this plate
food.liked = true;
}
return food;
});
}
Is possible to have a non-reactive model but with some reactive properties on it?
I'm using Angular 1.X with TS btw
Thanks in advance!
PS: is it normal that this works as non-reactive when I change reactive to true?
Modification to your code:
//console.log(food.likes);
this.subscribe('reactiveFoodData', {ownerId: food.owner, userId: Meteor.userId()}).subscribe(()=>{
console.log(this.user);
});
// THIS IS THE PUBLISH METHOD LOCATED IN THE SERVER SIDE:
Meteor.publish('reactiveFoodData', function(params: {ownerId:string, userId:string) {
const owner = Meteor.users.findOne(params.ownerId);
if (!owner) {
throw new Meteor.Error('404', 'Owner does not exist');
}
let result = {};
result.avatarS = owner.avatarS;
result.username = owner.username;
const food = Food.find({});
result.liked = !(food.likes.indexOf(params.userId) == -1);
return result;
});
You have few problems:
1. The reactive flag is true by default, you do not need to set it.
2. The function find is accepting only two arguments, not 3.
Should be:
const food = Food.find({}, {reactive: true, sort: this.sort})
If you need some, subset of data to be reactive only (from some collection). You could create a specific Method (which udpates only "likes").
https://guide.meteor.com/methods.html
UPDATE:
Here is how you write a method with return parameter (check two examples, with Future and without):
How to invoke a function in Meteor.methods and return the value
UPDATE2:
You have lost reactivity when you used fetch(). Because you moved from reactive cursor to just simple array over which you map values. Do not expect reactivity after fetch(). If you want fetch or do not want to use Cursors, you could wrap the find inside Tracker.autorun(()=>{}) or utilize publish/subscribe.
Note: But be careful, if you somehow manage to get "empty" cursor in find(), your Tracker.autorun will stop react reactively. Autorun works only if it has something to watch over.
The main point with method, is that if you want to have one time non-reactive action for something. You define the method on server:
Meteor.methods({
myMethod: ()=> {
return "hello";
}
});
And you can call it from client with:
Meteor.call('myMethod', (error, result) => {
console.log(result); // "hello"
});
Instead of working with pure collections. You could start using publish/subscribe. On server you publish 'likes' and on client you just listens to this new reactive view. E.g.,
Meteor.publish('likes', (options: {owner: string, likes: Array<any>}) => {
let result: any = {}
const owner = Meteor.users.findOne(options.owner, username: 1, avatarS: 1, following: 1}});
result.avatarS = options.owner && options.owner.avatarS;
result.username = options.owner && options.owner.username;
result.liked = !(options.likes.indexOf(Meteor.userId()) == -1)
return result;
});
On client side: Meteor.subscibe('likes', {food.owner, food.likes}).subscribe(()=>{});
This is just off the top of my head.
Have you tried looking at Tracker ? https://docs.meteor.com/api/tracker.html
But more specifically the method Tracker.nonreactive
https://docs.meteor.com/api/tracker.html#Tracker-nonreactive
I'm building a simple database for small pixel-art files. The images are saved directly to the database:
Template.pixUpload.events({
'change .myPixInput': function(event, template) {
event.preventDefault();
var file = event.target.files[0]; //assuming 1 file only
if (!file) return;
var reader = new FileReader();
reader.onload = function(event){
MyPix.insert({
binary: reader.result,
createdAt: new Date
});
}
reader.readAsDataURL(file);
}
})
The idea is to be able to modify the images on their way back to the browser, scale them on the fly (if things don't get too slow). So I'm trying to read the image from the db, and scale it with Imagemagick, before displaying it. It doesn't work – and I can't find anything helpful I would be able to understand:
Template.pixList.helpers({
'thumbnail': function() {
var bin = this.binary;
var thumb = new FileReader();
Imagemagick.convert(['bin', '-filter', 'point', '64x64', 'thumb']);
return thumb;
}
})
im using GM Package Right now, take a look at full repo here
First Install All FSCollecton Packages.
GridFS (Because you say you want to store the file inside MongodB).
Graphicsmagick meteor add cvs:graphicsmagick
Second Declare collections on /lib/collection.js
kaiStackExample = new FS.Collection("kaiStackExample ", {
stores: [new FS.Store.GridFS("kaiStackExample ",{
beforeWrite:function(fileObj){
return {
extension: 'png',
type: 'image/png'
};
},
transformWrite:function(fileObj, readStream, writeStream){
// Here you can choose, for example 10x10 (thumbnail), etc.
gm(readStream).resize(600).stream('PNG').pipe(writeStream);
}
})]
});
and our basic Subscribe, on the same /lib/collection.js
if(Meteor.isClient) {
Meteor.subscribe('kaiStackExample');
}
Ok, at this point we have the GridFS and GM, to verify both
server console.
=> GraphicsMagick found
Client console.
kaiStackExample.find().fetch();
should return [];
Third SECURITY
kaiStackExample.allow({
insert:function(userId,doc){
//here we can do stuff like, only permit user with accounts insert on the collection
if(!Meteor.userId()){
return false;
}else{
return true
},
update:function(userId,doc){
if(userId === doc.authorId{
return true;
}else{
return false; // if user don't own the document can update it.
}
}
});
Four Template and Events
HTML markup
<template name="exampleKai">
Select File
<input type="file" id="fileExampleKai">
<button type="submit" id="buttonExampleKai">Click to upload</button>
</template>
Js Code
Template.exampleKai.events({
'click #buttonExampleKai':function(event,template){
event.preventDefault();
var file = $('#fileExampleKai').get(0).files[0];
fsFile = new FS.File(file);
fsFile.metadata = {
coolMetadata:"Yes You can add some metadata too"
}
if(file === undefined){
alert("Please upload a file to continue")
}else{
kaiStackExample.insert(fsFile,function(err,succes){
if(err){
console.log(err.reason);
}else{
console.log("ok we insert yeaaa")
}
});
}
}
});
Like i say this works for me, and i think its your best option for editing size,type,etc Take a look hope it help you
Even though Meteor is very good at keeping the client and server environments in sync, that doesn't mean it can do everything on the client that it can do on the server and vice versa. Converting images using ImageMagick would be one example of what you can only do on the server.
If I were to build something like this, I'd look into using CollectionFS for syncing files. They also have a section in the README that describes how to manipulate images before saving them, which seems to be just what you're after.
I'm very new to Meteor.js and I'm finding the documentation a bit hard to understand.
I'm starting with a very simple app where Users will simply be allowed to add existing Games to their profile by clicking a button. The Games are stored in another Meteor Collection.
In rails I would just create a has_and_belongs_to_many relationship but that isn't how Meteor works. I thought the best way would be to add an empty array when the user's account is created - then, when they click the "add game" button it would pass the game's title into the users array.
I have this in my /server/users.js file:
Accounts.onCreateUser(function(options, user){
user.games = [];
return user;
});
Meteor.methods({
addGame: function(title) {
Meteor.users.update(Meteor.userId(), { $addToSet: { games: title}});
}
});
And I'm making a call to the addGame method in my /client/views/games/games_list.js file as such:
Template.gamesList.events({
'click .add-to-chest-btn': function(e){
var title = $(e.target).attr('name');
e.preventDefault();
Meteor.call('addGame', title, function(title){ console.log(title)});
}
});
Am I on the right track or is there a better way to do this?
You're on the right track, but do declare an array instead of an object:
Accounts.onCreateUser(function(options, user){
user.games = [];
return user;
});
Push the value directly instead of an object, and use $addToSet to avoid duplicates in case you push the same gameId multiple times:
Meteor.methods({
addGame: function(gameId) {
Meteor.users.update(Meteor.userId(), { $addToSet: { games: gameId }});
}
});