Close all open elements - meteor

I'm using reactive-var, currently the event below shows and hides the hidden div when it is clicked. Since there are a number of options I would like to change the event below to hide any of the open divs and only display the one I've clicked on. How would I write that?
Path: templates.html
<template name="jobOfferLayout">
{{#each jobOffers}}
{{> jobOfferCard}}
{{/each}}
</template>
<template name="jobOfferCard">
<div class="list-group">
<a class="list-group-item smallInfo">
<h4 class="list-group-item-heading">{{jobs}}</h4>
</a>
</div>
{{#if showFullContent}}
<div class="bigInfo">
Show big info
</div>
{{/if}}
</template>
Path: jobOfferCard.js
Template.jobOfferCard.onCreated(function(){
this.showFullContent = new ReactiveVar(false);
});
Template.jobOfferCard.helpers({
showFullContent: function(){
return Template.instance().showFullContent.get();
},
});
Template.jobOfferCard.events({
"click .smallInfo": function(event, template){
template.showFullContent.set(!template.showFullContent.get());
},
});

If your job offer cards have a unique id like _id, then you can save the id inside the ReactiveVar instead of saving true or false like this,
Template.jobOfferCard.onCreated(function(){
this.showFullContent = new ReactiveVar(false);
});
Template.jobOfferCard.helpers({
showFullContent: function(){
return Template.instance().showFullContent.get() === this._id;
},
});
Template.jobOfferCard.events({
"click .smallInfo": function(event, template){
template.showFullContent.set(this._id);
},
});
UPDATE
Template.jobOfferCard.onCreated(function(){
Session.setDefault('job-offer-card-opened', false);
});
Template.jobOfferCard.helpers({
showFullContent: function(){
return Session.get('job-offer-card-opened') === this._id;
},
});
Template.jobOfferCard.events({
"click .smallInfo": function(event, template){
Session.set('job-offer-card-opened', this._id);
},
});

Related

Meteor : create a search (filter) without using autopublish

I have a search facility in my project and I need to be able to publish results without using autopublish.
html
<template name="search">
<form id="searchform">
<input type="text" id="kategori" placeholder="Sila masukkan maklumat carian."/>
<button>Carian</button>
</form>
<hr/>
<h3>Maklumat</h3>
<ol>
{{#each profil}}
<li>{{jenama}}</li>
{{/each}}
</ol>
</template>
js:
Template.search.events({
"submit #searchform": function (e) {
e.preventDefault();
Session.set("kategori", e.target.text.value);
}
});
Template.search.helpers({
profil: function() {
return Profil.find({
kategori: Session.get('kategori'),
});
}
});
You can simply subscribe to a filtered publication:
client:
Template.search.events({
"submit #searchform": function (e) {
e.preventDefault();
Session.set("kategori", e.target.text.value);
Meteor.subscribe('profiles',Session.get('kategori'));
}
});
server:
Meteor.publish('profiles',function(kategori){
return Profil.find({ kategori: kategori });
});
If you don't have any other subscriptions to the same collection you can also simplify your helper to:
Template.search.helpers({
profil: function() {
return Profil.find();
}
});
Since the set of documents will be defined by your publication.
In practice though you usually use the same search in your helper as you do in the publication just to avoid documents from other publications showing up.

How can I route to a users profile page?

I show a list of items and for each item its belongs to a user. I list the item's name, description and the user who created it (by there name). I want to also have a link that goes to the user who created the item. Note: That I also link to an item's page.
Here is what I've done so far:
packages
aldeed:collection2
aldeed:simple-schema
iron:router
aldeed:autoform
useraccounts:iron-routing
accounts-password
accounts-base
useraccounts:core
zimme:active-route
todda00:friendly-slugs
reywood:publish-composite
client/items/listed_item.html
<template name="listedItem">
<a href="{{pathFor 'itemPage'}}">
{{name}}
</a>
<a href="{{pathFor 'profile'}}">
{{usernameFromId user}}
</a>
</template>
client/items/items.html
<template name="items">
<div class="items">
{{#each items}}
{{> listedItem}}
{{/each}}
</div>
</template>
client/subscriptions.js
Meteor.subscribe('items');
Meteor.subscribe('allUsernames');
Meteor.subscribe('users');
client/helpers.js
Template.items.helpers({
items: function() {
return Items.find();
},
});
Template.registerHelper("usernameFromId", function (userId) {
var user = Meteor.users.findOne({_id: this.userId});
return user.profile.name;
});
server/publications.js
Meteor.publish("items", function () {
return Items.find();
});
Meteor.publish("users", function () {
return Meteor.users.find({}, {
fields: { profile: 1 }
});
});
Meteor.publish("allUsernames", function () {
return Meteor.users.find({}, {
fields: { 'profile.name': 1, 'services.github.username': 1 }
});
});
lib/routes.js
Router.route('/items', function () {
this.render('items');
});
Router.route('/users/:_id', {
template: 'profile',
name: 'profile',
data: function() {
return Meteor.users.findOne({_id: this.params._id});
}
});
What this does though is show the items URL for the users URL, so it links to a user who doesn't exist. i.e. items/1234 = users/1234 when theres only users/1 in the system. How can I get it to link to the correct user ID?
use a link with the constructed URL
<template name="listedItem">
<a href="{{pathFor 'itemPage'}}">
{{name}}
</a>
<a href="http://yourdomain.com/users/{{user}}">
{{usernameFromId user}}
</a>
</template>
In this part:
Router.route('/users/:_id', {
template: 'profile',
name: 'profile',
data: function() {
return Meteor.users.findOne({_id: this.params._id});
}
});
you have referenced the route and created a route to a template with the name "profile" but I can't see any template with that name. Is it possible you just forgot to include it here? or that you really don't have that template?
Just making sure.
Try this:
Router.route('/users/:_id', {
template: 'profile',
name: 'profile'
}
In helper you can retrive that record id by this:
Router.current().params._id
I usually use this way:
In the template:
<div class="css-light-border">
{{title}}
</div><!-- / class light-border -->
In the router.js file:
Router.route('/document/:_id', function(){
Session.set('docid', this.params._id);
this.render('navbar', {to: "header"});
this.render('docItem', {to: "main"});
});
Basically here docid is the _id of the document which I want to display. Here is an example of how you pull from your database:
somehelper:function(){
var currentId = Session.get('docid');
var product = Products.findOne({_id:currentId});
return product;
I think it is an easy way because it uses Sessions.

Limiting each inside of each based on context

Having trouble trying to pass some sort of context into my little test chan-clone.
Problem is I have threads, with replies. Showing just the threads works great, showing threads with replies based upon the threads they come from not so much.
Question is how do I send context on the outer each. Thank you so much for the help!
meteorchan.html
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
</head>
<body>
<h1>Meteor Chan - Let's do this</h1>
<form class="new-thread">
<input type="text" name="text" placeholder="New Thread" />
<button>Submit</button>
</form>
<br/>
{{#each threads}}
{{> thread}}
{{#each replies}}
{{> reply}}
{{/each}}
{{/each}}
</body>
<template name="thread">
<div class="thread">
<span>Anonymous</span> No. {{iden}}
<br>
{{text}}
</div>
<a class="reply" href="#">Reply</a> : <button class="purge">Purge</button>
<form class="new-reply {{this._id}}" style="display:none;">
<input type="text" name="text" placeholder="Add Reply" />
<input type="text" name="thread" value="{{this._id}}" style="display:none;" />
<button>Add New Reply</button>
</form>
<br>
<br>
</template>
<template name="reply">
<div class="reply">
{{text}}
<br>
{{parentThread}}
</div>
</template>
meteorchan.js
Threads = new Mongo.Collection("threads");
Replies = new Mongo.Collection("replies");
if (Meteor.isClient) {
Meteor.subscribe("threads");
Template.body.helpers({
threads: function () {
return Threads.find({}, {sort: {createdAt: -1}});
},
replies: function () {
return Replies.find({}, {sort: {createdAt: 1}});
}
});
Template.body.events({
"submit .new-thread": function (event) {
event.preventDefault();
var text = event.target.text.value;
var currentId = 0;
if(Threads.findOne({}, {sort: {createdAt: -1}})){
currentId = Threads.findOne({}, {sort: {createdAt: -1}}).iden;
}
Threads.insert({
text: text,
createdAt: new Date(),
iden: currentId + 1
});
event.target.text.value = "";
},
"submit .new-reply": function (event) {
event.preventDefault();
var text = event.target.text.value;
var thread = event.target.thread.value;
Replies.insert({
text: text,
createdAt: new Date(),
parentThread: thread
});
event.target.text.value = "";
}
});
Template.thread.events({
"click .purge": function (){
Threads.remove(this._id);
},
"submit .new-reply": function(event){
event.preventDefault();
},
"click .reply": function(){
$('.'+this._id+'.new-reply').css('display','inherit');
}
});
}
if (Meteor.isServer) {
Meteor.publish("threads", function () {
return Threads.find();
});
Meteor.publish("replies", function () {
return Replies.find();
});
}
You can refer to the context in the template helper using this.
So in your replies helper this refers to the current thread you are iterating over using the each.
You can do the following to only fetch replies for the corresponding thread:
replies: function () {
return Replies.find({
// this refers to the current thread
parentThread: this._id
}, {sort: {createdAt: 1}});
}

iron:router will not re-render after route change with same template

How can I make Iron:router re-render a template?
I have this html:
<head>
</head>
<body>
</body>
<template name="mainLayout">
list
find
{{> yield}}
</template>
<template name="listTemplate">
<p>list</p>
</template>
and this js:
Router.configure({
layoutTemplate: 'mainLayout'
});
Router.route('/list', {
name: 'list',
template: 'listTemplate'
});
Router.route('/find', {
name: 'find',
template: 'listTemplate',
data: function () {
return this.params.query;
}
});
if (Meteor.isClient) {
Template.listTemplate.rendered = function () {
if (this.data)
console.log('find ' + this.data.q);
else
console.log('list all');
};
}
When I click on the links to switch views (simulated here with console.log), the route does change, but the template is not re-rendered.
Is there a way to force iron:router to re-render?
Setting the router controller state did not work for me. The answer Antônio Augusto Morais gave in this related github issue worked. Using the Session to store the reactive var to trigger the autorun reactiveness. It's a hack, but it works.
## router.coffee
Router.route '/profile/:_id',
name: 'profile'
action: ->
Session.set 'profileId', #params._id
#render 'profile'
## profile.coffee
Template.profile.onCreated ->
#user = new ReactiveVar
template = #
#autorun ->
template.subscription = template.subscribe 'profile', Session.get 'profileId'
if template.subscription.ready()
template.user.set Meteor.users.findOne _id: Session.get 'profileId'
else
console.log 'Profile subscription is not ready'
Template.profile.helpers
user: -> Template.instance().user.get()
## profile.html
<template name="profile">
{{#if user}}
{{#with user.profile}}
<span class="first-name">{{firstName}}</span>
<span class="last-name">{{lastName}}</span>
{{/with}}
{{else}}
<span class="warning">User not found.</span>
{{/if}}
</template>
You can try something like this:
Router.configure({
layoutTemplate: 'mainLayout'
});
Router.route('/list', {
name: 'list',
template: 'listTemplate',
action: function() {
this.state.set('query', this.params.query);
}
});
Router.route('/find', {
name: 'find',
template: 'listTemplate',
data: function() {
return this.params.query;
},
action: function() {
this.state.set('query', this.params.query);
}
});
if (Meteor.isClient) {
Template.listTemplate.rendered = function() {
this.autorun(
function() {
if (this.state.get('query'))
console.log('find ' + this.data.q);
else
console.log('list all');
}
);
};
}
The rendered method isn't reactive, that's why you need an autorun.
The template "this.data" isn't reactive so you're gonna need a reactive var to do that, either a Session variable, a controller state, or some kind of reactive var.
You may need to add the reactive-var package depending on what approach you take.

Iterating through an array in a Meteor.js collection

Background: writing a web application using Meteor.js where users can add board games to their profile. There is a Users collection and a Games collection.
I have the list of Games and each game has a button next to it so the user can add the game to their profile. The button will find the _id of each game and add it to a user.games array. The _id is a really long hash.
On the user's dashboard, I want to be able to display each game the user has "subscribed" to but I'm not sure how to access the user.games field.
Here is my code:
/server/publications.js
Meteor.publish('userGames', function () {
return Meteor.users.find({_id: this.userId},
{fields: {'games': 1}});
});
/client/main.js
Meteor.subscribe('userGames');
/client/views/dashboard/dashboard.html
<template name="dashboard">
<div class="dashboard">
{{#if hasGames}}
<ul class="games-list">
{{#each userGames}}
{{> game}}
{{/each}}
</ul>
{{else}}
<h3>You have not added any games to your chest</h3>
{{/if}}
</div>
</template>
/client/views/dashboard/dashboard.js
Template.dashboard.helpers({
hasGames: function(){
},
userGames: function(){
}
});
As you can see I'm not sure what goes into the dashboard.js helper function in order to be able to access the user.games field.
EDIT:
So I ran a test to see if the below answer works - I've updated the following:
dashboard.html
<template name="dashboard">
<div class="dashboard">
{{test}}
</div>
</template>
dashboard.js
Template.dashboard.helpers({
test: function(){
var user = Meteor.user();
return Games.find({_id: { $in: user.games }});
}
});
The console says "Uncaught TypeError: Cannot read property 'games' of undefined"
Finding all games for currently logged user
Games = new Meteor.Collection("userGames");
if (Meteor.isClient) {
Meteor.subscribe('userGames');
Template.hello.greeting = function () {
var user = Meteor.user();
if(user && Array.isArray(user.games)){
return Games.find({_id: { $in: user.games }}).fetch();
}else{
return [];
}
};
}
if (Meteor.isServer) {
Meteor.publish('userGames', function () {
return Meteor.users.find({_id: this.userId},
{fields: {'games': 1}});
});
}

Resources