I've a problem, or more probably a misundertanding with variable passed to the template on Meteor.
I want to do something really simple; I've a Collection of articles. Each article have an correspondy inner Tags array.
template articles
<template name="articles">
<div class="row">
<div class="small-12 small-centered columns">
{{#each articles}}
{{> _article}}
{{/each}}
</div>
</div>
</template>
partial _article
<template name="_article">
<div class="article-container small-12 columns">
<div class="article-header small-12 columns">
<h1 class="left">Titre: {{title}}</h1>
<button class="right delete">×</button>
</div>
<article class="overview small-12 columns">
<h2>Categorie: {{nameCateg}}</h2>
<h2>{{createdAt}}</h2>
<p>{{content}}</p>
</article>
<div class="article-footer small-12 columns">
<p>Tags:
{{#each tags}} // I want to do something like that...
lbl: {{tag.label}}
{{/each}}
</p>
</div>
</div>
</template>
Inspection on one article with Mongol
{
"_id:" "c47cezerzerz",
"title": "titre,
"tags"[
{"id": "aurtht58",
"label": "tag1"},
{"id": "aurthfe8",
"label": "tag2"}
]
}
When I try to iterate with the #each on tags, tags seems to refers to the whole tags collection, not only the inner tags list. How to fix that?
So I've maybe missed something, like the scope of these variables... Actually I don't really know how they work...
The context inside {{#each}} is a child context focused on the document currently being iterated.
For example with the following document:
{
foo : [
{
bar : 1
},
{
bar : 2
}
]
}
You would use it this way:
{{#each foo}}
<p>bar is : {{bar}} and also {{this.bar}}</p>
{{/each}}
So, in your case, directly reference the fields ({{label}}, ...)
The lookup is first done on the current template context, then on its helpers, then up and up toward the global helpers.
Template contexts are a whole topic, a good starting point in the SpaceBars documentation.
I think that until you access to the tags level, you iterate on a cursor. (to be confirmed). A cursor is obtained with a collection.find() if you add a fetch()or use collection.findOne() you get an array (in your case, you just access an array within your document) and the rules are changing for the spacebar iteration.
You need to access your tags using {{this}} and tag items using {{this.label}}
Related
I use Angular 8 and I have 3 components:
CalculationFirst : app-calculation-first
CalculationSecond : app-calculation-second
CalculationThird : app-calculation-third
CalculationFirst is the "parent" component and has CalculationSecond as a "child" in a way that I use CalculationSecond inside html template of CalculationFirst.
So in CalculationFirst.component.html it looks like this:
<div class="container">
<form #f="ngForm" (ngSubmit)="submit()" novalidate *ngIf="settings.isloaded == true">
<div class="row">
<app-calculation-second [elements]="settings.seconds.elements" [epics]="epics"[types]="types" (opendialoglistevent)="openDialogList($event)"></app-calculation-second>
</div>
</form>
</div>
Then CalculationSecond.component.html looks like this:
<div class="col-lg-2 box">
<div class="row">
<div class="row content" *ngFor="let element of elements;">
<div class="col-lg-14">
<app-calculation-third [elem]="element" [types]="types"></app-calculation-third>
</div>
</div>
</div>
</div>
And finally CalculationThird.component.html looks like :
<div class="col-lg-14">
TEST_TEXT_1
{{elem}}
TEST_TEXT_2
</div>
Now, the problem is if I were to just look at my rendered page, only TEST_TEXT_1 page shows, {{elem}} does not render and neither does TEST_TEXT_2. If I am to check the value of elem, it is undefined. I would expect to get an error that I am trying to display undefined, however I do not get anything. My environment.ts is not set to production, so I do not know if my error is not big enough for angular to inform me. (On a tangent, I don't even get an error if I use a json object in an *ngFor instead of an enumerable (Array), but I'm still not sure if I have some lacking setting).
I have also tried, when I use the third component, to not pass the element from *ngFor but to pass a handmade object {'elemId':0}, this had the same result.
If I move :
<app-calculation-third [elem]="element" [types]="types"></app-calculation-third>
to CalculationFirst, making it a direct child, it works perfectly fine, regardless of what I put in there. Is there a limit to the amount of child components you can have? Am I missing something?
*Edit: Also for those who suggested it is the *ngFor or the usage of different variables, I can make CalculationSecond look like the following and still it doesn't work:
<div class="col-lg-2 box">
<div class="row">
<div class="row content" *ngFor="let element of elements;">
<div class="col-lg-14">
</div>
</div>
</div>
<app-calculation-third [elem]="{ elementId: '' }" [types]="types"></app-calculation-third>
</div>
You are passing the elements to seconds input.
[seconds]="settings.seconds.elements"
But you use elements in your app-second-calculation element.
Say I have the following code:
<div>
{{#each questions}}
<div id="question_{{#index}}">
{{#each this.answers}}
<div id="answer_{{???}}_{{#index}}">
{{this}}
</div>
{{/each}}
</div>
{{/each}
</div>
How can I access the outer loop's index (question index) inside of the inner (answer) loop? Essentially I want the id in the format of "answer_questionIndex_answerIndex"
Found this deep in some documentation
Block Parameters
New in Handlebars 3.0, it's possible to receive named parameters from supporting helpers.
{{#each users as |user userId|}}
Id: {{userId}} Name: {{user.name}}
{{/each}}
In this particular example, user will have the same value as the current context and userId will have the index value for the iteration.
https://handlebarsjs.com/guide/block-helpers.html#hash-arguments
Hey everyone, thank you very much for your help. Question is edited per suggestions in the comments.
I'm new to Mongo and Meteor.
I have a collection "posts" with a field "slug".
The "post" template is populating correctly with each post's values. Slug value is always something like "my-great-post".
I need to get the text value for the _id's slug, which will be different each time the template is accessed, encode it, write a string, and spit the string back out into the template.
Things tried
can't return a value for "this.slug" or "this.data.slug" in either template helpers or onRendered, even though collection is defined and correctly populating spacebars values in the template
"this" returns "[object Object]" to console.log
app crashes when I try to javascript encode and deliver a string from the helper, probably I don't fully understand helper syntax from the documentation
(I followed advice in the comments to avoid trying to create scripts in the template html, so below is more information requested by everyone helping on this thread)
- Template html -
{{#with post}}
<div class="blog-article">
<div class="blog-header">
<div class="left">
<!-- title -->
<h1 class="post-title">{{title}}</h1>
<div class="holder">
<div class="post-tags">
<!-- tags -->
{{#each tags}}
<span>{{this}}</span>
{{/each}}
</div>
</div>
</div>
</div>
<div class="blog-post">
<div class="blog-copy">
<!-- date -->
<div class="post-date">{{post_date}}</div>
<!-- social -->
<div class="blog-social">
<!--
<a class="so-facebook" target="_blank" href="need to encode slug here"></a>
-->
</div>
<!-- ============== post ============== -->
{{{content}}}
<!-- ============ end post ============ -->
</div>
</div>
</div>
{{/with}}
- Template js -
Template.post.onCreated(function() {
var self = this;
self.autorun(function() {
var postSlug = FlowRouter.getParam('postSlug');
self.subscribe('singlePost', postSlug);
});
});
Template.post.helpers({
post: function() {
var postSlug = FlowRouter.getParam('postSlug');
var post = Posts.findOne({slug: postSlug}) || {};
return post;
}
// can't get these working in a helper, out of helper they crash the app
// console.log(this.slug);
// console.log(this.data.slug);
});
Template.post.onRendered( function () {
// these do not work
// console.log(this.slug);
// console.log(this.data.slug);
});
db.posts.findOne();
{
"_id" : ObjectId("576c95708056bea3bc25a91f"),
"title" : "How Meteor Raised the Bar For New Rapid-Development Technologies",
"post_date" : "May 28, 2016",
"image" : "meteor-raised-the-bar.png",
"slug" : "how-meteor-raised-the-bar",
"bitlink" : "ufw-29Z9h7s",
"tags" : [
"Tools",
"Technologies"
],
"excerpt" : "sizzling excerpt",
"content" : "bunch of post content html"
}
If some one can solve this using any method, I will accept answer with joy and gratitude most intense.
The problem is probably with the parent template, rather than this one. The way that Meteor works is that the JS files are separated from the HTML, so don't try to include a <script> tag in the HTML.
The first thing is that you have to load all of your documents into the client. (NOTE: once you've got the hang of that, then you can worry about only loading the documents that you need).
To do that, you need a collection and a publication. By default all collections are automatically published completely, so unless you removed the autopublished module, then I'll assume that it is still loaded.
So let's start with the parent template. In this case, I'm going to just loop through all of the posts in the collection and display them using the innerTemplate.
<template name=parent>
<ul>
{{#each post}}
{{> innerTemplate}}
{{/each}}
</ul>
</template>
And now our inner template might look like this:
<template name=innerTemplate>
<li>{{slug}}</li>
</template>
The end result will be a simple list with each slug.
Now, to link everything together, we need to create a JS file, which will:
1. define the collection on both client and server
2. pass the collection to the parent template
This file should be accessible to both the client and the server.
posts = new Mongo.Collection('posts');
if(Meteor.isClient) {
Template.parent.helpers({
posts() {
return Posts.find();
}
});
}
Now, if you want to do something with 'slug' in the JS file, you could do something like this:
if(Meteor.isClient) {
Template.innerTemplate.helpers({
upperCaseSlug() {
return this.slug.toUpperCase();
}
});
}
Then, you could refer to upperCaseSlug in your template, like thus:
<template name=innerTemplate>
<li>{{upperCaseSlug}}</li>
</template>
A few things about Meteor:
You should never see a pattern such as:
<script type="text/javascript">
...some code
</script>
Because Meteor combines all your js files into one big file and includes it automatically in your app. You should never have to declare your own script in this way.
Secondly, you should never have to get the value of a data object by reading the DOM. The data context of each template gives you your data in the variable this.
In either a helper or template event you can refer to this and be assured that you're going to get exactly the data being displayed in that instance of the template.
Having now seen your template code it's now apparent that your template has no data context - you set the data context inside your {{#with post}} and its associated helper but that doesn't end up creating the this you need one level below.
So... #Nathan was on the right track except that he assumed you were iterating over a cursor instead of just looking at a single post.
Take all html you have between your {{#with post}} and {{/with}} and put it in a new template, say postDetail then make your outer template:
<template name="post">
{{#with post}}
{{> postDetail}}
{{/with}}
</template>
Now your postDetail template will get a data context equal to the post object automatically and your helpers can refer to this safely.
Template.postDetail.helper({
slugURI{
return "/"+encodeURI(this.slug);
}
});
Then in your postDetail template you can get the encoded slug with:
<a class="so-facebook" target="_blank" href={{slugURI}}>
Say I have a common piece of markup that will be repeated throughout my website with only a few small differences.
For example, I have a bunch of <div> elements that all have a title.
Elements with class: section-title are all styled the same way, but their text content is different.
I would like to make a template to represent a title, sort of like this:
<template name="section-title">
<div class="section-title">
<span> Profile </span>
</div>
</div>
Except, I need "Profile" to be interchangeable, because I have many different sections: "profile", "contact information", etc.
What is the best way to do this in Blaze?
You can use either template arguments or set the template current data context to the appropriate list of arguments.
HTML
<template name="sectionTitle">
<section class="section-title">
<span>{{title}}</span>
</section>
</template>
{{> sectionTitle title="Profile"}}
<template name="parent">
{{> sectionTitle args}}
</template>
JS
Template.parent.helpers({
args: function(){
return {
title: "Profile"
};
}
});
This problem is a bit hard to explain, but I'll give it a try. When I'm using a nested view inside an ItemView of a CollectionView the next ItemView will append inside the previous childView.
<div id="items">
{{#collection}}
<div class="item">
{{title}}
{{view childView}}
</div>
{{/collection}}
</div>
Results in:
<div id="items">
<div class="item">
The title
<div class="childView">
This is the childView..
<div class="item">
The title
<div class="childView">
This is the childView..
</div>
</div>
// And so on...
</div>
</div>
</div>
It looks like the collectionView is keeping a reference to the last view there was something inserted into. So in this case the childView of the itemView, but it should be the itemView.
I hope I made myself a bit clear, because it is frustrating.
This looks like a known thorax bug that was closed.
I would double check your templates and make sure all your html elements have closing tags. Also get the latest version of thorax (assuming there are no breaking changes). It could be that the bug was closed incorrectly and still a problem.