Passing arguments to a Blaze template - javascript

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"
};
}
});

Related

How to use getElementById with a reactive var and Meteor

In general doing a getElementById works pretty well like this :
console.log(document.getElementById('{{chart_id}}').setAttribute("id", "div_top2"));
But it returns me null and I don't really know why but I think it's becoming from the following lines:
<template name="chart">
<div id="{{chart_id}}"></div>
</template>
And later in the JS I do :
Template.currentData().chart_id
to get the div and place a HighChart here.
The thing I want to do is to rename the id (by the way I want to add a identifier so the final id would be something like <div id ="{{chart_id}}+ a random number">)
Then I could do this in my JavaScript:
Template.currentData().chart_id + theRandomNumber
Finally I have modified the HTML where I create the <div> and it looks like this :
{{#each gimmeContainersToCreateChart}}
{{> chart chart_id=this._id}}
<!-- {{> chart chart_id="cpuChart"}}
{{> chart chart_id="netChart"}}
{{> chart chart_id="diskChart"}} -->
{{/each}}
gimmeContainersToCreateChartreturns a cursor with the running&&paused containers from the collection

Append dynamic vue component after specific element

I want to add component in random position (not random actually, based on some logic) based on click of other elements.
In jQuery, we can achieve that by $('#someId').append('element') or by $('#someId').find('.someClass').after('element')
What I want achieve (jquery version): jsfiddle
How to do that in Vue?
N.B: This couldn't be achieved by v-for as all elements won't appear sequentially or in the same place. Also, components should not be appeared on initialization as this is dynamic.
If this is about arranging that which component will appear after another. Let me assume I have all those components in an array and you can re-arrange those in the array as par your logic, and you can use special attribute: is to render those component.
Here in sample I have an array of components and I use v-for to render those one after another:
See fiddle here.
HTML:
<div id="app">
<h1>
Following are the components
</h1>
<div v-for="comp in compArray" :is="comp"></div>
<br>
<button #click="resuffle">
Resuffle
</button>
</div>
<template id="comp1">
<div>
<span>This is component 1</span>
</div>
</template>
<template id="comp2">
<div>
<span>This is component 2</span>
</div>
</template>
<template id="comp3">
<div>
<span>This is component 3</span>
</div>
</template>
JS:
Vue.component('comp1', {
template: '#comp1',
})
Vue.component('comp2', {
template: '#comp2',
})
Vue.component('comp3', {
template: '#comp3',
})
new Vue({
el: '#app',
data: {
compArray: ['comp1', 'comp2', 'comp3']
},
methods: {
resuffle: function() {
this.compArray.push(this.compArray[1])
this.compArray.splice(1,1)
}
},
})
Can you provide an example of your parent component when you are traversing your array/list to map your element with a component? (just to understand your use case)
You could use v-for but on multiple arrays generated by some computed properties in the place you want to display them (if the places you wanna display components are not also randomly chosen).

javascript get value of Mongo field already rendered - Meteor

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}}>

Access and iteration inner objects collection from template Meteor

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}}

Handlebarsjs: render multiple passes with nested templates

I see many things that just refers me to partials which sucks because they have to built out of the layout context.
What I'm wanting to do is make nested templates
For example:
<div id="person">
{{name}}
<div id="address">
{{street}}
</div>
</div>
<script>
var outer = Handlebars.compile($('#person').html());
outer({ name: 'someone special' });
var inner = Handlebars.compile($('#address').html());
inner({ street: 'somewhere cool' });
</script>
When running this, the inner template is never rendered as the outer templating gobbles it up.
It would be nice if you could namespace nested templates like this:
{{> name}}
<div id="person">
{{name}}
{{> address}}
<div id="address">
{{street}}
</div>
{{/> address}}
</div>
{{/> name}}
<script>
var outer = Handlebars.compile($('#person').html(), 'name');
outer({ name: 'someone special' });
var inner = Handlebars.compile($('#address').html(), 'address');
inner({ street: 'somewhere cool' });
</script>
or something like this, so that when the outer renders, it will leave the address alone and let inner render address itself without removing it from the DOM.
Is anything like this possible?
The reason for this question is that I'm using backbone and want to separate out all of my small views but it compiles to one file. when the outer is templated with handlebars, everything else breaks. I don't want to use partials as that just take everything out of the flow of the html document for the designers.
EDIT
I think what I really need is a way to do {{noparse}} and from the registerHelper just return the raw html between the noparse tags
Here is a demo of a no-parse helper. To use this functionality, you will need to use a version of at least v2.0.0-alpha.1. You can get it from the handlebars build page. Here is the pull request that details about it.
Here is the relevant code.
Template
<script id="template" type="text/x-handlebars-template">
<div id="person">
{{name}}
{{{{no-parse}}}}
<div id="address">
{{street}}
</div>
{{{{/no-parse}}}}
</div>
</script>
Handlebars.registerHelper('no-parse', function(options) {
return options.fn();
});
You're missing a crucial step. You have to take the template & compile it (which you're doing) but you also have to register the partial.
Really the pattern you want to use is the partial template - where you register the inner template as a partial template then fill it with the person's address data - there is a great example here, but this also will help in your specific HTML setup.
<script id="person-template" type="text/x-handlebars-template">
{{#each person}}
{{name}}
{{> address}}
{{/each}}
</script>
<script id="address-partial" type="text/x-handlebars-template">
<div class="address">
<h2>{{street}}</h2>
</div>
</script>
<script type="text/javascript">
$(document).ready(function() {
var template = Handlebars.compile($("#person-template").html());
Handlebars.registerPartial("address", $("#address-partial").html());
template({ name: 'someone special', street: 'somewhere cool' });
}
</script>

Categories

Resources