How do I use a subscription in flow-router? - javascript

// server/publications.js
Meteor.publish('crewMessages', function(crewId) {
return CrewMessages.find({crewId: crewId}, {sort: {submitted: -1}, limit: 100});
});
// lib/router.js
FlowRouter.route('/crew/:crewSlug', {
subscriptions: function(params) {
console.log("Subscribed to this crew's chat messages:", params.crewSlug);
this.register('crewMessages', Meteor.subscribe('crewMessages', params.crewSlug));
},
action: function(params) {
FlowLayout.render("layout", { content: 'crew' });
}
});
And inside my crew.html template:
<div class="container">
{{> crewChat}}
</div>
And my crewChat.html/js:
Template.crewChat.helpers({
messages: function() {
return CrewMessages.find({}, {sort: {submitted: -1}});
}
});
<div class="ui feed">
{{#each messages}}
{{> crewChatMessage}}
{{/each}}
</div>
In my crewChat.js file, how can I use the subscription I set in Flow-router?

I create a functional example for you at MeteorPad
http://meteorpad.com/pad/Ba5DTe94NjFi3ZTPA/Playground_Flow-Router_Chat
Most important to your question is, that you just can use the Collection inside your template with Collection.find() because you are only subscribed to the crews messages via route subscription.
Each message can be accessed as normal document see template showChatMessage
Hope that makes it clear for you
Cheers
Tom
PS: You can also change the url inside the meteorpad to also switch to chats for team2 and team3

Related

Why aren't my template's if clauses update reactively?

For whatever reason I am unable to solve this issue with countless hours of troubleshooting. I have some simple helpers working with a Bootstrap 3 nav-tabs list.
I want to render a different template based on which list item is active. Here are my helpers:
Template.Profile.helpers({
'personal':function(){
if($('.profile-info').hasClass('active')) {
return true;
} else {
return false;
}
},
'groups':function(){
if($('.profile-groups').hasClass('active')) {
return true;
} else {
return false;
}
},
'commitments':function(){
if($('.profile-commitments').hasClass('active')) {
return true;
} else {
return false;
}
}
});
And here is my HTML:
<ul class="nav nav-tabs">
<li class="active profile-info">Personal Info</li>
<li class="profile-groups">Groups</li>
<li class="profile-commitments">Commitments</li>
</ul>
{{#if personal}}
{{> ProfilePersonal}}
{{else}}
{{#if groups}}
{{> ProfileGroups}}
{{else}}
{{> ProfileCommits}}
{{/if}}
{{/if}}
The helpers will not be re-run when you click a tab, as there is no reactive data change to invalidate the computation.
A more Meteor-ish approach would be to add a reactive variable to hold the tab state and change that in an event listener.
<template name="Profile">
<ul class="nav nav-tabs">
{{#each tabs}}
<li class="{{isActive #index}} profile-{{name}}">{{title}}</li>
{{/each}}
</ul>
{{> Template.dynamic template=tpl}}
</template>
#index references the index of the current loop, and it's provided as an argument to the isActive helper.
Then, your JavaScript file can include a definition for the tabs and the handling code:
var tabs = [{
idx: 0,
name: "info",
title: "Personal Info",
template: "ProfilePersonal"
}, {
idx: 1,
name: "groups",
title: "Groups",
template: "ProfileGroups"
}, {
idx: 2,
name: "commitments",
title: "Commitments",
template: "ProfileCommits"
}];
The tabs are a plain JS array. The following code uses them in the template's context:
Template.Profile.helpers({
// get current sub-template name
tpl: function() {
var tpl = Template.instance();
return tabs[tpl.tabIdx.get()].template;
},
// get the tabs array
tabs: function() {
return tabs;
},
// compare the active tab index to the current index in the #each loop.
isActive: function(idx) {
var tpl = Template.instance();
return tpl.tabIdx.get() === idx ? "active" : "";
}
});
Template.Profile.events({
'click .nav-tabs > li': function(e, tpl) {
tpl.tabIdx.set(this.idx);
}
});
Template.Profile.onCreated(function() {
this.tabIdx = new ReactiveVar();
this.tabIdx.set(0);
});
When the template is created (onCreated()), a new reactive variable is added as an instance variable. This variable can then be accessed in helpers and set in event handlers.
The event handler receives the event object and template instance as parameters and has the data context set as the this pointer; therefore, tpl.tabIdxrefers the reactive variable and this refers to the object that represents the clicked tab (for example,
{
idx: 0,
name: "info",
title: "Personal Info",
template: "ProfilePersonal"
}
for the first tab, as this was the template's data context when the first tab was rendered.
The helper functions get the Template instance using a call to Template.instance(). Then, it queries the value of the reactive array.
This creates a computation in a reactive context (helpers are reactive contexts and they are rerun when the computation they create is invalidated, and that happens when an Mongo cursor, or a reactive variable that is read in the computation is changed).
Therefore, when the reactive variable is set in the event handler, the helpers are re-run and the template reflects the new value.
These are all fundamental to Meteor and are explained in the full Meteor documentation and in many resources.

Meteor: Adding sortable with rubaxa:sortable package

I try to integrate rubaxa:sortable to make my list sortable.
client/helpers.js
Template.getElements.helpers({
children: function() {
return Articles.find({ parent: this._id }, {sort: {order: 1}});
}
});
server/publications.js
Meteor.publish('articles', function() { return Articles.find({}, {sort: {slug: 1}}); });
Sortable.collections = 'articles';
template
<template name="getElements">
<ul class="sortable">
{{#each children}}
{{#sortable items=Articles sortField="order"}}
<li data-id="{{_id}}"><input type="text" name="keyword" value="{{title}}"></li>
{{/sortable}}
{{/each}}
</ul>
</template>
In the documentation (https://atmospherejs.com/rubaxa/sortable) I see the info:
Client:
{{#sortable items=<collection|cursor|array> sortField="order"}}
Server:
Sortable.collections = <collectionName>; // the name, not the variable
So what am I doing wrong? Right now no list-element is beeing shown.
I'm not an expert but based on integration this will fix your issues.
Server JS
Update the declaration to include the collation name in [].
Sortable.collections = ['articles'];
HTML Template
remove the following:
{{#each children}}
{{/each}}
Regards,
Vince

Meteor content not displaying

I am having issues with either Flow Router or my template level subscriptions but the data is not being rendered on the page.
Just incase the issue isn't what I have pasted here I have included a link to the entire github repo: https://github.com/adjohnston/checkr-meteor
lib/routes.js
listRoutes.route('/:slug', {
name: 'list',
subscriptions: function (params) {
this.register('singleList', Meteor.subscribe('singleList', params.slug));
},
action: function () {
FlowLayout.render('mainLayout', {
main: 'list'
});
}
});
server/publication/lists.js
Meteor.publish('singleList', function (slug) {
return Lists.find({slug: slug});
});
client/lists/list.js
Template.list.helpers({
singleList: function () {
return Lists.find();
}
});
client/lists/list.html
<template name="list">
{{#if isSubReady}}
{{#with singleList}}
<h2>Name: {{name}}</h2>
{{/with}}
{{/if}}
</template>
Solution
Change returns Lists.find() to Lists.findOne() since the publication 'singleList' is only returning a single result.
client/lists/list.js
Template.list.helpers({
singleList: function () {
return Lists.findOne();
}
});
Try changing your singleList helper to a findOne:
Template.list.helpers({
singleList: function () {
var slug = FlowRouter.getParam("slug");
return Lists.findOne({slug: slug});
}
});
Right now you are trying to display the name property of a cursor, which is what find() returns. You also don't need {{#with singleList}} in your handlebars.

How to get a child collection when the id of the parent isn't in the URL?

I have a Book and a Chapter collection. In a template called book_list.html there's an each statement listing all the book items:
<!-- book_list.html -->
<template name="bookList">
<div class="book-list">
{{#each books}}
{{> bookItem}}
{{/each}}
</div>
In order to get the word count I created a helper in book_item.js which works by fetching all the chapters in the book and returning the sum of all of their words.
Everything was OK, until I decided to remove the autopublish package and use publish and subscribe instead. The problem now is that I don't know how to do get the ID of the current book in book_list since its ID is not present in the URL (book_list is the home page).
This is the code (minus the code for the word count):
//publications.js
Meteor.publish("books", function() {
return Books.find({});
});
Meteor.publish("chapters", function(bookId) {
return Chapters.find({
bookId: bookId
}, {
sort: {
position: 1
}
});
});
//route.js
Router.map(function() {
this.route("bookList", {
path: "/",
waitOn: function() {
return Meteor.subscribe("books");
},
data: function() {
return Books.find({});
}
});
});
//book_item.js
Template.bookItem.helpers({
words: function() {
var chapters = Chapters.find({
bookId: this._id
}).fetch();
// code for the word counter

Child outlet goes empty when I refresh the url with id from {{link-to}} in Emberjs

I have a weird issue, the child outlet goes empty whenever I will refresh the page with the id. I have a list generated by {{link-to}} helper.
<script type="text/x-handlebars" id="twod">
<div class="row">
<div class="span4">
<img src="/img/2DPipeline.jpg" />
</div>
<div class="span3">
<h4>People with Roles</h4>
<div class="row">
<div class="span2">
<ul>
{{#each item in model}}
<li>{{#link-to 'twoduser' item}}{{item.firstname}} {{/link-to}}</li>
{{/each}}
</ul>
</div>
<div class="row">
<div class="span">
{{outlet}}
</div>
</div>
</div>
</div>
</script>
Here's the twoduser template,
<script type="text/x-handlebars" data-template-name="twoduser">
<div class="row">
<div class="span3">
Full Name: {{firstname}}{{lastname}}
EMail: {{email}}
</div>
</div>
</script>
App.js,
App.Router.map(function() {
this.resource('twod', function() {
this.resource('twoduser', {
path : ':user_id'
});
});
this.resource('threed');
});
App.TwoduserRoute = Ember.Route.extend({
model : function(params) {
return App.Twod.findBy(params.user_id);
}
});
App.Twod.reopenClass({
findAll : function() {
return new Ember.RSVP.Promise(function(resolve, reject) {
$.getJSON("http://pioneerdev.us/users/index", function(data) {
var result = data.users.map(function(row) {
return App.Twod.create(row);
});
resolve(result);
}).fail(reject);
});
},
findBy : function(user_id) {
var user = App.Twod.create();
$.getJSON("http://ankur.local/users/byId/" + user_id, function(data) {
user.setProperties(data.user);
});
user.set("user_id", user_id);
return user;
}
});
App.TwodRoute = Ember.Route.extend({
model : function() {
return App.Twod.findAll();
}
});
Selecting each one individually works fine and fills the child outlet, but when I refresh it, it goes blank.
Any ideas what might be causing the issue?
I can see two possible problems.
The first is that your URLs are different between findAll and findBy. Was that intentional?
The second is that findAll returns an Ember promise (Ember.RSVP.Promise), but findBy does not.
[UPDATE] : Based on the JSBin in the comments : http://jsbin.com/iPUxuJU/1/
The problem here is that the API endpoint is returning an array in the user response. It currently looks like this:
{user : [{ ... }] }
Ideally it would look like this :
{user : {....} }
You could change the API endpoint, or you could update your code to pull the first element from that array. Instead of :
user.setProperties(data.user);
You could do :
user.setProperties(data.user[0]);
Here's an altered JSBin : http://jsbin.com/oquBoMA/1#/twod/2

Categories

Resources