I'm having an issue with aldeed:autoform which I can't solve, nor understand what is the cause. The template:
<template name="staffCaseEdit">
{{> quickForm collection=Cases id="inserNewItem" type="insert"}}
</template>
I use aldeed:collection2 and aldeed:simple-schema to manage collections. So, I have the Case schema and Cases collection, both defined in /lib so they should be available on the client side, too.
Next, there's the route:
FlowRouter.route('/staff/case/:id', {
triggersEnter: [
AccountsTemplates.ensureSignedIn
],
subscriptions: function (params) {
this.register('theCase', Meteor.subscribe('theCase', params.id));
},
action: function (params, queryParams) {
return BlazeLayout.render('container', {
main: 'staffCaseEdit',
id: params.id
});
}
});
And of course, theCase is published:
Meteor.publish('theCase', function (id) {
return Cases.find({
id: Number(id)
});
});
In browser console, Cases are present:
> Cases
< Object
> Cases.find().count()
< 1
Which I suggest to be sufficient for the quickForm to consume the collection properly (it requires one as one of the arguments).
The problem is that, on the client side, I'm having an error
Exception in template helper: quickFormContext#http://localhost:3000/packages/aldeed_autoform.js?b0918af3b0bb0ae387d80c71f4230daca94cae11:6851:34
which I cannot trace. As a result, no form is being shown (actually, the whole DOM is left empty.
What should I look for? What could be the source of this particular problem?
A bit shooting in the dark here, I don't know if it will fix your problem but it will certainly help... Use this:
<template name="staffCaseEdit">
{{> quickForm collection="Cases" id="inserNewItem" type="insert"}}
</template>
You are passing the variable Cases to the collection parameter instead of passing it the name of the target collection "Cases"
Related
I'm having trouble printing the contents that I am getting from a meteor collection. I have declared my collection in /ui/api/collections.js
import { Meteor } from 'meteor/meteor';
import { Mongo } from 'meteor/mongo';
import 'meteor/aldeed:collection2';
export const Courses = new Mongo.Collection("courses");
Courses.allow({
'insert': function () {
// if (Meteor.isEducator(Meteor.userId())) {
return true;
}
})
/imports/api/course.html
<template name="course">
<h2> You are enrolled in the following courses:</h2>
<ul>
{{#each course_list}}
{{> getCourseName}}
{{/each}}
</ul>
</template>
<template name="getCourseName">
<li><h2>{{courseName}}</h2></li>
</template>
/imports/api/course.js
Template.course.helpers({
course_list: function() {
var result = Courses.find({});
console.log("result is:");
console.log(result);
return result;
}
})
I have declared Courses as a new Mongo.Collection and imported that into /server/main.js I have then imported /imports/api/collections.js into course.js and course.html.
the console output of printing course_list's result is :
L…n.Cursor {collection: LocalCollection, sorter: null, matcher: M…o.Matcher, _selectorId: undefined, skip: undefined…}
I noticed that it says LocalCollection which makes me think it isn't finding the actual collection. When I do a db.courses.find() on the serverside mongo console I am seeing the two courses that do exist in the collection and they both have a courseName field. I don't think that I need to publish/subscribe since I am declaring the collection in imports and then exporting the collection as a global var. I'm new to meteor and javascript in general so any explanation is greatly appreciated.
No worries. You had access to the data already. It's just that Meteor creates local collections on the client side - the power of Minimongo. That is why you saw LocalCollection on the console instead of Courses, which you might be expecting.
Note as well that find on collections returns a cursor. One needs to use operations fetch, forEach, map, etc to manipulate the actual data.
I have a template that has data getting passed into it through the Iron Router params here, it might be obvious what the template is being designed to do:
lib/routes.js
// custom reset password page for child user
Router.route('/reset-password/child/:_id', {
name: 'reset-child-password',
template: 'childResetPassword',
layoutTemplate: 'appLayout',
data: function() {
return Users.findOne({ _id: this.params._id });
}
});
But, when I try to access this child user data in the template I get errors saying this.data is undefined. or cannot read property 'profile' of undefined. Here are my helpers and template use of the helper.
client/templates/childResetPassword.html
...
<h3>Reset Password for {{childFirstName}}</h3>
<form id='childResetPassword'>
<div class="form-group">
<input type="password" name="new-child-password" class="form-control" value=''>
</div>
...
client/templates/helpers/childResetPassword.js
Template.childResetPassword.helpers({
childFirstName: function() {
console.log("Template helpers:");
console.log(this.data);
return this.data.profile.firstname;
}
});
Any thoughts on how to access the data-context passed with the iron router data callback? Am I using it incorrectly?
UPDATE (STILL NOT ANSWERED): I have verified that this particular user that I am passing into the template data-context is being found and that their profile is populated with a firstname property, and I'm still getting the same error.
Based on another question I found I tried this. I added a Template rendered callback function like this:
client/templates/helpers/childResetPassword.js
Template.childResetPassword.rendered = function() {
console.log(this);
};
I do see this.data containing the correct user object in the browser console, but my this.data.profile.firstname still fails, yet again, with the same console output error. If there something I need to do between the template rendering and the template helper?? SO CONFUSED!!!
You don't have to mention data ... you can just call this.profile.firstname. Your application already understands 'this' as the data object that is returned.
Template.childResetPassword.helpers({
childFirstName: function() {
return this.profile.firstname;
}
});
So, #Joos answer is not wrong, however after a lot more trial and error I found the solution for the meteor project I am working on.
My project (unbeknownst to me until I looked around more) has the meteor package autopublish removed. Therefore in order to access data in my collections I have to be subscribed to them. So the best place I put this subscription line is within my Router.route declaration for this template:
Router.route('/reset-password/child/:_id', {
name: 'reset-child-password',
template: 'childResetPassword',
layoutTemplate: 'appLayout',
waitOn: function() { // this is new new line/option i added to my route
return Meteor.subscribe('users');
},
data: function() {
if (this.ready()) {
var childUser = Users.findOne({_id: this.params._id});
if (childUser)
return childUser;
else
console.error("Child User not found", childUser);
}
else {
this.render("Loading");
}
}
});
So with that being said, if you still have autopublish package in your project and you intending to keep it, then #Joos answer is all you need to do.
However, if you DO intend to remove it, you need my router solution above, combined with making sure that you have published your users collection like this somewhere on the server:
server/publications.js
Meteor.publish("users", function () {
return Meteor.users.find();
});
I'm trying to implement a basic route using Flow Router. But no matter what _id of a collection document I request; I always only get the info about the first document in my collection - 'Requests'.
So here's my route definition in the file /lib/routes.js:
FlowRouter.route('/requests/:reqId', {
subscriptions: function (params, queryParams) {
this.register('theRequest', Meteor.subscribe('singleRequest', params.reqId));
},
action: function (params, queryParams) {
FlowLayout.render('layout', { aside: 'aside', main: 'singleRequest' });
console.log("Yeah! We are on the post:", params.reqId);
},
name: 'aRequest'
});
Here's my helper:
Template.singleRequest.helpers({
getRequest: function () {
return Requests.findOne();
}
});
Here's my server publish:
Meteor.publish('singleRequest', function (reqId) {
return Requests.find({ _id: reqId});
});
And here's the template:
<template name="singleRequest">
{{#if isSubReady 'theRequest'}}
{{#with getRequest}}
<h2>{{_id}}</h2>
<p>{{reqFrom}}</p>
<p>{{reqBy}}</p>
{{/with}}
{{else}}
loading...
{{/if}}
</template>
What am I doing wrong?
Note: In the console, I do see right 'reqId' slug due to the console.log command in the route definition. But I do not see the relevant info for the document which it belongs to.
Thanks!
OK my problem was that I previously had another subscription where I published all the Requests - not just the one with that certain _id. And because I did not create the helper to get only the one with that certain _id; of course server just sent me the very first request.
My solution was only to subscribe to previous subscription and define in the helper to fetch to the request _id:
FlowRouter.route('/requests/:reqId', {
subscriptions: function (params, queryParams) {
this.register('allRequests', Meteor.subscribe('Requests', params.reqId));
},
action: function (params, queryParams) {
FlowLayout.render('layout', { aside: 'aside', main: 'singleRequest' });
console.log("Yeah! We are on the post:", params.reqId);
},
name: 'aRequest'
});
and the helper:
Template.singleRequest.helpers({
getRequest: function () {
var reqId = FlowRouter.getParam("reqId")
return Requests.findOne({_id:reqId});
}
});
For anyone who browses to this question looking for how to get Flow Router to capture and dynamically link to slugs from the db, then call a template page for each item, I made a very simple example app and posted it on here on GitHub.
Hope it will help someone.
I believe your code is correct for what you are trying to do. However, I infer from your direct replication of the code from flow-router that you're pretty new to Meteor. Therefore, I'm willing to take a punt that you still have the autopublish package in your app.
Autopublish pushes all data in your collection to the client. Even without a publish/subscribe call.
You can do two things. To keep autopublish (which will make your development process easier at the start but maybe harder later on), just change your template helper to:
Template.singleRequest.helpers({
getRequest: function () {
return Requests.findOne({ _id: FlowRouter.getParam("reqId")});
}
});
Alternatively (and you will want to do this eventually), go the the command line and type:
meteor remove autopublish
You can read up on the pros and cons of autopublish in lots of places to make your own decision on which option is best for you. However, you should also consider whether or not you want to cache your data in future (e.g. using subsManager) so you may want to change your template helper regardless.
I have a problem implementing a non shuffling each over a reactive collection.
With this I mean that I have a collection ordered by a score value, and I don't want to change the order even if a score change.
I'd like it to be reactive but without the list items changing position every time.
The current code is this
ListController = FastRender.RouteController.extend({
find: function (tag){
if(!!tag){
return Lib.Items.Tags.SupportedType[tag].find;
}
return {};
},
findOptions: function(order, pageNum) {
var findOption = { limit: Lib.Items.Settings.ItemsPerPage * (parseInt(pageNum) || this.increment)};
if(!!order){
_.extend(findOption, Lib.Items.ListOrders.SupportedType[order].findOption);
}
return findOption;
},
waitOn: function() {
return [
Meteor.subscribe('items.all', this.params.tag, this.params.order, this.pageNum),
];
},
data: function () {
return {
"items": Items.find(this.find(), this.findOptions(this.params.order, this.params.pageNum))
};
},
action: function(){
this.render();
}
});
[...]
this.route('list', {
path: '/list/:pageNum?',
controller: ListController
});
this.route('list_order_limit', {
path: '/list/order/:order/:pageNum?',
controller: ListController
});
this.route('list_tag_limit', {
path: '/list/tag/:tag/:pageNum?',
controller: ListController
});
this.route('list_tag_order_limit', {
path: '/list/tag/:tag/order/:order/:pageNum?',
controller: ListController
});
and
{{#each items}}
{{> item}}
{{/each}}
With this everything works but the list change orders (which is correct but not the behaviour I'd like).
I tried some different solutions but nothing worked.
I started by modifying the data.
I put
data: function () {
return {
"items": Items.find(this.find(), this.findOptions(this.params.pageNum))
};
},
In this way the data knows nothing about the ordering (only the subscription knows about which order is used to take the first number of items items).
The problem is that in this way even if the route change from one ordering to another the list is not updated. And while this happens I see the subscription getting called with the new order parameter (so the data sent via ddp should actually be correct).
I even tried stopping and resubscribing but nothing changed.
What I'm doing wrong? Is there a better way to have a list reactive in its item but not in its order?
Thanks in advance.
I don't see anything in your code that explicitly references the order by the score value; however I believe this is an unresolved issue in Meteor: https://github.com/meteor/meteor/issues/1276.
One of the workarounds suggested in that thread was to add an explicit sort by _id on the client, which will ensure that the cursor is stable even when items change.
I can imagine a more unconventional way to work around this, which is to compute the desired order in the publish function and use the publish API to send custom fields over to the client, and then sorting by these fields. This is more complicated than a cursor but will result in the items showing up in the exact order determined at publish time.
I'm building a free TV-tracking app with Meteor, and along the way I seem to have hit a wall. A part of my template contains this:
<template name="results">
<div class="row">
<div class="span6 offset6">
<h4>Search Results</h4>
<ul>
{{#each series}}
<li><a href="http://thetvdb.com/?tab=episode&seriesid{{tvdbseriesid}}&lid={{tvdblid}}">{{seriesname}}</li>
{{/each}}
</ul>
</div>
</div>
</template>
Then, within my client js code, I do this:
Template.search.events({
'click #search' : function(evt) {
evt.preventDefault();
var query = $('#query').val();
if (query) {
Meteor.call('queryshow', query, function(error, result) {
Template.results.series = result;
console.log(Template.results());
});
}
}
});
The "queryshow" server method is just a Collection query method that returns an object containing the data that my template needs.
The problem is this however: the change isn't reflected in the browser window. I can't seem to figure out why, because the console.log(Template.results()) call shown below returns the correct html that I am expecting.
How do I fix this? I tried looking around Meteor's docs and I can't seem to find a function that forcibly re-renders a Template. Any help will be much appreciated!
Template.results.series should be a function that returns the series, rather than set directly to the series you want to use. That way, when Meteor runs the function to get the series, it can register what the function depends on, and then re-run the function whenever any of the dependencies change.
The easiest way to register a dependency for your information is to use the Session object. So your code would look something like:
Template.results.series = function () { return Session.get('resultsSeries'); }
Meteor.call('queryshow', query, function (err, res) {
// handle the error if it's there
// ...
// Then set the session variable, which will re-run anything that depends on it.
Session.set('resultsSeries', res)
}