Lesscomfortable
Lesscomfortable

Reputation: 823

Variables not being correctly rendered into HTML with Meteor (probably a Data Context issue)

I am trying to create a link in my web page to each of the users' posts. I have succeeded in creating the new page (message.html) for each post (the "Hello World" for this web page is displayed correctly, see below). However and in contrast to my main page, the variables I want to render into the HTML fail to be displayed. Thanks in advance!

Notes: Routing was done with Flow Router.

Here are the relevant snippets of code:

Message.js (or controller for the html)

import { Meteor } from 'meteor/meteor';
import { Template } from 'meteor/templating';
import { Boosts } from '../api/boosts.js';

import './boost.js';
import './message.html';

Template.ApplicationLayout2.onCreated(function messagesOnCreated() {
  Meteor.subscribe('messages');
});

Template.boostPage.helpers({
messages() {
  return Boosts.findOne(this._id());
  },
});

Message.html (or html to be displayed when a new page is opened)

Note: the Hello World in this html is placed as an example of what can be effectively displayed in the browser.

<template name="ApplicationLayout2">
Hello World
    {{> boostPage}}
</template>

<template name="boostPage">
    {{#with messages}}
    <h2>Boost: {{text}}</h2>
    <q class="message">{{message}}</q>
    {{/with}}
</template>

Boosts.js (relevant part) (Meteor.publish('boosts', function boostsPublication()...) is not relevant for this problem but I included it to keep the code's original format)

import { Meteor } from 'meteor/meteor';
import { Mongo } from 'meteor/mongo';
import { check } from 'meteor/check';
import { Match } from 'meteor/check';

export const Boosts = new Mongo.Collection('boosts'); 

if (Meteor.isServer) {
  Meteor.publish('boosts', function boostsPublication() {
    return Boosts.find({
      $or: [
        { private: { $ne: true } },
        { owner: this.userId },
      ],
    });
  });
  Meteor.publish('messages', function messagesPublication() {
    return Boosts.find({
        {_id: this._id},
    });
  });
}

Routes.js

FlowRouter.route('/', {
  name:'home',
  action() {
    BlazeLayout.render('ApplicationLayout');
  }
});


FlowRouter.route('/boosts/:_id', {
  name:'messages',
  action: function() {
    BlazeLayout.render("ApplicationLayout2", {_id: this._id});
  }
});

EDIT #1 I included the corrections suggested by zim.

Message.js (or controller for the html)

import { Meteor } from 'meteor/meteor';
import { Template } from 'meteor/templating';
import { Boosts } from '../api/boosts.js';
import { ReactiveVar } from 'meteor/reactive-var';

import './boost.js';
import './message.html';

Template.Message.onCreated(function() {
    // get the message id from the route parameter
    let messageId = FlowRouter.getParam('messageid');

    // save it to a reactive var so the helper can use it
    this.messageId = new ReactiveVar(messageId);

    // subscribe to 1 message using that message id
    this.subscribe('message', messageId);
});

Template.Message.helpers({
    // get data for 1 message
    message() {
        let messageId = Template.instance().messageId.get();
        return Boosts.find({_id: messageId});
    }
});

Message.html (or html to be displayed when a new page is opened)

Note: the Hello World and the Boost: header together with object Object surrounded between quotes are displayed in the browser.

<template name="Message">
Hello World
    {{#with message}}
    <h2>Boost: {{text}}</h2>
    <q class="message">{{message}}</q>
    {{/with}}
</template>

Boosts.js (relevant part) (Meteor.publish('boosts', function boostsPublication()...) is not relevant for this problem but I included it to keep the code's original format)

import { Meteor } from 'meteor/meteor';
import { Mongo } from 'meteor/mongo';
import { check } from 'meteor/check';
import { Match } from 'meteor/check';

export const Boosts = new Mongo.Collection('boosts');

if (Meteor.isServer) {
  // This code only runs on the server
  // Only publish boosts that are public or belong to the current user
  Meteor.publish('boosts', function boostsPublication() {
    return Boosts.find({
      $or: [
        { private: { $ne: true } },
        { owner: this.userId },
      ],
    });
  });
    // Publish only the message corresponding to the boost in question
    Meteor.publish('message', function(messageId) {
    return Boosts.find({_id: messageId});
  });
}

Routes.js

  FlowRouter.route('/', {
  name:'home',
  action() {
    BlazeLayout.render('ApplicationLayout');
  }
});


FlowRouter.route('/boosts/:messageid', {
  name:'messages',
  action: function() {
    BlazeLayout.render("Message", {messageid: this._id});
  }
});

Upvotes: 0

Views: 115

Answers (1)

zim
zim

Reputation: 2386

in this publish:

  Meteor.publish('messages', function messagesPublication() {
    return Boosts.find({
        {_id: this._id},
    });
  });

... what are you expecting "this._id" to represent? did you log it to the console? i suspect it's undefined.

update:

let's say you have a route like this: /message/:messageid

you can have a single message template like this:

Message.html:

<template name="Message">
    {{#with message}}
      {{content}}    <!-- or some field within message -->
    {{/with}}
</template>

Message.js:

Template.Message.onCreated(function() {
    // get the message id from the route parameter
    let messageId = FlowRouter.getParam('messageid');

    // save it to a reactive var so the helper can use it
    this.messageId = new ReactiveVar(messageId);

    // subscribe to 1 message using that message id
    this.subscribe('message', messageId);
});

Template.Message.helpers({
    // get data for 1 message
    message() {
        let messageId = Template.instance().messageId.get();
        return Boosts.find({_id: messageId});
    }
});

that's a simple template for getting the data for a single message, and displaying it. now you need to publish that single message:

server.js:

  Meteor.publish('message', function(messageId) {
    return Boosts.find({_id: messageId});
  });

that's one way of doing it. note the flow: the router defines the id of the message, the template subscribes to it, that tells the server to publish it, and then the helper does a find on that published data.

you could also create a template that loops through the messages. here, we're subscribing to all the message data, and publishing it all. (note how i'm naming things in singular and plural to help convey the concept)

Messages.html:

<template name="Messages">
    {{#each message in messages}}
      {{message.content}}    <!-- or some field within message -->
    {{/each}}
</template>

Messages.js:

Template.Messages.onCreated(function() {
    // subscribe to all the messages
    this.subscribe('messages');
});

Template.Messages.helpers({
    // get data for all the messages
    messages() {
        return Boosts.find();
    }
});

this publishes all the data:

server.js:

  Meteor.publish('messages', function() {
    return Boosts.find();
  });

in this example, we're not pulling any info from the route, but looping through all the messages.

does that help?

Upvotes: 1

Related Questions