Dani
Dani

Reputation: 4187

Non-reactive and reactive data in Meteor (same helper)

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?

Upvotes: 2

Views: 743

Answers (2)

laser
laser

Reputation: 570

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:

  1. 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.

  1. 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"
    });
  1. 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(()=>{});

Upvotes: 2

Jake Lacey
Jake Lacey

Reputation: 633

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

Upvotes: 1

Related Questions