How to correctly implement dynamical conditional "Meteor.publish"?


#1

Dear Meteors,

I am trying to limit the published fields of a collection dynamically depending on “onCreated” hooks from Templates.

My publish function in “server.js”:

Meteor.publish("playerData", function(userName, fieldLimit) {

        if (this.userId) {
            if(fieldLimit === 'mineBase') {
                return playerData.find({
                    user: userName
                },{
                    fields: {
                        'user': 1,
                        'mine.ownSlots': 1,
                        'mine.scrSlots': 1,
                        'mine.supSlots': 1,
                        'mine.minControl': 1,
                        'mine.maxControl': 1,
                    }
                });
            }
            else if(fieldLimit === 'improvementsmine') {
                return playerData.find({
                    user: userName
                },{
                    fields: {
                        'user': 1,
                        'level': 1,
                        'XP': 1,
                        'requiredXP': 1,
                        'mine.science': 1,
                        'mine.scrItem.benefit': 1
                    }
                });
            }
        } else {
            this.ready();
        }
    });

Calls in “onCreated” hooks in “client.js”:

Template.improvements.onCreated(function() {

        var inst = this;
        var self = Meteor.users.findOne({
                _id: Meteor.userId()
            }, {
                fields: {
                    menu: 1,
                    username: 1,
                    _id: 0
                }
            });

        inst.autorun(function() {

            var subsPlayerDataImprovements = inst.subscribe('playerData', self.username, 'improvements'+self.menu);

            }
        })
    });

    Template.mineBase.onCreated(function() {
    
            var inst = this;
            var self = Meteor.users.findOne({
                    _id: Meteor.userId()
                }, {
                    fields: {
                        username: 1,
                        _id: 0
                    }
                }).username;
    
            inst.autorun(function () {
                var subsPlayerDataMineBase = inst.subscribe('playerData', self, 'mineBase');           
       }
    })
});

DDP message for mineBase:

a["{\"msg\":\"added\",\"collection\":\"playerData\",\"id\":\"TBcisBavyTi9KEn6k\",\"fields\":{\"user\":\"bot20\",\"mine\":{\"ownSlots\":3,\"scrSlots\":6,\"supSlots\":2,\"minControl\":0.1,\"maxControl\":10}}}"]

consequent DDP message for improvementsMine:

a["{\"msg\":\"changed\",\"collection\":\"playerData\",\"id\":\"TBcisBavyTi9KEn6k\",\"fields\":{\"level\":0,\"XP\":2,\"requiredXP\":2014}}"]

mineBase.onCreated is invoked first and improvements.onCreated after that. The DDP message for improvementsMine misses two specified fields: “mine.science” and “mine.scrItem.benefit” and I don’t understand why.

Is there some major mistake in how I am trying to achieve this? Mainly I am trying to change my application’s architecture from global subscriptions to template level subscriptions.

Maybe it is relevant that the improvements Template is nested inside the mineBase Template?

Thank you very much in advance!


#2

I don’t see any obvious issue in your code.
One hint, though: your 2 autoruns are useless (nothing reactive inside).


#3

Also, it would be clearer with several publish functions.


#4

Thanks for your feedback. :smile:

I tried to follow along this: https://www.discovermeteor.com/blog/template-level-subscriptions/, thatswhy the autoruns. I removed them and it works nevertheless.

I’m going to consider tidying up the code with several publish functions but at the moment I am desperately trying to solve this issue. I just can’t find any clues as to why this isn’t working…

Might Meteor have a problem with the nested fields? In the first DDP message ‘mine.ownSlots’ and all other nested fields of 'mine" are delivered correctly. In the second DDP message all additional specified fields are added except for the two nested in ‘mine’. Maybe Meteor thought the field ‘mine’ doesn’t have to be changed because it’s already contained in the previous DDP?


#5

There is no Meteor problem I am aware of in this case.
Please post your simplified js code (autoruns removed + 2 publish functions + Meteor.user() instead of Meteor.users.findOne) and your html.


#6

Javascript without autorun and Meteor.user:

Template.improvements.onCreated(function() {
        var inst = this;
        var self = Meteor.user({
                _id: Meteor.userId()
            }, {
                fields: {
                    menu: 1,
                    username: 1,
                    _id: 0
                }
            });

            var subsPlayerDataImprovements = inst.subscribe('playerDataImprovements'+self.menu, self.username);

         //further logic omitted
    });

    Template.mineBase.onCreated(function() {
    
            var inst = this;
            var self = Meteor.user({
                    _id: Meteor.userId()
                }, {
                    fields: {
                        username: 1,
                        _id: 0
                    }
                }).username;
    
                var subsPlayerDataMineBase = inst.subscribe('playerDataMineBase', self); 

     //further logic omitted
});

2 Publish functions:

Meteor.publish("playerDataMineBase", function(userName, fieldLimit) {

        console.log('playerDataMineBase');
        if (this.userId) {
        return playerData.find({
                    user: userName
                },{
                    fields: {
                        'user': 1,
                        'mine.ownSlots': 1,
                        'mine.scrSlots': 1,
                        'mine.supSlots': 1,
                        'mine.minControl': 1,
                        'mine.maxControl': 1,
                        //'mine.science': 1,
                        //'mine.scrItem.benefit': 1
                    }
                });
        } else {
            this.ready();
        }
    });

    Meteor.publish("playerDataImprovementsmine", function(userName, fieldLimit) {

        console.log('playerDataImprovementsmine');
        if (this.userId) {
        return playerData.find({
                    user: userName
                },{
                    fields: {
                        'user': 1,
                        'level': 1,
                        'XP': 1,
                        'requiredXP': 1,
                        'mine.science': 1,
                        'mine.scrItem': 1
                    }
                });
        } else {
            this.ready();
        }
    });

HTML improvements template:

<template name="improvements">
    {{#if Template.subscriptionsReady}}
    <div id="improvementsWrapper" class="top left right">
        <div id="improvementsLeftEdge" class="sshr"></div>
        <div id="improvements" class="sshr">
            {{#with improvement}}
            <div class="improvementsText" style="color: {{color}};">{{name}}</div>
            <div id="improvementsText" class="hammersmith" style="display:table-cell">; XP: {{xp}} ; LEVEL: {{level}} ; SCIENCE: {{science}} ; ITEM: {{item}}</div>
            {{/with}}
        </div>
        <div id="improvementsRightEdge" class="sshr"></div>
    </div>
    {{/if}}
</template>

HTML mineBase template:

<template name="mineBase">
{{#if Template.subscriptionsReady}}
    <div id="mitte">
        {{> improvements}}
        <div class="area_left bot">
            <div id="up_slider_base" class="hover_slide_height top slider_base_height">
                <div id="base_up" class="slider slider_size center_h SSbase_left_slider_up hover ssh"></div>
            </div>
            <div id="base_area">
                <div id="base_area_content" class="scrollable relative">

                    {{#each mineUnusedSlots}}
                    <div class="slotWrapper">
                        <div class="slot_edge_left_BaseLeft left sshr">
                            <div class="slot_icon slot_icon_mineUnused ssvr"></div>
                        </div>
                        <div class="unused_slot slotBackgroundBaseLeft sshr">
                            <div class="slot_upper_bar">
                                <div id="left_slider_slot_colors" class="left slot_colors_slider SScolors_pfeil_left ssh"></div>
                                <div class="slot_colors_wrapper">
                                    <div class="left slot_colors_edges sshr"></div>
                                    <div class="slot_colors_content sshr">
                                        {{#each blockColors}}
                                        <div class="SScolor relative ssh" style="background-position:{{color}}"></div>{{/each}}
                                    </div>
                                    <div class="right slot_colors_edges sshr"></div>
                                </div>
                                <div id="right_slider_slot_colors" class="right slot_colors_slider SScolors_pfeil_right ssh"></div>
                            </div>
                            <div class="slot_items">
                                <div id="slot_items_left" class="slider left slot_items_slider left_slider_slot_items ssh"></div>
                                <div class="slot_items_wrapper">
                                    <div class="slot_items_content scrollable relative">
                                        {{#each matterBlocks}}
                                        <div id={{matter}} class="item relative">
                                            <div class="itemImg ssib" style="background-position:{{color}}"> </div>
                                            <div class="itemTextValue">{{value}}</div>

                                            <div class="wrapperCosts" style="bottom: 5px; display: inline-flex;">

                                                <div class="itemTextCostPre relative">pay</div>
                                                <div class="itemTextCost relative">{{cost}}</div>
                                                <div class="itemTextCostAfter relative ssib" style="background-position:{{colorCost}}"> </div>

                                            </div>

                                        </div>
                                        {{/each}}
                                    </div>
                                </div>
                                <div id="slot_items_right" class="slider right slot_items_slider right_slider_slot_items ssh"></div>
                            </div>
                        </div>
                        <div class="slot_edge_right_BaseLeft right sshr"></div>
                    </div>


                    {{/each}} {{#each mineUsedSlots}}
                    <div class="slotWrapper dropdown">

                        <div class="used_slot_Wrapper relative">

                            <div class="slot_edge_left_BaseLeft left sshr">
                                <div class="slot_icon ssib" style="background-position:{{color}}"></div>
                            </div>
                            <div class="used_slot slotBackgroundBaseLeft sshr">
                                <div class="slot_upper_bar tooltip_hover">
                                    <span id="tooltip_left_handle_{{slot}}" class="range_slider_tooltip"></span>
                                    <span id="tooltip_right_handle_{{slot}}" class="range_slider_tooltip"></span>
                                    <div id="range_slider_{{slot}}" class="range_slider"></div>
                                </div>
                                <div class="slot_stats">
                                    <p class='text relative'>Value: {{value}}</p>
                                    <p class='text relative'>Slots: {{slots}}</p>
                                    <p class='text relative'>Remaining: <a id='{{remainingId}}' class='text relative'> {{remaining}} </a>
                                    </p>
                                </div>
                            </div>
                            <div class="slot_edge_right_BaseLeft right sshr"></div>
                        </div>



                        <div class="used_slot_advanced_wrapper">

                            <div class="used_slot_advanced">

                                <div class="slot_edge_left_BaseLeft_advanced left sshr"></div>

                                <div class="slot_advanced_stats_wrapper relative slotBackgroundBaseLeftAdvanced sshr">

                                    <div class="div_center_vertical">

                                        <div class="slot_advanced_stats">
                                            <p class='text relative'>Profit: {{profit}}</p>
                                            <p class='text relative'>Miningrate: {{miningrate}}</p>
                                            <p class='text relative'>Time spent: <a id='{{timeSpentId}}' class='text relative'> {{timeSpent}} </a>
                                            </p>
                                        </div>

                                    </div>

                                </div>

                                <div class="slot_edge_right_BaseLeft_advanced right sshr"></div>

                            </div>

                            {{#each supporter}}
                            <div class="scrounge_slot_supporter relative">

                                <div class="slot_edge_left_BaseLeft_supporter left sshr">

                                    <div class="scrounger_icon ssib" style="background-position: -1754px 4px "></div>

                                    <div class="scrounger_icon_userName hammersmith">{{supName}}</div>

                                </div>


                                <div class="scrounger_stats_wrapper relative slotBackgroundBaseLeftSupporter sshr">
                                    <div class="div_center_vertical">
                                        <div class="scrounger_stats relative">
                                            <p class='text relative'>Miningrate: {{miningrate}}</p>
                                            <p class='text relative'>Mined: {{mined}}</p>
                                            <p class='text relative'>Time spent: <a id='{{timeSpentId}}' class='text relative'> {{timeSpent}} </a>
                                            </p>
                                        </div>
                                    </div>
                                </div>

                                <div class="slot_edge_right_BaseLeft_supporter right sshr"></div>

                            </div>
                            {{/each}}
                        </div>
                    </div>

                    <script>
                    Session.set("middle", "mineBase");
                    </script>

                    {{/each}}
                </div>
            </div>
            <div class="down_slider_base hover_slide_height bot">
                <div id="base_down" class="slider slider_size center_h SSbase_left_slider_down hover ssh"></div>
            </div>
        </div>

        <div id="seperation_bar" class="left right ssvr"></div>


        <div class="area_right bot">
            <div id="up_slider_scrounge" class="hover_slide_height top">
                <div id="scrounge_up" class="slider slider_size center_h SSbase_right_slider_up hover ssh"></div>
            </div>
            <div id="scrounge_area">
                <div id="scroungeAreaContent" class="scrollable relative">
                    {{> mineRightBaseUnusedSlots}} {{> mineRightBaseUsedSlots}}
                </div>
            </div>

            <div id="down_slider_scrounge" class="hover_slide_height bot">
                <div id="scrounge_down" class="slider slider_size center_h SSbase_right_slider_down hover ssh"></div>
            </div>
        </div>
    </div>
    {{else}}
    <div id="mitte"> <img style="position: relative; width:256px; height:152px; top:50%; left: 50%; margin-left:-128px;" src="loadingScreen.gif"> </div>
{{/if}}
</template>

This template is pretty big. I’m not sure what is needed to judge this matter which is why I left it like that. I am using the “Template.subscriptionsReady” helper.

Also why “Meteor.user()” instead of “Meteor.users.findOne()”?


#7

Sorry, this is too much code to swallow. Maybe you should try to create a simplified Meteor project.

Regarding Meteor.user(): see the Meteor doc to see how to use it. It is just a mean to simplify your code (at the cost of a little CPU overhead).


#8

No need to apologize. Thanks for taking your time.

Ok, I’ll check Meteor.user.

You’re right that creating a simpler project might help to find the issue. Unfortunately I need this for my bachelor thesis, so time is very limited. : )


#9

Finally found something related through googeling. It seems the problem is indeed related to Meteor because of restrictions to DDP messages.

““right now DDP only exposes top level fields of objects, which does keep the protocol much less complex.” — David Glasser, MDG”


#10

Glad you got it!
Maybe you could fill an issue asking for a console warning.