Meteor js - show template only for clicked item - meteor

I just need to have a "slideout" displayed for a clicked on list of items. For example, 20 items in the list, click on item 2, item 2 should display the slideout. Not sure why I'm failing here, but when I click, the slideout template displays across all 20 items. Perhaps its the each loop?
HTML:
{{#each wantJobs}}
<div id="bidelement" class="bidelement">
{{#if clickedBidElement}}
{{>bidelementslideout}}
{{/if}}
</div>
{{/each}}
And my js with helpers and reactive var:
OnCreated:
this.clickedBidElement = new ReactiveVar;
this.clickedBidElement.set(false);
Helper for clickedBidElement check:
clickedBidElement : function() {
return Template.instance().clickedBidElement.get();
}
Click event:
'click .bidelementinfocontainer' : function(e,template) {
if (template.clickedBidElement.get() == false) {
template.clickedBidElement.set(true);
}
}
What am I missing here thats causing all "Bid Elements" to draw a slideout when I click on only one? Thanks.

Related

rendering alternate components under the same flow:router URL

How can I render two different views in a one page app without changing URLs. I'm using meteor with the default blaze as well as the flow:router package. Right now I have it set up like this:
routes.js..
FlowRouter.route("/", {
name: "App.home",
action() {
BlazeLayout.render("App_body", {
main: "App_home",
mainContent: "calendar"
});
}
});
FlowRouter.route("/list", {
name: "App.list",
action() {
BlazeLayout.render("App_body", { main: "App_home", mainContent: "list" });
}
});
but this way I'm using the url /list and i dont want that. I would like to simply render an alternate component template in the same url. I'm very new to coding so forgive me if this is obvious. Essentially I just want two different view styles: a list and a calendar. So I would like a way to set it up so that a spacebars template can be rendered if a certain button is clicked, and a different one can be rendered instead if another button is clicked.
Thanks so much for any help, i've been at this for a couple of days :)
Create another template, which renders particular view conditionally. Something like this:
FlowRouter.route("/", {
name: "App.home",
action() {
BlazeLayout.render("App_body", {
main: "App_home",
mainContent: "listOrCal"
});
}
});
<template name="listOrCal">
{{#if showList}}
{{> list}}
{{else}}
{{> calendar}}
{{/if}}
<button id="switchView">Switch view</button>
</template>
Template.listOrCal.onCreated(function listOrCalOnCreated() {
this.showList = new ReactiveVar(true);
})
Template.listOrCal.helpers({
showList() {
return Template.instance().showList.get();
}
})
Template.listOrCal.events({
'click #switchView' {
let showList = Template.instance().showList.get();
Template.instance().showList.set(!showList);
}
})
You can handle this within a single template like so:
FlowRouter.route('/', {
name: 'App.home',
action() {
BlazeLayout.render('App_body', { main: 'App_home', mainContent: 'ListOrCalendar' });
}
And then the ListOrCalendar template would look like this:
{{#if displayList}}
{{> List}}
{{else}}
{{> Calendar}}
{{/if}}
<button>Switch</button>
You would set up a ReactiveVar in the ListOrCalendar template:
Template.ListOrCalendar.onCreated(function() {
const instance = this;
instance.displayList = new ReactiveVar(true);
});
See ReactiveVar explanation here (ignore Session)
Then you would have a helper which returns the value of your ReactiveVar:
Template.ListOrCalendar.helpers({
displayList() {
const instance = Template.instance();
return instance.displayList.get();
}
});
Finally, you would hook up an event to change the value of displayList to switch between templates:
Template.ListOrCalendar.events({
"click button"(event, instance) {
const displayListCurrent = instance.displayList.get();
const displayListNew = !displayListCurrent;
instance.displayList.set(displayListNew);
// or, more concisely, instance.displayList.set(!instance.displayList.get());
}
});
So, in summary:
When the template is created, your ReactiveVar is true
so your displayList returns true
so the #if displayList condition in the template is satisfied
and so the List template is displayed
When the button is clicked
The ReactiveVar is set to false
so the displayList helper returns false
so the #if displayList condition in the template is not satisfied and it goes to the else statement
and so, finally, the Calendar template is displayed
When the button is clicked again, the ReactiveVar is toggled back to true, and on we go as above
This might seem daunting or over-complicated, but there's nothing fancy going on here at all. You'll get used to it pretty quickly

Gridster add_widget with Meteor

I have some troubles wihth gridster & meteor. At first, I loaded the whole widgets into my template and then recalculate the grid with a method below.
I have a template named dashboard, in this template I do a loop through my widgets and call a second template called widgetTmpl that contains all the formatted html
<template name="dashboard">
<div id="dashboardBody">
<button id="configMode" class="btn btn-primary"> <i class="fa fa-pencil"></i>Configuration </button>
<div class="gridster">
<ul id="widgetItemList" class="widget_item">
{{#each activeWidgets}}
{{> widgetTmpl}}
{{/each}}
</ul>
</div>
</div>
</template>
I execute this code with the callback onRendered
Template.dashboard.onRendered(function(){
var gridsterUl = jQuery(".gridster ul");
gridsterUl.gridster({
widget_margins: [5, 5],
widget_base_dimensions: [25, 25],
resize : {
enabled : true
},
draggable: {
start: overlay_fix_start,
stop: overlay_fix_stop
},
serialize_params : function($w, wgd){
return {
id : $w.prop('id'),
col : wgd.col,
row : wgd.row,
size_x : wgd.size_x,
size_y : wgd.size_y
};
}
});
});
This works fine, but when I add or reload a widget, I have to refresh the page due to the onRedered callback.
I heard about a gridster.add_widget methods, that does the perfect job but I don't know how to implement it to my code.
Where should I use the gridster.add_widget ?
There is methods like Blaze.insert and Blaze.renderWithData but I have no idea how to use it
I've never tried using Gridster, however I have integrated d3 into quite a few of my meteor apps. When I want the d3 context to reactivity change without a page refresh, I use a Tracker.autorun in my onRendered callback with a reactive datasource. For example my simplified code would look like this:
Template.d3Plot.onRendered( function () {
Tracker.autorun(function() {
// Function That Draws and ReDraws due to Tracker and Reactive Data
drawPlot(Plots.find());
});
});
drawPlot = function (plotItems) {
.....
}
Where Plots is my mongo collection so that whenever a new item is inserted/updated in the collection, the drawPlot function re-fires.

Can I pass the this._id value from one template helper to another with Meteor?

I have the following templates (.html) with their respected managers (.js files):
adminManageCategories
adminAddCategory
adminUpdateCategory
Consider the following:
<template name="adminManageCategories">
{{#each category}}
<div class="clickme">{{title}}</div>
{{/each}}
{{> adminUpdateCategory}}
</template>
Notice the {{> adminUpdateCategory}} is outside of the iteration. This is also a form, and I want to keep it on the same page.
And admin_manage_categories.js
Template.adminManageCategories.events({
"click .clickme": function(event) {
event.preventDefault();
console.log(this._id);
}
});
Notice the console.log() function, which works, as the template manager is smart enough to know the ID of the item that was clicked.
What I want to do is load this items values into the form when clicked. My example above is slim, but in my real data I have a title, sort order, among other things.
So my question is, what is the proper way to pass the _id from the adminManageCategories template to the adminUpdateCategory template, which is the form?
I can hack at this with JavaScript and make things happen, but I think I'm missing a "Meteor way" of doing things.
You need to use a ReactiveVar to store the currently clicked item.
First you need to run meteor add reactive-var, as it's not a package added by default in a standard meteor web app.
JS:
Template.adminManageCategories.created=function(){
// instantiate the reactive-var in the created callback
// we store it as a property of the template instance
this.currentItemId=new ReactiveVar(null);
};
Template.adminManageCategories.helpers({
// this helper reactively returns the currently clicked item
currentItem:function(){
// retrieve the reactive-var from the template instance...
var currentItemId=Template.instance().currentItemId.get();
// ...to fetch the correct collection document
return Items.findOne(currentItemId);
}
});
Template.adminManageCategories.events({
"click .clickme": function(event,template) {
event.preventDefault();
// assign the correct item id to the reactive-var attached to this template instance
template.currentItemId.set(this._id);
}
});
HTML:
<template name="adminManageCategories">
{{#each category}}
<div class="clickme">{{title}}</div>
{{/each}}
<p>Current item title is : {{currentItem.title}}</p>
{{! pass the currentItem as a parameter to your child template this will be
accessible as {{item}} in the HTML and "this.item" in JS helpers or
"this.data.item" in created/rendered/destroyed callbacks}}
{{> adminUpdateCategory item=currentItem}}
</template>
EDIT:
When I initialize the reactive-var in the created callback, I set it to null, this means that until one item is clicked, the helper will return null too and when you'll try to access this.item._id in the adminUpdateCategory this will fail.
The simplest way to solve this issue is maybe to not initialize the variable to null but to the first item in the collection.
Template.adminManageCategories.created=function(){
var firstItem=Items.findOne({},{
sort:{
sortedField:1
}
});
this.currentItemId=new ReactiveVar(firstItem && firstItem._id);
};
There may still be a case when you have 0 items in the collection, so you'll probably end up having to guard against the existence of the item in the JS.
Template.adminUpdateCategory.helpers({
itemProperty:function(){
return this.item && this.item.property;
}
});

Meteor.js: how to swap partial templates in and out of the main page

Just starting with meteor.
Looking for a way to have a single "main page", which would contain an area, in which different partial templates could be swapped in and out, at a click of a Next/Previous buttons.
I understand how to include partial templates statically by using {{> step_1_Template}} syntax.
What I need, is to have the Next/Previous buttons permanently on the main page, and, when the Next button is clicked - remove the {{> step_1_Template}} and insert the {{> step_2_Template}}.
How is this done?
My knee-jerk reaction is that you should just use iron-router. However, that may only make sense if you are swapping out templates based on routes. If you are sticking with the same route and only changing the partials then you can accomplish this with session variables.
When the user clicks the 'next' button you can set a session variable like:
Template.myTemplate.created = function() {
Session.setDefault('currentStep', 1);
};
Template.myTemplate.events({
'click #next': function() {
var step = Session.get('currentStep');
return Session.set('currentStep', step + 1);
}
});
Then you can add a helper like:
Template.myTemplate.helpers({
isStep: function(n) {
return Session.equals('currentStep', n);
}
});
Finally your template can select the proper partial based on the session:
<template name='myTemplate'>
{{#if isStep 1}}
{{> step_1_Template}}
{{/if}}
{{#if isStep 2}}
{{> step_2_Template}}
{{/if}}
</template>

What is the proper way to manipulate template instance in Meteor framework?

I am new to Meteor and wondering how to solve what seems to me is a common problem.
Let's say I have a handlebars template listing restaurants:
<template name="Restaurants">
{{#each Restaurant}}
{{name}}
{{/each}}
</template>
Now when user clicks on a restaurant template I want to display a menu for that restaurant.
I added a subtemplate named "menuItems" that contains all menu items for a given restaurant:
<template name="Restaurants">
{{#each Restaurant}}
{{name}}
{{> menuItems}}
{{/each}}
</template>
I want to render only one instance of menuItems subtemplate when user clicks anywhere on Restaurant template (render only the menu items for the selected restaurant).
It should go something like:
Template.Restaurants.events({
'click' : function (e) {
// This is where I need help - what's the right way to display only one subtemplate instance?
}
});
My question is - how I can select and display only the correct menuItems template instance?
Also I would like to place menuItems template instance in DOM only after the click and not before (having all the menu items for all restaurants and only hiding those divs is not an option because of high number of those items in db).
If you think I should approach the solution in some other way please let me know, thanks!
You should use {{#if}} and Session. Like this:
<template name="Restaurants">
{{#each Restaurant}}
{{name}}
{{#if restaurantSelected}}
{{> menuItems}}
{{/if}}
{{/each}}
</template>
By using Session, a reactive data source, you can set a global flag indicating whether a restaurant is selected.
Template.Restaurants.restaurantSelected = function() {
// check whether this restaurant is selected. "this" refers to the current
// context, eg. the current restaurant in the loop
return Session.equals("restaurantSelected", this._id);
}
Whenever you change that session key, the value will update and the template will be redrawn. So, you can toggle it when clicking a restaurant:
Template.Restaurants.events({
'click' : function (e) {
// store the current restaurant ID
// make sure the event selector is correct!
Session.set("restaurantSelected", this._id);
}
});
Edit For clarity's sake I created a complete example that you can copy into your project and try out.
I almost always avoid Session. I think it pollutes the global scope. Also it prevents you from running multiple instances of the template. I recommend using a reactiveVar or reactiveDict scoped to the template instance. Thanks to Rahul for starting a demo project. I took his example and modified it to show my recommended approach.
attach a reactiveDict to the template instance onCreate. Use this to store state instead of global Session var!
Template.Restaurants.onCreated(function() {
this.state = new ReactiveDict;
this.state.set('currentRestaurant', null); // could set a init value here
});
this event handler will set the state of the reactiveDict on click
'click': function(e, t) {
t.state.set('currentRestaurant', this._id);
}
this helper is used to show/hide the menu template
currentRestaurant: function() {
// check whether this restaurant is selected. "this" refers to the current
// context, eg. the current restaurant in the loop
return Template.instance().state.equals("currentRestaurant", this._id);
},
menu template receives the selected id from data context instead of from Session
<template name="Restaurants">
<ul>
{{#each Restaurant}}
<li>
{{name}}
{{#if currentRestaurant}}
{{> menuItems restaurant=_id}}
{{/if}}
</li>
{{/each}}
</ul>
</template>
<template name="menuItems">
<ul>
<li class="menu">I'm a menu for {{restaurantName}}!</li>
</ul>
</template>
added this helper just to show we really got the id
Template.menuItems.helpers({
restaurantName: function() {
var restaurantMenu = Restaurants.findOne(this.restaurant);
return restaurantMenu.name;
},
})
Posted a fully working project to github.
https://github.com/white-rabbit-japan/scopedReactivityDemo
App is hosted on meteor.com
http://scopedreactitivydemo.meteor.com/

Resources