Blaze.getData to access previous template instance data context

I’m rendering a message list and each message is render by a Message template.
From each template instance I need to access the data context of the previous message rendered – so the previous instance data context.
I need to do that within a helper.

I thought that something like this would work, but it doesn’t:

Blaze.getData($('#' + Template.instance().data._id).prev().get(0))

where Template.instance().data._id is the id of current message.

My html template is something like this:

<template name="message">
  <div id ="{{getMessageId}}">{{messageBody}}</div>
</template> 

Any ideas what I’m doing wrong?

I’m not sure what you’re doing wrong. Have you considered creating a custom array and add the previous message to each array item, then use that for the list’s context ? like

    messagesWithPrev = _.map(messages,function(item, index){
      return {current:item, prev: messages[index-1]||null}
    });

This might work too, to get the previous element context.

element = $('#' + Template.instance().data._id).prev().get(0)
Blaze.getView(element).templateInstance().data

It’s pretty strange. Debugging I observed that for some reason helpers are executed twice for each message record. How is that possible?

In effect what I observe is that if look into:

element = $(’#’ + Template.instance().data._id).get(0)
before = $(’#’ + Template.instance().data._id).prev().get(0);

I get:

Blaze.getData(element) === Blaze.getData(before)

Array solution: Yes, thanks, that is a possible way to solve the problem. Another one I thought is adding to each message data context, the previous message data context like here:

About Blaze.getView I suppose that I will get the same issue as Blaze.getData. I’m gonna try today.

It’s not really a good idea to try to access a sibling template’s data context like this. A better approach would be to make the parent list template ‘smart’ and then have the message templates take what they’re given and simply show it. This separation is discussed in the guide here.

E.g.: If you were calling your message template from within you list template like this: {{> myMessage (messageArgs message)}} then messageArgs would be a helper that might look a bit like:

Template.myList.helpers({
  messageArgs(message) {
    const instance = Template.instance();
    return {
      message,
      active: instance.state.equals('activeMessage', message._id),
      previousId: // From the parent is should be easy to access the previous id
      makeMessageActive(messageId) { // A callback to have the child talk to the parent
        instance.state.set('activeMessage', message._id);
      }
    };
  }
});
1 Like

I got back to this after a while and your suggestion is really smart. Maybe a trivial question, but you say:

previousId: // From the parent is should be easy to access the previous id

How can I do that without using Messages.findOne(…) and get the message with the previous createdAt?
That slows down performance a lot.

Thanks

The idea is that at the parent level you would have an array of all messages in context. So within that messageArgs helper if you have the id of the current message (message._id) then you only need to use underscore or jquery or whatever to find the previous item.

It’s possible that the whole array isn’t in context (with pagination for example) in which case you have to go to the server to get it so I guess for this you might need to have some sort of server side logic to take the current id and return the previous message to that one. Note. You want to do this inside the server method (or pub) because the client might not be aware of messages that have been deleted.

You mean I have to keep a copy of the messages in an array saved instance var (global to messages)?

In the end I did something similar using a global variable to the template and injecting part of the previous message context data to the current message context data:

<template name="messages">
	{{#if Template.subscriptionsReady}}
		{{#if noMessages}}
			<div class="no-messages-yet">No messages posted yet. Be the first!</div>
		{{else}}
			{{#each msg in allMessages}}
				{{> message (msgArgs(msg))}}
			{{/each}}
		{{/if}}
		<spam id="bottom-page-anchor"></spam>
	{{/if}}
</template>

where the msgArgs helper is:

    var prev = null;
    ...

    msgArgs : function(props) {
	var augmentedRecord = {
		props,
		'prev' : prev ? {userId : prev.userId, createdAt : prev.createdAt} : null
	}
	prev = {
		userId : props.userId,
		createdAt : props.createdAt
	}
	return augmentedRecord;
    }

This solution made performance more than acceptable :slight_smile:
Thanks for your tips

It turns out that my approach creates a bunch of problems to the usual Blaze reactive behavior :confused:
I guess it depends of the fact that I injected new fields into current message context and that is a reactive space.

For example: when I delete a message from the list Blaze refreshes the entire list
I’d like add fields to the message data space without interfering with Blaze reactive rendering process.

Any ideas?