Meteor collection item id from template - meteor

I am loading a series of template from a collection like so.
sidebar/sidebar.html
<template name="Sidebar">
<ul id="slide-out" class="side-nav fixed grey darken-3">
<li class="action-bar">
<span id="add-new" data-target="modal-add" class="modal-trigger"><i class="small material-icons">add</i></span>
<span id="save"><i class="small material-icons">note_add</i></span>
<span id="rename"><i class="small material-icons">mode_edit</i></span>
<span id="delete"><i class="small material-icons">delete</i></span>
<span data-activates="slide-out" id="close" class="button-collapse close "><i class="small material-icons right">reorder</i></span>
</li>
<!-- Load save items-->
{{#if Template.subscriptionsReady}}
{{#each userSaves}}
{{>ListItem}}
{{/each}}
{{else}}
<p>Loading</p>
{{/if}}
</ul>
<i class="material-icons">menu</i>
<!-- Modal form to add new simulator file -->
<!-- Modal Structure -->
<div id="modal-add" class="modal">
<div class="modal-content">
<h4>New Simulator</h4>
{{> quickForm collection=saves id="newSimulator" type="insert" buttonClasses="modal-action modal-close btn waves-effect waves-light" buttonContent="Add"}}
</div>
</div>
</template>
sidebar/Sidebar.js
import { Template } from 'meteor/templating';
import { Saves } from '../../../../api/lists/SimulatorSaves.js';
import { Meteor } from 'meteor/meteor';
import './Sidebar.html';
import './SidebarListItem.js'
Template.Sidebar.onCreated(function() {
var self = this;
self.autorun(() => {
self.subscribe('saves');
})
});
Template.Sidebar.onRendered(function() {
// the "href" attribute of .modal-trigger must specify the modal ID that wants to be triggered
$('.modal-trigger').leanModal({
ending_top: '25%', // Ending top style attribute
});
});
Template.Sidebar.events({
'click .button-collapse': function() {
console.log("here")
$(".button-collapse").sideNav();
}
})
Template.Sidebar.helpers({
saves: () => {
return Saves;
},
userSaves: () => {
return Saves.find({});
}
});
I am trying to get the collection items id from the template so i can use it as a session variable but I am getting undefined.
sidebar/listItem.js
import { Template } from 'meteor/templating';
import { Meteor } from 'meteor/meteor';
import './SidebarListItem.html';
Template.ListItem.events({
'click .file-link': () => {
console.log(this._id);
}
});
sidebar/listitem.html
<template name="ListItem">
<li class="file-link"><i class="material-icons">description</i><span>{{name}}</span></li>
</template>
If any would could help me with this would be great thanks.

You don't have this when using fat arrow ;)

Related

How to add a multiple choice system for my gallery filter

I'm working on a fictive project for my webdev formation, and I would need just a little help in fixing my filter system for my gallery. It's pretty straightforward, you click on a category and the gallery displays the images corresponding to the category. Everything (finally) works pretty nicely, the only two problems that keep bugging me are:
-Once a category is checked, I can't uncheck it and have to check another category instead (which automatically unchecks the previous one);
-And as the title of my question suggests, I can only select one category at a time.
I would like the user to be able to check multiple categories in order to display all photos corresponding to the categories selected, thus meaning I would like the user to be able to uncheck a category, and if no categories are checked, then the gallery would display all photos of the api.
Thank you for your help and your research.
import Head from 'next/head'
import Image from 'next/image'
import { useRouter } from 'next/router'
import { useState } from 'react'
import Nav from '../components/nav'
import styles from '../styles/style.module.css'
export default function Gallery({ res, res2 }) {
const [targetCategory, setTargetCategory] = useState("");
return(
<main className={`${styles.container} ${styles.gallery}`}>
{/* NAVBAR */}
<div className={`${styles.menu}`}>
<Nav />
</div>
{/* GALLERY FILTER - MOBILE COLLAPSIBLE */}
<div className={`${styles.filter}`}>
<div className="container-fluid">
<nav className="navbar navbar-expand-lg">
<button className="navbar-toggler text-white"
type="button"
data-bs-toggle="collapse"
data-bs-target="#filterContent"
aria-controls="filterContent"
aria-expanded="false"
aria-label="Toggle navigation">
<span className="align-self-center">Filtres</span>
<i className="bi bi-list fs-1"/>
</button>
<div className="collapse navbar-collapse" id="filterContent">
<form id="img-sort" action="">
<ul className="nav navbar-nav">
{res2.data.map((category) => (
<>
<label key={category.id} className={styles.formControl}>
<input
type="checkbox"
name="checkbox"
id={category.attributes.name}
value={category.attributes.name}
checked={targetCategory === category.attributes.name}
onClick={(e) => setTargetCategory(e.target.value)}/>
{category.attributes.name}
</label>
</>
))}
</ul>
</form>
</div>
</nav>
</div>
</div>
{/* GALLERY CONTENT */}
<div className={`${styles.pictures_holder}`}>
<div className={`${styles.pictures}`}>
{res.data.filter((photo) => photo.attributes.category.data.attributes.name.includes(targetCategory)).map((photo) => (
<div key={res.id}>
<img
src={`http://localhost:1337` + photo.attributes.img.data[0].attributes.formats.small.url}
alt={photo.attributes.img.data[0].attributes.formats.small.name}
/>
<p className="text-white text-center">{photo.attributes.name}</p>
</div>
))}
</div>
</div>
</main>
)
}
export async function getStaticProps() {
try {
const res = await fetch("http://localhost:1337/api/photos?populate=*").then((res) => res.json());
const res2 = await fetch("http://localhost:1337/api/categories?populate=*").then((res2) => res2.json());
return {
props: { res, res2 }
};
} catch (err) {
console.error(err);
}
}

Gatsby: CSS by className only sometimes works [duplicate]

I'm almost new to react.
I'm trying to create a simple editing and creating mask.
Here is the code:
import React, { Component } from 'react';
import Company from './Company';
class CompanyList extends Component {
constructor(props) {
super(props);
this.state = {
search: '',
companies: props.companies
};
}
updateSearch(event) {
this.setState({ search: event.target.value.substr(0,20) })
}
addCompany(event) {
event.preventDefault();
let nummer = this.refs.nummer.value;
let bezeichnung = this.refs.bezeichnung.value;
let id = Math.floor((Math.random()*100) + 1);
$.ajax({
type: "POST",
context:this,
dataType: "json",
async: true,
url: "../data/post/json/companies",
data: ({
_token : window.Laravel.csrfToken,
nummer: nummer,
bezeichnung : bezeichnung,
}),
success: function (data) {
id = data.Nummer;
this.setState({
companies: this.state.companies.concat({id, nummer, bezeichnung})
})
this.refs.bezeichnung.value = '';
this.refs.nummer.value = '';
}
});
}
editCompany(event) {
alert('clicked');
event.preventDefault();
this.refs.bezeichnung.value = company.Bezeichnung;
this.refs.nummer.value = company.Nummer;
}
render() {
let filteredCompanies = this.state.companies.filter(
(company) => {
return company.bezeichnung.toLowerCase().indexOf(this.state.search.toLowerCase()) !== -1;
}
);
return (
<div>
<div className="row">
<div className="col-xs-12 col-sm-12 col-md-12 col-lg-12">Suche</div>
<div className="col-xs-12 col-sm-12 col-md-9 col-lg-9">
<div className="form-group">
<input className="form-control" type="text" value={this.state.search} placeholder="Search" onChange={this.updateSearch.bind(this)} />
</div>
</div>
</div>
<form onSubmit={this.addCompany.bind(this)}>
<div className="row">
<div className="col-xs-12 col-sm-12 col-md-12 col-lg-12">Neuen Eintrag erfassen</div>
<div className="col-xs-12 col-sm-12 col-md-3 col-lg-3">
<div className="form-group">
<input className="form-control" type="text" ref="nummer" placeholder="Nummer" required />
</div>
</div>
<div className="col-xs-12 col-sm-12 col-md-3 col-lg-3">
<div className="form-group">
<input className="form-control" type="text" ref="bezeichnung" placeholder="Firmenname" required />
</div>
</div>
<div className="col-xs-12 col-sm-12 col-md-3 col-lg-3">
<div className="form-group">
<button type="submit" className="btn btn-default">Add new company</button>
</div>
</div>
</div>
</form>
<div className="row">
<div className="col-xs-10 col-sm-10 col-md-10 col-lg-10">
<ul>
{
filteredCompanies.map((company)=> {
return <Company company={company} key={company.id} onClick={this.editCompany.bind(this)} />
})
}
</ul>
</div>
</div>
</div>
);
}
}
export default CompanyList
The Company class looks like this:
import React, { Component } from 'react';
const Company = ({company}) =>
<li>
{company.Nummer} {company.Bezeichnung}
</li>
export default Company
My question now is, why is the onclick event not being fired, when I click on a Company.
In this part:
<ul>
{
filteredCompanies.map((company)=> {
return <Company company={company} key={company.id} onClick={this.editCompany.bind(this)} className="Company"/>
})
}
</ul>
The reason is pretty simple, when you onClick like
<Company company={company} key={company.id} onClick={this.editCompany.bind(this)} />
its not an event that is set on the component, rather a prop that is being passed to the Company component and can be accessed like props.company in the Company component,
what you need to do is to specify the onClick event and className in the Company component like
import React, { Component } from 'react';
const Company = ({company, onClick, className}) => (
<li onClick={onClick} className={className}>
{company.Nummer} {company.Bezeichnung}
</li>
)
export default Company
The function passed on as prop to the Company component can be passed on by any name like
<Company company={company} key={company.id} editCompany={this.editCompany.bind(this)} className="Company"/>
and used like
import React, { Component } from 'react';
const Company = ({company, editCompany}) => (
<li onClick={editCompany}>
{company.Nummer} {company.Bezeichnung}
</li>
)
you have to bind the event on you child component :
import React, { Component } from 'react';
const Company = ({ company, onClick }) =>
<li onClick={onClick}>
{company.Nummer} {company.Bezeichnung}
</li>
export default Company
or
const Company = ({ company, ...props }) =>
<li {...props}>
{company.Nummer} {company.Bezeichnung}
</li>
if you want that all props passed to your Compagny component (exept compagny) goes to your li tag. see ES6 spread operator and rest.

Meter: embedding templates in Blaze

Using Bootstrap and Meteor / Blaze, I am trying to dynamically control the size of a template using a template helper. I'd like to have a button to switch between col-md-4 and col-md-12. The hard-coded column sizing looks like this:
<div class="panel-body">
<div class="row">
{{#each articles}}
<div class="col-md-4">
{{> article this}}
</div>
{{/each}}
</div>
I have a template helper that returns the div and found I needed a closing helper call, or Meteor could complain about an unbalanced <\div>. This seems a bit hacky.
Template.articles.helpers({
format: function() {
return '<div class="col-md-4">';
// return '<div class="col-md-12">';
},
end_format: function() {
return '</div>'
}
});
The markup is:
<div class="panel-body">
<div class="row">
{{#each articles}}
{{{format}}}
{{> article this}}
{{{end_format}}}
{{/each}}
</div>
</div>
But the div tags are returned closed and empty, with the markup I'd like enclosed underneath, as can be seen in this screen shot:
Don't return HTML from template helpers, there is usually a better way.
Why don't you return a dynamic class name from a template helper ?
HTML
<div class="panel-body">
<div class="row">
{{#each articles}}
<div class="{{columnSize}}">
{{> article this}}
</div>
{{/each}}
</div>
<button type="button" class="btn btn-primary js-toggle-column-size">Toggle column size</button>
</div>
ES2015
Template.articles.onCreated(function(){
this.largeColumns = new ReactiveVar(false);
});
Template.articles.helpers({
columnSize(){
const largeColumns = Template.instance().largeColumns.get();
return largeColumns ? 'col-md-12' : 'col-md-4';
}
});
Template.articles.events({
'click .js-toggle-column-size'(event, template){
const largeColumns = template.largeColumns.get();
template.largeColumns.set(!largeColumns);
}
});

iron:router's yieldTemplates not working

I'm trying to use iron:router's yieldTemplatesproperty to render multiple templates on the same layout.
According to this tutorial, we should be abble to do something like this:
template.html
<template name="complexLayout">
<div class="left">
{{> yield region="menu"}}
</div>
<div class="main">
{{> yield}}
</div>
<div class="bottom">
{{> yield region="footer"}}
</div>
</template>
route.js
this.route('home', {
path: '/',
layoutTemplate: 'complexLayout',
yieldTemplates: {
'myMenu': {to: 'menu'},
'myFooter': {to: 'footer'}
}
});
I tried to do it, but the yieldTemplates part doesn't work.
Here is the relevant code:
Router.js
Router.map(function() {
this.route('home', {
path: '/home',
controller: 'homeController'
});
});
Controllers.js
baseController = RouteController.extend({
layoutTemplate: 'baseLayout'
});
homeController = baseController.extend({
yieldTemplates: {
'homeNavTop': {to: 'top'}
}
});
Templates.html
<template name="baseLayout">
<main>
<!-- NAV TOP -->
<div id="nav-top" class="hide-on-large-only light-blue darken-3 white-text">
<div class="row nomargin valign-wrapper hide-on-large-only">
{{> yield region='top'}}
</div>
</div>
<!-- / NAV TOP -->
<!-- BODY -->
<div class="row nomargin">
<div class="col s12">
{{> yield}}
</div>
</div>
<!-- / BODY -->
</main>
</template>
<template name="homeNavTop">
<a href="#" data-activates="slide-out" class="menu button-collapse btn-flat waves-effect">
<i class="material-icons">menu</i>
</a>
</template>
As explained, the BODY part works fine. The top region remains empty.
I have no console errors at all.
Do you have any clue of what is wrong in my code?
Perhaps the syntax has changed since that tutorial was written, but according to the IronRouter guide you should be doing this:
{{> yield 'top'}}
rather than this
{{> yield region='top'}}

Discover Meteor Book Routing Issue?

I've recently been trying to work through the Discover Meteor book in an attempt to learn Meteor. In this part we make a 'discuss' button that will route to a page displaying only one component of a list.
A couple of people on the official git site have been talking about how the 'discuss' button is not rendering {{postPagePath this}} even when following the instructions in the book.
I'm wondering if Meteor may have changed its routing format since this book was written.
Here is the git page:
https://github.com/SachaG/Microscope/commit/d0e035e2b175f755b80f3c4201cd5aae5f6885d2
If you've managed to get to that point without any issues before that, then theres no changes that would stop you from being able to complete that section.
Could you share your code of Templates HTML and also your Router js code?
Wasn't quite sure which html so I put 2.
application/layout.html
<template name="layout">
<div class="container">
{{> header}}
<div id="main">
{{> yield}}
</div>
</div>
</template>
header.html
<template name="header">
<nav class="navbar navbar-default" role="navigation">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navigation">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="{{pathFor 'postsList'}}">Microscope</a>
</div>
<div class="collapse navbar-collapse" id="navigation">
<ul class="nav navbar-nav">
{{#if currentUser}}<li>Submit Post</li>{{/if}}
</ul>
<ul class="nav navbar-nav navbar-right">
{{> loginButtons}}
</ul>
</div>
</nav>
</template>
router.js
Router.configure({
layoutTemplate: 'layout',
loadingTemplate: 'loading',
notFoundTemplate: 'notFound',
waitOn: function() { return Meteor.subscribe('posts'); }
});
Router.route('/', {name: 'postsList'});
Router.route('/posts/:_id', {
name: 'postPage',
data: function() { return Posts.findOne(this.params._id); }
});
Router.route('/posts/:_id/edit', {
name: 'postEdit',
data: function() { return Posts.findOne(this.params._id); }
});
Router.route('/submit', {name: 'postSubmit'});
var requireLogin = function() {
if (! Meteor.user()) {
if (Meteor.loggingIn()) {
this.render(this.loadingTemplate);
} else {
this.render('accessDenied');
}
} else {
this.next();
}
}
Router.onBeforeAction('dataNotFound', {only: 'postPage'});
Router.onBeforeAction(requireLogin, {only: 'postSubmit'});

Resources