Why isn't the URL being generated for this route? - meteor

So, I'm working on a Meteor project and I can't get this route to generate properly, or at all for that matter.
<template name="browseAll">
<h3>List of classes with books available!</h3>
<ul>
{{#each aggCount}}
<li>{{ _id }} ({{ count }})</li>
{{/each}}
</ul>
</template>
The data that is being iterated over is a result of aggregation using MongoInternals, and that is as follows:
(server/methods.js excerpt):
classCount: function() {
// Attempt aggregation of the books table to count by class, maybe.
var db = MongoInternals.defaultRemoteCollectionDriver().mongo.db;
var col = db.collection("books");
var aggregateSync = Meteor._wrapAsync(col.aggregate.bind(col));
var pipeline = [
{$group: {_id: "$class", count: {$sum: 1}}},
{$sort: {_id: 1}}
];
var theAnswer = aggregateSync(pipeline);
return theAnswer;
}
It seems that the data is coming through okay, and sample data from aggregation (coming into the template) looks like this:
[ { _id: 'ADNR1234', count: 2 }, { _id: 'ARTH1234', count: 1 } ]
That's the template code I've got, and this is the route that it's supposed to be working with:
this.route('browse-class', {
path: '/browse/:_class',
data: function() {
var booksCursor = Books.find({"class": this.params._class},{sort:{"createdAt": 1}});
return {
theClass: this.params._class,
numBooks: booksCursor.count(),
books: booksCursor
};
}
});
I don't understand it. The data is being SHOWN, and what I want to do is generate a URL for browse-class (route) that takes the value of {{ _id }} in the helper as a parameter, so as to generate something like this:
application.org/browse/CLSS

Be aware that {{pathFor}} must be called with a data context properly set :
{{#with class}}
{{pathFor "browse-class"}}
{{/with}}
Optionnaly it is possible to pass the data context as a parameter :
{{pathFor "browse-class" class}}
The data context provided to pathFor is used when generating the route path, if you defined a route path like this :
path: "/browse/:_id"
Then it will use the _id from the class to properly generate a URL.
For the text of the link, I doubt you want to display the _id, your class documents probably include a "label" so you could use this :
{{ label }}

Related

Search and Sort on the same page

I'm trying to implement sort and search to my items, so i started with sort and it works:
Template
<button class="sort">Sort</button>
{{#each cvs}}
{{> Interviu}}
{{/each}}
JS:
Template.Interviuri.onCreated(function () {
var self = this
self.autorun(function () {
self.sortOrder = new ReactiveVar(-1)
})
Template.Interviuri.helpers({
cvs() {
const instance = Template.instance()
return Cvs.find({}, { sort: { createdAt: instance.sortOrder.get() } })
},
})
Template.Interviuri.events({
'click .sort'(event, instance) {
instance.sortOrder.set(instance.sortOrder.get() * -1)
Next i wanted to implement Search on the same page. So the best way i could found was EasySearch.
But using EasySearch, it means i must change the way my items are being displayed. And then the sort doesn't work anymore.
Template
<div class="searchBox pull-right">
{{> EasySearch.Input index=cvsIndex attributes=searchAttributes }}
</div>
{{#EasySearch.Each index=cvsIndex }}
{{> Interviu}}
{{/EasySearch.Each}}
Collection
CvsIndex = new EasySearch.Index({
collection: Cvs,
fields: ['name'],
engine: new EasySearch.Minimongo()
})
JS
cvsIndex: () => CvsIndex,
How can i have both search and sort working at the same time?
With EasySearch you can use two methods on your index, namely getComponentDict() and getComponentMethods().
With getComponentDict() you can access search definition and options:
index.getComponentDict().get('searchDefinition');
index.getComponentDict().get('searchOptions');
You also have the corresponding setters to change the search definition/option.
getComponentMethods has mehods like
index.getComponentMethods().loadMore(integer);
index.getComponentMethods().hasMoreDocuments();
index.getComponentMethods().addProps(prop, value);
index.getComponentMethods().removeProps([prop])
From that you can set your prop, say index.getComponentMethods().addProp('sort', -1) and then on the index definition, in your MongoDB engine, set the sort from that prop:
index = new EasySearch.index({
// other parameters
engine: new EasySearch.MongoDB({
sort: function(searchObject, options) {
if(options.search.props.sort) {
return parseInt(options.search.props.sort);
}
return 1;
}
})
});
See EasySearch Engines for more info.

How to escape current blaze {{#each}} interation

This is the routes templateData
this.route('editSession', {
path: '/dashboard/events/:_id/editSession/:_sid',
template: 'editSession',
layoutTemplate: 'dashboard_layout',
data: function () {
var sId = this.params._sid;
var eId = this.params._id;
var templateData = {
session: Sessions.findOne({ _id: sId }),
event: Events.findOne({ _id: eId })
};
return templateData;
}
})
I'm trying to use the events object being passed through the router, but when I use events._id in the template, nothing renders because it is within the #each tags for the session. How can I escape it so I am able to use another data object?
{{#each sessions}}
<h3>Session</h3>
<p>{{name}}</p>
<p>{{description}}</p>
<p>{{startTime}} - {{endTime}}</p>
<button class="btn btn-danger">Edit Session</button>
{{/each}}
Please help. So stuck. Would be really appreciated :)
In spacebars you can use ../ to jump up one context level. Assuming event is in the enclosing context of the #each you can do this:
<a href="/dashboard/events/{{../event._id}}/editSession/{{_id}}">
Note: I'm using the singular event, as specified in your router.

Meteor & Mongo: addToSet inserting

I have some documents in my base:
//example docs
{"_id": "qwerty12345", "name": "Bob", "cards":["cardId1", "cardId2", "cardId3"]}
I'm using this for inserting data:
Template.insert.events({
'click add': function(){
if(confirm("Add card?"));
mycollection.update({_id: Session.get('fooId')}, { $addToSet: { cards: this._id}})
}
});
Then i'm using this helper for my template:
Template.index.helpers({
cards: function(){
query = mycollection.findOne({_id: Session.get('fooId')});
return query.cards;
}
});
And in template:
<img src="{{img}}" class="add">
{{#each cards}}
{{this}}<br>
{{/each}}
This works perfecty, but i have a trouble:
As you see, every image have id and url({{image}}), i'm need to add image url to 'mycollection' too for every card(on click).
How to make it?
And the second problem:
How to allow mongo insert duplicates to "cards" array?
Do you mean every card has id and image field? I guess so.
You can add nested object to an array fields. Like that
mycollection.update({_id: Session.get('fooId')}, { $addToSet: { cards: {id: this._id, image: this.image}}}).
In the template:
{{#each cards}}
{{this.id}}: {{this.image}}<br>
{{/each}}
For second problem: You can use $push instead of $addToSet

IronController Helpers not working

i am farely new to meteor, so it might be obvious.
I am trying to define some routes:
Router.route('/translations', {name:'translation.index'});
Router.route('/translations/:_id', {name:'translation.show'});
I also have defined a Controller which should define how to get the Data:
TranslationIndexController = RouteController.extend({
template: 'TranslationIndex',
data: function () {
return Translations.find();
},
sayHello: function(){
return 'hello';
}
});
I have some Collection that is just fetched and some random tempalte helper. My template looks like this:
<template name="TranslationIndex">
TestOutput:
{{sayHello}}
{{#each data}}
<li>askljdfh</li>
{{/each}}
</template>
But neither my hello nor my collection is shown. PS: I have checked if my collection contains data by using Translations.find().count() in the console.
You need to call the controller in your route, e.g.,
Router.route('/translations', {
name:'translation.index',
controller: TranslationIndexController
});
The data should then be available.

Display error with using firebase-element inside template repeat

<template repeat="memberId in members | objKeys">
<firebase-element data={{member}} location="{{'SOME_LOCATION/' + memberId}}"></firebase- element>
<h2>member.name</h2>
</template>
objKeys: function(members) {
return Object.keys(members);
}
the data looks like this
members = {
'memberId_1': true,
'memberId_2': true,
'memberId_3': true
}
and at another location store actual users data.
Here, I'm expecting the template repeat to render each user (member1, member2, member3) accordingly. However, it prints out same name for 3 entries as if it re-use the variable "member" for all 3 firebase element in the template repeat, which doesn't really make sense.
I've tried to modified objKeys functions to return
[ {memberId: memberId_1, member: {}},
{memberId: memberId_2, member: {}},
{memberId: memberId_3, member: {}]
then use the inner member object for firebase element but the result is still the same
<template repeat="{{item in members | objKeys}}>
<firebase-element data={{item.member}} location={{'SOME_LOCATION/' + item.memberId}}> </firebase-element>
</template>
Do I not understand template repeat correct and use it incorrectly here ? Or is it a bug with polymer template.
I think the problem is that the repeater binds {{member}} to itself.
Check your Firebase - you'll see that the binding not only displays the name name repeatedly in your view, it also set all the values to the same name in your Firebase.
Try this instead, using {{members[memberId}}:
<polymer-element name="member-test">
<template>
<firebase-element data="{{members}}" location="{{'https://YOUR_APP_NAME.firebaseio.com/members/'}}"></firebase-element>
<template repeat="{{memberId in members | objKeys}}">
<firebase-element data="{{members[memberId]}}" location="{{'https://YOUR_APP_NAME.firebaseio.com/members/' + memberId}}"></firebase-element>
<h2>{{members[memberId].name}}</h2>
</template>
</template>
<script>
Polymer({
objKeys: function(members) {
if(!members) return null;
return Object.keys(members);
}
});
</script>
</polymer-element>
This assumes that your Firebase has data in the following format:
{ members:
{ memberID1: { name: "Name1" },
memberID2: { name: "Name2" }
}
}

Resources