Handlebars + Meteor + iron-router - meteor

I am using iron-router for my meteor project and everything was going fine but I just ran into some strange behavior.
I have a loop set up for a list of items that looks something like this.
{{#each list_items}}
<div>{{user.username}}
Click here!
</div>
{{/each}}
The JSON object for my user looks something like this:
{
user: {
username: jdoe
},
images: {
low-res-url: http://example.com
},
link: http://example.com/profile
}
Now the {{user.username}} shows up as expected but when I try to put the {{link}} in the href I get an error from iron-router saying
"You called Router.path for a route named undefined but that that route doesn't seem to exist. Are you sure you created it?"
Any help or advice would be appreciated.

Under the hood Iron-Router registers handelbars helper:
Handlebars.registerHelper('link', function (options) {
...
});
Simply change field link to different name like my_link.

As #perhelium mentioned Iron-Router has specified a helper named 'link'
Handlebars.registerHelper('link', function (options) {...});
In order to access an item named 'link' in your JSON object you need to explicitly refer to the JSON object itself.
So your line: Click here!
Would need to be specified as Click here!

Related

Meteor not displaying data from collection in html

I am trying to get data from a collection in meteor and using a helper passing it to a template.
Here is my code in collection:
Meteor.publish('displayCustomers', function tasksPublication() {
return Customers.find();
});
Below code in template JS file
Template.customerlist.onCreated(function() {
Meteor.subscribe('displayCustomers');
});
Template.customerlist.helpers({
displayCustomers :function(){
console.log(Customers.find({}));
return Customers.find({});
},
});
Template:
<template name="customerlist">
<h1>All registered users</h1>
{{#each displayCustomers}}
{{fname}}
{{/each}}
</template>
It is only displaying HTML content i.e. <h1>All registered users</h1>
Check that your publication is returning values to the client with this MiniMongo chrome extension
Check to make sure Customers is defined on the server and that your publish block is running only on the server.
Also I would toss a debugger into your onCreated block to make sure that your subscribe is being initialized.
Other than that, your code looks fine. I would try installing MeteorToys Mongol for client pub/sub debugging. https://atmospherejs.com/msavin/mongol
You need to actually fetch documents in your template :
Template.customerlist.helpers({
displayCustomers :function(){
return Customers.find().fetch(); //will return an array with all published documents
},
});

meteor iron-router with autopublish not getting prefilled fields in update form

probably that has nothing to do with autoform but since I am not entirely sure, I thought I'd put this information here, too. In my form I do not get any prefilled fields which I would expect of an update form and also there is some strange output in my console that I do not understand.
The corresponding router part is:
// app/lib/routes.js
Router.route('/assignment/:_id', function(){
var assignment = Assignments.findOne({_id: this.params._id});
console.log('ass w/' + this.params._id);
console.log(assignment);
this.render('Assignment', {data: assignment});
}, {name: 'assignment.show',
waitOn: function(){
console.log('do the wait');
Meteor.subscribe('assignments');
}
}
);
As you see, there is a lot of debug output in there already. The route seems to be called correctly because the template is displayed after click.
Publishing is made here:
// app/server/publish.js
Meteor.publish('assignments', function() {return Assignments.find();})
Now, this code seems to be called multiple times. The overall output after getting into this route is:
do the wait
routes.js:22 ass w/M2gtLf9vbbWCTgxze
routes.js:23 undefined
debug.js:41 insert failed: Access denied. No allow validators set on restricted collection for method 'insert'.
routes.js:27 do the wait
routes.js:22 ass w/M2gtLf9vbbWCTgxze
routes.js:23 Object {_id: "M2gtLf9vbbWCTgxze", title: "Neuer Titel", priority: "high", description: "just do it"}
(The insert error probably comes from some other place) There might be some other bugs in there, too, but I hope the output shows the problem. What I do not get is that I seem to not get the assignment at first but in the second call it is received, which is strange to me (why does this happen?). I am not so sure about folder structures, but isn't publish.js called after route.js, so that there might something unpublished? If yes, where to put my files then?
Probably not important but this is my assignment.html
<template name="Assignment">
<div class="panel panel-default" id="main">
<div class="panel-heading">Change Assignment</div>
<div class="panel-body">
{{>quickForm collection="Assignments" id="updateAssignmentForm" type="method" meteormethod="updateAssignment"}}
</div>
</div>
</template>
So, why is the router called twice, why is there no output at first and why do I not get any prefilled fields here? (all fields are just empty in my form)
Your route function is running before the subscription has returned data. You can simply put this in a data function and it will wait:
Router.route('/assignment/:_id',{
data: function(){
return Assignments.findOne({_id: this.params._id});
},
name: 'Assignment',
waitOn: function(){
return Meteor.subscribe('assignments');
}
});

Template empty initially but renders properly on changing and coming back to route

I have a template named profile which contains three other templates. One of these templates is {{> postlist}}
and the helper function for this template is
Template.postlist.helpers({
posts: function() {
return Posts.find({rph: {$in : postsArr}});
}
});
The problem is on going to the route, postlist template is empty, since postsArr is calculated later after the dom has loaded on the basis of other two templates. But, if I click on other route and come back to this route, the template renders properly.
What should I do that template renders properly initially itself?
The easiest way would be to us Session, though it's probably the worst option:
Template.postlist.helpers({
posts: function() {
return Posts.find({rph: {$in : Session.get('postsArr') }});
}
});
If you now call Session.set('postArr', ...) anywhere in your code the posts helper will update automatically. The second option is to use a shared reactive variable:
var postsArr = new ReactiveVar();
and then inside your helper:
return Posts.find({rph: {$in : posts.Arr.get() }});
Now you can do postsArr.set(...) and everything should work fine. Just remember to meteor add reactive-var do your project.
One last doubt is: where to put that reactive variable declaration? In most cases you can do away with putting in a single "controller" file. It will work as long as:
- you only have one instance of your template a time
- the code which sets ad gets the value of you reactive variable may be put in the same file
If one of the above conditions does not hold, then the only option to go, which is BTW the best possible, is to put your state variable in your template's scope. This is how you do it:
Template.postsList.created = function () {
this.postsArr = new ReactiveVar();
};
Template.postlist.helpers({
posts: function() {
return Posts.find({rph: {$in : Template.instance().postsArr.get() }});
}
});
From helpers you can always access postsArr using the Template.instance() routine which always return the current template instance, for which the helper was called. From event handlers, note that the second argument of your handler is always the template instance, which you're interested in.
If you need to access it from another templates, then you should probably put your state variable on the corresponding route controller. Assuming you're using iron-router, that would be:
Iron.controller().state.get('postsArr');
The Iron.controller routine grants you access to the current route controller. Read this for more details.

meteor app: how to properly use a button to influence the route

I'm working on a survey/wizard type of meteor application.
The interface will only have two navigation buttons, who's caption/value will be determined dynamically based on whatever step a given user is currently at.
My question is: what markup syntax should I use within each of these two button's definitions in order to direct the Iron Router to change the route. Another words, where in the button definition should I put this: this.redirect('/anotherpath') ?
If you have a button in your template. If you are using a hrefs check answer from f3rland.
html:
<button #id="mybutton>m<Button</button>
you can catch the buttonpress event with javascript
js
Template.myTemplate.events({
'click #myButton': function(event, template) {
event.preventDefault();
Router.go('anotherpath');
}
});
The router should look like:
Router.map(function() {
this.route('anotherpath', {
path: '/',
layoutTemplate: 'myLayout',
controller: myController
});
});
I suggest you to use Iron-Router pathFor helper in your template
{{buttonText}}
That will redirect the user to the corresponding page/template.
You should also take a look at new blaze pattern to define custom block helper
Edit:
You could also use a custom helper for dynamic route as mentioned here
UI.registerHelper("_foo", function fooHelper() {
var templateName = figureOutNameDynamically(this.context.criticalInfo);
return Template[templateName];
});
<template name="foo">
{{> _foo context=.. atts=this}}
</template>
you can do this/change the route by using the action option which will automatically cal this.render().
you can do it yourself by calling the action function but I guess it is better to use onBeforeAction/after hooks

Meteor Scope error when using Autoform

Using autoform and dependencies plus iron router. Autopublish is on and I'm seeing the collection on the client console. New project on .8, everything newly installed.
in schema.js, which I've tried in a few locations (/lib, /)
Tvseries = new Meteor.Collection("tvseries", {
schema: {
title: {
type: String,
label: "Title",
max: 250
},
airStartDate: {
type: Date,
label: "First episode air date"
}
}
});
Then a very basic autoform taken from the example:
<template name="addseries">
{{> quickForm collection="tvseries" id="inserttvseriesForm" type="insert"}}
</template>
Plus a route that is just loading this form:
Router.map(function () {
this.route('addseries', {
path: '/addseries',
template: "addseries"
});
});
I get this message in the JS console:
Exception from Deps recompute function: Error: tvseries is not in the window scope.
You have a typo:
<template name="addseries">
{{> quickForm collection="Tvseries" id="inserttvseriesForm" type="insert"}}
</template>
Your collection is named as Tvseries, not tvseries.
In case Serkan's typo suggestion didn't work, and for anyone looking for relief on this since this shows up on google and is how I got here:
From the docs (this section is buried too far down IMO)
Should the value of schema and collection have quotation marks around it?
If you use quotation marks, then you are telling the autoform to "look for an object in the window scope with this name". So if you define your collections at the top level of your client files and without the var keyword, then you can use this trick to avoid writing helpers.
If you don't use quotation marks, then you must define a helper function with that name and have it return the SimpleSchema or Mongo.Collection instance.
So you'd need a helper function that would look like this:
Template.addseries.helpers({
Tvseries: function () {
Return Tvseries;
}
});
And if you have a schema that is not attached to the collection, you'd also create another helper to return the schema so that you can call it from the template. The docs recommend registering this helper globally:
Template.registerHelper('Schemas', Schemas);

Resources