Use replace function inside Meteor.js Spacebars [duplicate] - javascript

I like to work with meteor, but I have a problem I can't find a solution for.
In the template file I have this code:
<template name="klussenboard">
<h2>Klussen</h2>
<div class="klussenboard">
{{#each klus}}
{{#if status=1}}
<li>
<a class="startlink" href="#"><img src="/images/starten.png"></a>
</li>
{{/if}}
{{/each}}
</div>
</template>
This is the js client code
Template.klussenboard.klus = function () {
return Klussen.find({"status": { $gt: 0 }}, {
sort: {datum: -1}
});
};
But this doesn't work. How can I do a statement in a template file?
Looking forward to an answer.

Spacebars (meteor's templating library), much like Handlebars (that it is based on) doesn't execute arbitrary expressions like, say, angular.js's templates.
If you change the statement you're trying to write into a helper method, like so (feel free to pick a better name!):
<template name="klussenboard">
<h2>Klussen</h2>
<div class="klussenboard">
{{#each klus}}
{{#if isEnabled}}
<li>
<a class="startlink" href="#"><img src="/images/starten.png"></a>
</li>
{{/if}}
{{/each}}
</div>
</template>
You could then define the isEnabled helper in any client side .js file - say, client/klussenboard.js like so:
Template.item.isEnabled = function() {
return this.status == 1;
}
So, this, in helper functions is
This assumes that you are in a context where status is a variable (which, based on your question, you are)
This would then reactively update whenever the status variable changes.

Related

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

Meteor: how to paginate simple-todos demo?

I am looking at Meteor pagination today.
I am interested in this repo:
https://github.com/alethes/meteor-pages
The initial code shown looks simple:
this.Pages = new Meteor.Pagination("collection-name");
and:
<body>
{{> collection-name}}
</body>
<template name="collection-name">
{{> pages}}
{{> pagesNav}} <!--Bottom navigation-->
</template>
I want to paginate this demo:
https://github.com/meteor/simple-todos
The code I see there simplifies to this:
Tasks = new Mongo.Collection("tasks");
if (Meteor.isServer) {
// This code only runs on the server
Meteor.publish("tasks", function () {
return Tasks.find({})})}
if (Meteor.isClient) {
// This code only runs on the client
Meteor.subscribe("tasks");
// ...
}
and:
<body>
<ul>
{{#each tasks}}
{{> task}}
{{/each}}
</ul>
</body>
<template name="task">
<li>
{{text}}
</li>
</template>
Perhaps my brain is a bit slow today.
It is not obvious to me how to paginate the above code.
How do I use
github.com/alethes/meteor-pages
to paginate the above code from simple-todos?
Been a while since I've used meteor-pages, but you should be able to just do replace Tasks = new Mongo.Collection("tasks"); with this.Tasks = new Meteor.Pagination("tasks"); - common code between client and server.
Basically meteor pages just creates a wrapper around a mongo collection and applies search and filter criteria.
If you're familiar with coffeescript make sure to check out their /examples directory in the repo.
Also, the settings https://github.com/alethes/meteor-pages#settings will help explain some of the defaults like items per page etc.

How to execute a callback after an #each is done?

I'm having trouble with a callback after the #each has finished. I have a template named "content":
<template name="content">
{{#if Template.subscriptionsReady}}
{{#each currentData}}
<div data-cid="{{this._id}}"></div>
{{/each}}
{{else}}
Loading...
{{/if}}
</template>
At first I wait for a subscription, when this is available, I iterate through my Collection with {{#each}} and append the div. What I need is a sort of callback for when the for-each loop is done (in other words DOM ready).
Template.content.onRendered()
-> triggers to early
I also tried appending an image after the {{each}} and fire a function in its onload like this:
<img style="height:0;width:0" src="*mysource*" onload="callback()">
-> did work sometimes but not reliable somehow
Is there a way to get this callback? I do not fear to change the structure of this template, if that brings the solution.
There's no easy way to get notified when a Spacebars {{#each}} block has done rendering into the DOM every item getting iterated over.
The best solution is to use another reactive computation (Tracker.autorun) to observe your (reactive) current data.
Everytime your current data (which is likely a cursor) is modified, you can run arbitrary code after every other reactive computations are done performing whatever their job is, using Tracker.afterFlush.
The {{#each}} block is one of those computations, whose role is to listen to the reactive data source you give it as argument and rerender its Template.contentBlock as many times as items fetched from the source being iterated over, with the current item as current data context.
By listening to the exact same reactive data source as the {{#each}} block helper and running your code AFTER it has finished its own reactive computation, you can get the actual requested behavior without relying on some weird tricks.
Here is the full implementation of this pattern :
JS
Template.content.helpers({
currentData: function(){
return Template.currentData();
}
});
Template.content.onRendered(function(){
this.autorun(function(){
var cursor = Template.currentData();
// we need to register a dependency on the number of documents returned by the
// cursor to actually make this computation rerun everytime the count is altered
var count = cursor.count();
//
Tracker.afterFlush(function(){
// assert that every items have been rendered
console.log(this.$("[data-cid]") == count);
}.bind(this));
}.bind(this));
});
I had a similar problem and after a lot of searching found the following solution. I tried using Tracker, onRendered and other tricks, none of them worked. This could be considered more of a hack, but works. Unfortunately can't remember where I found this solution initially.
Start with your template, but add an template tag after your each.
<template name="content">
{{#if Template.subscriptionsReady}}
{{#each currentData}}
<div data-cid="{{this._id}}"></div>
{{/each}}
{{doneTrigger}}
{{else}}
Loading...
{{/if}}
</template>
Then define a helper that returns null.
Template.content.helpers({
doneTrigger: function() {
Meteor.defer(function() {
// do what you need to do
});
return null;
}
});
You can read more about Meteor.defer() here, but it is equivalent to using a 0 millisecond setTimeout.
You can also use sub-templates and count the number of sub-templates rendered. If this number is the number of items in the collection, then all are rendered.
<template name="content">
{{#if Template.subscriptionsReady}}
{{#each currentData}}
{{> showData}}
{{/each}}
{{else}}
Loading...
{{/if}}
</template>
<template name="currentData">
<div data-cid="{{this._id}}"></div>
</template>
With that, initialize a reactive variable and track it:
var renderedCount = new ReactiveVar(0);
Tracker.autorun(function checkIfAllRendered() {
if(renderedCount.get() === currentData.count() && renderedCount.get() !== 0) {
//allDataRendered();
}
});
When the currentData template is rendered, increment it, and decrement it when it is destroyed.
Template.currentData.onRendered(function() {
renderedCount.set(++renderedCount.curValue);
});
Template.currentData.onDestroyed(function() {
renderedCount.set(--renderedCount.curValue);
});
A possibly simpler approach to consider - create a template around your #each block and then get an onRendered event afterwards:
html:
<template name="content">
{{#if Template.subscriptionsReady}}
{{> eachBlock}}
{{else}}
Loading...
{{/if}}
</template>
<template name="eachBlock">
{{#each currentData}}
<div data-cid="{{this._id}}"></div>
{{/each}}
</template>
js:
Template.eachBlock.onRendered(function(){
console.log("My each block should now be fully rendered!");
});

Using Meteor sessions to toggle templates

I'm brand new to JavaScript and Meteor, and am a little stuck in trying to create edit functionality for a list item to be able to modify properties of the list item (from a Mongo document). I can make it work if I make editMode a boolean property of the item itself, but I'd like it to be local and temporary rather than stored with the document itself.
Looking at the code below, you'll notice I'm still green and don't yet completely understand what I'm doing, but you should be able to understand what I'm trying to do :)
The code is somewhat there, but am trying to hook up changes in the session, based on toggling an edit button, to get the edit mode template to render. Here's what I have (stripped down to the relevant stuff):
// Item template
<template name="item">
<div class="item">
<span>{{itemText}}</span>
<span class="glyphicon glyphicon-pencil item-edit" aria-hidden="true"></span>
</div>
<div class="mod-table">
{{#if this.editMode}}
{{> modTable}}
{{/if}}
</div>
</template>
// Associated .js file
Session.set(this.editMode, false);
Template.item.events({
'click .item-edit': function() {
if (this.editMode) {
Session.set(this.editMode, false);
} else {
Session.set(this.editMode, true);
}
}
});
Don't use Session variables because they are global thus considered bad practice, you should use a ReactiveVar tied to your item template instead.
item.html :
<template name="item">
<div class="item">
<span>{{itemText}}</span>
<span class="glyphicon glyphicon-pencil item-edit" aria-hidden="true"></span>
</div>
<div class="mod-table">
{{#if editMode}}
{{> modTable}}
{{/if}}
</div>
</template>
item.js :
Template.item.created=function(){
this.editMode=new ReactiveVar(false);
};
Template.item.helpers({
editMode:function(){
return Template.instance().editMode.get();
}
});
Template.item.events({
'click .item-edit': function(event,template) {
var editMode=template.editMode.get();
template.editMode.set(!editMode);
}
});
Don't forget to meteor add reactive-var to your project !

Sending different data to an embedded component in Ember

I've got a drag and drop setup in two views, currently. I am trying to pull those out and make reusable components out of them. At this point I'm not even worried about the actions and bringing that information back (that will be the next issue that I will look into). Right now I'm only worried about getting the proper data to display.
The templates for my current setup are:
The outside list view:
<ul class="sortable-list">
{{#each view.permission}}
{{ view App.GroupsPermissionView }}
{{/each}}
</ul>
The individual item view:
<li class="sortable-item">{{ this.displayName }}</li>
I have changed this into the following component templates:
drag-and-drop.handlebars:
<ul class="sortable-list">
{{#each dad}}
{{ drag-and-drop-item dadi=this.displayName }}
{{/each}}
</ul>
and drag-and-drop-item.handlebars:
<li class="sortable-item">{{ dadi }}</li>
I call this with {{ drag-and-drop dad=unselectedPermissions }} or {{ drag-and-drop dad=selectedPermissions }}
and this works fine.
Here is my issue. My other use of drag and drop does not use the field name of "displayName". The field is just "name". I'm sure other co-workers will need to also use other field names as well.
I've tried many different things to attempt to get it passed through to the inner component, but nothing has worked. I think it is a limitation in handlebars and that this will be impossible to do.
Any idea of what could be done here, if anything?
Edit: An update
I've got things somewhat closer...but now I've run into another related issue. Here's my current drag-and-drop.handlebars:
<ul class="sortable-list">
{{#each dad}}
{{ drag-and-drop-item self=this dadi=../field }}
{{/each}}
</ul>
Here's drag-and-drop-item.handlebars:
<li class="sortable-item">{{ dragAndDrop self dadi }}</li>
And here's drag_and_drop_helpers.js:
Ember.Handlebars.helper("dragAndDrop", function(context,value) {
return this.self.get(value);
});
I realize that context isn't being used above, I've got it in there because of testing I was doing.
Here's the thing...with the code above, I get the correct 'this'. If I return "this.self.get('displayName');" in the helper, it works. However, the ../field isn't being returned in the template. I so wish you could have nested {{}}'s...then this would work!
To get the ../field working, I found I could make the following change to drag-and-drop.handlebars:
<ul class="sortable-list">
{{#each dad}}
{{#if ../field}}
{{ drag-and-drop-item self=this dadi=../field }}
{{/if}}
{{/each}}
</ul>
But if I do this, 'this' isn't the right one, so I'm getting 'displayName' through, but the wrong context.
It seems I can have one, or the other, but not both.
I could really use some help here.
I got it figured out, after quite a few days of searching and trying lots of things.
I did not realize that you could use "view." to pull the information.
Here's the call to this:
{{ drag-and-drop dad=unselectedPermissions field="displayName" }}
Here's the working drag-and-drop.handlebars:
<ul class="sortable-list">
{{#each dad}}
{{ drag-and-drop-item self=this dadi=view.field }}
{{/each}}
</ul>
Here's the working drag-and-drop-item.handlebars:
<li class="sortable-item">{{ dragAndDrop dadi }}</li>
Here's the working drag_and_drop_helpers.js:
Ember.Handlebars.helper("dragAndDrop", function(value) {
return this.self.get(value);
});
I'm hoping this helps someone in the future so they don't repeat mistakes I've made.
Now, on to figuring out the coding for the component.js...

Categories

Resources