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.
Related
I have a Vue component for a sidebar with a template defined as:
Vue.component('navigation',{
template:`
<ul class="nav">
<li v-if="checkIfUserIsAdmin() == true" id="adminMenu"></li>
<li id="userMenu"></li>
`,
methods: {
checkIfUserIsAdmin() {
var result = false;
axiosInstance.get("/Profile/GetUserInfo").then(userInfo => {
result = userInfo.data.u.isAdmin;
})
.catch(userError => {
swal({
title: "Operational Platform",
text: "Unable to retrieve user info"
});
result = false;
});
return result;
}
}
});
Some code was removed for brevity.
When I visit the /Profile/GetUserInfo I get a JSON in return that's properly returning me true but the adminMenu is not displaying and I'm wondering why. It seems that the v-if is the place that I've messed up. I've tried also changing the adminMenu to have v-if="checkIfUserIsAdmin() == 'true'" but it still didn't work.
You need to wait for the method's result.
First you need to run the checkIfUserIsAdmin method when the component is mounted.
In checkIfUserIsAdmin method you need to store the result of the query in adminMenuDisplay variable and then you can watch this variable in v-if.
Vue.component('navigation',{
template:`
<ul class="nav">
<li v-if="adminMenuDisplay" id="adminMenu"></li>
<li id="userMenu"></li>
`,
data() {
return {
adminMenuDisplay: false
};
}
methods: {
checkIfUserIsAdmin() {
var result = false;
axiosInstance.get("/Profile/GetUserInfo").then(userInfo => {
this.adminMenuDisplay = userInfo.data.u.isAdmin;
})
.catch(userError => {
swal({
title: "Operational Platform",
text: "Unable to retrieve user info"
});
this.adminMenuDisplay = false;
});
}
},
mounted() {
this.checkIfUserIsAdmin();
}
});
// 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
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
I've been looking at this for awhile and I'm pretty sure it has something to do with an infinite callback loop.
I have a method that returns an integer from a collection called Sessions. Here are my methods:
Meteor.methods({
going: function(sessionsId) {
return Sessions.update(sessionsId, {$addToSet: {participants: Meteor.userId()}, $inc: {slots:-1}});
},
retract: function(sessionsId) {
return Sessions.update(sessionsId, {$pull: {participants: Meteor.userId()}, $inc: {slots:1}});
},
sessionFull: function(sessionsId) {
var session = Sessions.findOne({_id:sessionsId});
console.log("gets here");
return session.slots;
}
});
Then in my client I have:
if (Meteor.isClient) {
Template.hello.sessions = function () {
return Sessions.find();
};
Template.session.this_info = function () {
return this._id;
};
Template.session.isGoing = function() {
var session = Sessions.find({_id:this._id, participants:Meteor.userId()}).count();
if (session > 0) return true;
else return false;
};
Template.session.sessionFull = function() {
if (this.slots === 0) return true;
else return false;
};
Template.session.slotsMethod = function () {
Meteor.call('sessionFull',this._id, function(error, slots) {
Session.set("slots",slots);
});
return Session.get("slots");
};
Template.session.events({
'click input.going' : function () {
//Sessions.update(this._id, {$inc: {slots: -1}});
Meteor.call('going', this._id, function(error, updated) {
if (error)
return alert(error.reason);
});
},
'click input.retract' : function () {
Meteor.call('retract', this._id, function(error, removed) {
if (error)
return alert(error.reason);
});
}
});
So I basically have a couple buttons that will increase or decrease the slots field and I want to have a method that will return what the slots field contains. Here is my template:
{{#each sessions}}
{{> session}}
{{/each}}
<template name="session">
<br>
{{date_time}}, {{duration}}
{{#if isGoing}}
<input type="button" class="retract" value="not going/give slot" />
{{else}}
{{#if sessionFull}}
<h1>SORRY SESSION FULL</h1>
{{else}}
<input type="button" class="going" value="going/subract slot" />
{{/if}}
{{/if}}
{{participants}},{{sessionFull}},{{this_info}}
</template>
If I try to add the Template.session.slotsMethod to my template (which calls the sessionFull Meteor method) I get an infinite loop, as in, it will display a rapidly changing integer for each session.
Am I doing something wrong?? Can't figure it out, I think it has something to with callbacks/async/sync but not sure.
Yes, your Template.session.slotsMethod will cause an infinite loop since Session is reactive.
This is what happens:
Whenever Session.get("slots") changes, Template.session.slotsMethod will be called because its dependent on Session.get("slots").
However, Template.session.slotsMethod itself is also updating the value of Session.get("slots") so the process starts all over again.
Not quite sure when you want Template.session.slotsMethod to be run, but you probably want to break it up into two pieces, something like:
Template.session.getSlots = function () {
return Session.get("slots");
};
and
Meteor.call('sessionFull',this._id, function(error, slots) {
Session.set("slots",slots);
});
needs to go wherever/whenever you need to do the sessionFull check, perhaps in Template.session.rendered?
I have a simple Handlebars helper which simply formats a money value. The helper works property when I test with static data, but not when I load data asynchronously. In other words, {{totalBillable}} will output the expected amount, but {{money totalBillable}} will output zero. But only when the data is loaded via an ajax call. What the heck am I doing wrong?
I've tried to pare the code down as much as possible, and also created a jsfiddle here:
http://jsfiddle.net/Gjunkie/wsZXN/2/
This is an Ember application:
App = Ember.Application.create({});
Here's the handlebars helper:
Handlebars.registerHelper("money", function(path) {
var value = Ember.getPath(this, path);
return parseFloat(value).toFixed(2);
});
Model:
App.ContractModel = Ember.Object.extend({});
App Controller:
App.appController = Ember.Object.create({
proprietor: null,
});
Contracts Controller (manages an array of contracts):
App.contractsController = Ember.ArrayController.create({
content: [],
totalBillable: function() {
var arr = this.get("content");
return arr.reduce(function(v, el){
return v + el.get("hourlyRate");
}, 0);
}.property("content"),
When the proprietor changes, get new contract data with an ajax request. When getting data asynchronously, the handlebars helper does not work.
proprietorChanged: function() {
var prop = App.appController.get("proprietor");
if (prop) {
$.ajax({
type: "POST",
url: '/echo/json/',
data: {
json: "[{\"hourlyRate\":45.0000}]",
delay: 1
},
success: function(data) {
data = data.map(function(item) {
return App.ContractModel.create(item);
});
App.contractsController.set("content", data);
}
});
}
else {
this.set("content", []);
}
}.observes("App.appController.proprietor")
});
If I use this version instead, then the Handlebars helper works as expected:
proprietorChanged: function() {
var prop = App.appController.get("proprietor");
if (prop) {
var data = [{
"hourlyRate": 45.0000}];
data = data.map(function(item) {
return App.ContractModel.create(item);
});
App.contractsController.set("content", data);
}
else {
this.set("content", []);
}
}.observes("App.appController.proprietor")
View:
App.OverviewTabView = Ember.TabPaneView.extend({
totalBillableBinding: "App.contractsController.totalBillable"
});
Kick things off by setting a proprietor
App.appController.set("proprietor", {
ID: 1,
name: "Acme"
});
Template:
<script type="text/x-handlebars">
{{#view App.OverviewView viewName="overview"}}
<div class="summary">
Total Billable: {{totalBillable}}<br/>
Total Billable: {{money totalBillable}}<br/>
</div>
{{/view}}
</script>
when using a helper, handlebars does not emit metamorph tags around your helper call. this way, this part of the template is not re-rendered because there is no binding
to manually bind part of a template to be re-rendered, you can use the bind helper:
<script type="text/x-handlebars">
{{#view App.OverviewView viewName="overview"}}
<div class="summary">
Total Billable: {{totalBillable}}<br/>
Total Billable: {{#bind totalBillable}}{{money this}}{{/bind}}<br/>
</div>
{{/view}}
</script>