Losing reactivity for some reason in JavaScript processed queries


#1

I do a lot of data “parsing” where I fetch Mongo data into a javascript variable (objects within array) and the process the data before I send it to helpers. This has worked very well.

In the following code I have defined some brief example of the case where I have lost reactivity and where it has worked before and it works in the previous version of my app.

Is this good way to do this? Should I move to react immiadiately?

    players_ranking:function() {
    
    // fetch data from mongo
    var data = PlayersRanking.find({}, {sort: {total: -1, goals: -1, player_name: 1}}).fetch();
    
    // set empty html variable
    var html = "";
        
    // loop to fill html variable with fetched array
    for (i = 0; i < data.length; i++) {
                
    html += "<div id='item' class='" + data[i]._id + "'></div>";
                
            }
            
            return html;
        
        
        
   
    }

#2

Actually it looks that I have lost reactivity for one collection. How have you troubleshooted these kind of situations?


#3

This is not the “Meteor way”. Unless you fundamentally change how you are doing this - or understand what you need to do - a move to React isn’t going to help.

If we look at this in the context of Blaze, there are two things you need:

  1. An HTML (spacebars) template.
  2. A matching Javascript Template object.

The HTML template might look like this:

<template name="ranking">
  {{each players}}
    <div>    
      Rank: {{total}}, name: {{player_name}}, goals: {{goals}}
    </div>
  {{/each}}
</template>

The Template object:

Template.ranking.helpers({
  players() {
    return PlayersRanking.find({}, {sort: {total: -1, goals: -1, player_name: 1}});
  }
});

Basically, that’s all you need for a reactive display, ranked by your sort criteria.


#4

Hello again @robfallows! Yes I am aware of the basics of spacebars, but things get a bit more different when I want to define div width etc, by Mongo data.

In the following
for loops i = 1,2,3,4,5,6,7 (on the right)
total = 3,3,2,2,2,1,1,1 (on the left)
goals = (grey block on the right, one of the is hovered on blue, showing also goals amount )
assists = (grey block on the left)

So the code in the background cannot be pushed into regular spacebars

    players_ranking_test:function() {
    
    // paging 15 items per page
    var paging_skip = +(Session.get('paging_skip'));
        
    
    var data = Players_ranking.find({}, {sort: {total: -1, goals: -1, player_name: 1}, skip:paging_skip, limit: 15}).fetch();
    
    var html = "";
                        
    for (i = 0; i < data.length; i++) {
                
    var player_rank = i + paging_skip + 1;
    var steelblue = "";
    
    if(i == 0) {Session.set('current_player', data[i]._id);Session.set('current_player_rank', player_rank);var steelblue = "steelblue"}
        
    if(data[i].assists == 0) {var width_assists = "'background:none !important;width:0px'"; data[i].assists = ""} else {var width_assists =  "width:" + data[i].assists/15*100 + "%;";}
    if(data[i].goals == 0) {var width_goals = "'background:none !important;width:0px'"; data[i].goals = "" } else {var width_goals =  "width:" + data[i].goals/15*100 + "%;";}
    

    
    html += "<div id='item' class='" + data[i]._id + "'> <div class=top_flex> <div style=width:10%></div><div class='name'>" + data[i].player_name + "</div>    <div class='team_player_ranking'>" + data[i].team + "</div>    </div><div class=flex>            <div class='left' style=position:relative><div id='player_rank' class=" + player_rank  +"></div>" + player_rank + "</div>    <div class='center' style=position:relative>                        <div class=inner_flex>                            <div class='item-item-item assist value "+steelblue+"' style=" + width_assists +" ;> " + data[i].assists + "</div>                                <div class='item-item-item goals value  "+steelblue+"' style=" + width_goals +";>" + data[i].goals + "</div>                                            </div>        </div><div class='right'>" + data[i].total + "p</div></div></div>";
                
            }
            
            return html;
        
        
        
        
        
    },

#5

You need to use Tracker.autorun so that when reactive data source changes, your code will be reran.


#6

@laosb Is it so, because it works in my previous app version and in development it suddendly died. I also tried to run direct into spacebars without any processing and collections is not reactive. Is there any practices to check why my collections is dead on reactivity?


#7

So you’re using the width of the div to make a sort of bar graph? You can definitely do that with standard spacebars and Meteor helpers.

<div style="width: {{width}};"><div>

Where you compute width in your helper.

Also, as your code stands, you will repeat the id='item' for each row on the page. That is an HTML anti-pattern - and causes known issues in Meteor if you want to add event handlers.


#8

Yes

But does not this create an additional helper? I was thinking that maybe I will parse the array into a new one where these calculations are included inside the objects and I will use {{#each}} {{/each}} to get the data into elements, like you said. I like to create “external functions” that I call to parse data :blush:

Do you mean that using duplicate ID’s is anti-pattern?


#9

Just to add to my post, above, where you need to calculate a value not contained in the collection document. Here are two ways to do that:

  1. Collection transforms - when Blaze renders the document, transform values are available.
  2. Within the helper, using map. This is similar to what you are doing now, in that you turn the cursor into an array. The difference is that if you do it inside a helper, it remains reactive (no need for an autorun):
Template.ranking.helpers({
  players() {
    return PlayersRanking.find({}, {sort: {total: -1, goals: -1, player_name: 1}}).map(doc => {
      // Do some stuff
      return newDoc;
    });
  }
});

#10

Yes - https://developer.mozilla.org/en-US/docs/Web/API/Element/id

It must be unique in a document …


#11

Thanks @robfallows I will try this one :muscle: :grin::v:

Early days I did not mind which one I used (ID/Class), but lately when I have became more advanced and faster with CSS I have started to create “class chains” for styling and leaving ID not used if I dont need it. With Meteor love the way I can update the stuff inside {{#each}} towards to Mongo, because this._id etc works so damn smoohtly.

However I have found that sometimes when using Meteor or jQuery for events inside the multi-layered divs and elements, classes cannot be used to trigger events, because Meteor and jQuery will return ID as undefined despite of .find(), .closest() etc. There I have been forced to use ID as a class, because browser can find them when class will return undefined.


#12

Do you have any examples? Often these can be fixed with more componentized template design.


#13

I dont have atm, but when I first faced issues to find classes inside multilayered divs, I went around stackoverflow and reckon that some others have had same kind of problems. This is something with JavaScript tho.