How do you populate a hidden div using handlebars data? - javascript

The handlebars data shows up fine on the static page, but the problem is I also have a modal, and the data is not being populated correctly on the modal.
I am using the avgrund modal (you can view the Codepen here)
main.js - handlebars code
var projectData = [
{
id: "0",
name: "Jack",
},
{
id: "1",
name: "Sally",
},
];
var theTemplateScript = $("#project-template").html();
var theTemplate = Handlebars.compile(theTemplateScript);
$("#content").append(theTemplate(projectData));
index.html
<div id="content">
<script id="project-template" type="x-handlebars-template">
<div class="pile">
{{#each this}}
<a onclick="avgrund.activate( 'stack' );">
<div class="box box{{id}}">
<span class="project_name project_name{{id}}">{{name}}</span>
</div>
</a>
<aside class="avgrund-popup">
<h2>{{name}}</h2>
<p>
You can hit ESC or click outside to close the modal. Give it a go to see the reverse transition.
</p>
</aside>
{{/each}}
</div>
</script>
</div>
Problem
The div boxes will show up fine, but whenever I click the boxes so that the modal appears, the modal only displays the last element in projectData.
I've been able to have the data appear fine when I use bootstrap modals, but for some reason not the avgrund modal.
Hint at a possible solution?
I think the answer to this StackOverflow q: how to populate hidden form field hints as something that might work, e.g. pulling out the data and re-inserting it in the avgrund modal code, as such
var data = {
"Value": [
{"Id": "6b7", "Notes": "testing", "CreatedBy": "User1"},
{"Id": "6b7", "Notes": "Testing 1", "CreatedBy": "User2"}
]};
data.Id = data.Value[0].Id;
var tmpl = Handlebars.compile($('#template').html());
var html = tmpl(data);
But I don't fully understand the answer and wasn't able to get it to work. Could someone point to some approaches or guiding principles that might be helpful?

Here's a fiddle that shows a working example: http://jsfiddle.net/9f9w1trs/
As pointed out by Roamer-1888 you had an extra closing </div> in there. You didn't include the code that actually compiles the template and populates it.
The basic idea is that you define a template - like you did
<script id="project-template" type="x-handlebars-template"> ....</script>
Then you "compile" the template (you can pre-compile them for performance reasons or if you'd like to only use the lightweight run-time library opposed to the full library).
var source = $("#project-template").html();
var template = Handlebars.compile(source);
and then you can populate your template with data ...
var populatedHtml = template(projectData);
and finally you add your populated HTML to the DOM
$('#myContent').append(populatedHtml);
Handlebars doesn't really care if you add it to a hidden div or not - it just replaces contents between {{}} ;)

Related

Problem loading an image dynamically with Vue & Webpack

I started building an application with the Vue CLI, and I have the following code snippet in a component:
<template>
<div v-loading="loading">
<el-carousel :interval="4000" type="card" height="350px">
<el-carousel-item v-for="element in persons" :key="element.id">
<div class="testimonial-persons">
<el-avatar :size="80">
<img :src="require('#assets/testimonial/'+ element.photo)">
</el-avatar>
<h3>{{element.name}}</h3>
<h4>{{element.occupation}}</h4>
<hr style="width:50%;">
<p>"{{element.comment}}"</p>
</div>
</el-carousel-item>
</el-carousel>
</div>
</template>
When the page is loaded I make a request for an API that returns an array of objects that I store in persons to iterate over the template.
Persons
[
{
"testimonial_id": "2",
"persons_id": "1",
"id": "1",
"name": "Tom lima",
"occupation": "CEO / Pugmania",
"comment": "Simply the best customer service and attention to detail.",
"photo": "20200320193143R7pChVea3IkmujRRmS.png"
},
]
Everything works normally, the problem is with the image loading.
When I enter the image name manually it works.
<img src="#assets/testimonial/20200320193143R7pChVea3IkmujRRmS.png">
Does anyone have any idea how to solve?
Your template is correct except that you're missing a / following the #. It should be:
<img :src="require('#/assets/testimonial/' + element.photo)">
This would be needed for the manual usage too, so maybe you left it out when you converted it to a dynamic path.

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

How do I create a grid system using handlebars in Meteor.js?

I'm trying to build a simple memory card game in Meteor and I can't seem to create a grid of my cards to populate in html. I'm just trying to get a 4x4 grid for right now.
Here is the javascript:
Template.body.helpers({
cards: function() {
var allCards = Deck.find({}).fetch();
var chunks = [];
while (allCards.length > 0) {
chunks.push(allCards.slice(0, 4));
allCards = allCards.slice(4);
}
return chunks;
},
});
And here is the HTML:
<body>
<div class="container">
<div name="gameBoard">
{{#each cards}}
{{> cardsRow}}
{{/each}}
</div>
</div>
</body>
<template name="cardsRow">
<div class="row">
{{#each row}}
{{> card}}
{{/each}}
</div>
</template>
<template name="card">
<span class="text">{{text}}</span>
</template>
Right now I just have simple text entries in the db to see what I'm doing before I pull in images. I've tried console logging from my JS and I believe I'm creating the array of spliced rows correctly so I think the problem may be in the way I have arranged the markdown.
I tried pulling the each cards loop into a template as well instead of inside the body but I'm not sure how to render templates on demand. This is potentially an option since I have a new game button event listener that could call a render. I'm just not sure how to explicitly render in Meteor.
I tried following this previous post but I couldn't get it to work:
How do I populate a bootstrap grid system using handlebars for each command in Meteor.js?
Thoughts? I can provide more of my code base if needed.
Neither row in {{#each row}} in the "cardsRow" template, nor {{text}} in the "cards" template, refer the way that you hope they do. You need to define row and text as template helpers for each of these templates. In a template helper, this refers to the data object associated with the template.
In your case, when you iterate through #each cards in "container", this is passed into the "cardsRow" template as one of the chunks defined in your cards helper function. The helpers below should illustrate this, and are sufficient for your example.
Template.cardsRow.helpers({
row: function() {
console.log(this); // a chunk of cards
return this;
}
});
Template.card.helpers({
text: function() {
console.log(this); // a number in a chunk
return this;
}
});

Working with ux-datagrid, angularJS and nested interations

I got it working excellent with one template but ran into an issue trying to replace my inner ng-repeat with another template. Kind of like grouped data or more like nested data.
so let me simplify my html here:
<div data-ng-controller="index.dataGridController" data-ux-datagrid="index.items" class="listA datagrid" data-addons="whichTemplate" grouped="'focus'">
<script type="template/html" data-template-name="default" data-template-item="item">
<div>
<h3>{{ item.obj.name }}</h3>
<!--- instead of
<ul>
<li ng-repeat="listItem in item.focus>{{listitem}}</li>
</uL
-->
</div>
</script>
<script type="template/html" data-template-name="innerFocus" data-template-item="item">
<div>
<ul>
<li>{{item.focus.id}}</li>
</ul>
</div>
</script>
</div>
and the index.items look like this:
[{
"obj": {
"name": "someName"
}
"focus": [
{"id": "something here"},
{"id": "Another etc"}
]
}]
// which template is basically copy and pasted from examples
angular.module('ux').factory('whichTemplate', function () {
return function (inst) {
// now we override it with our method so we decide what template gets displayed for each row.
inst.templateModel.getTemplate = function (item) {
var name = item.focus ? 'innerFocus' : 'default';
// now we get the template from the name.
return inst.templateModel.getTemplateByName(name);
};
};
});
Trying to iterate a list with the focus array. Any ideas? I think it would make for a great example in the docs. Adding a fiddle. http://jsfiddle.net/uftsG/88/
Instead of putting all focus array in one li i'm trying to get them spread out while also having the item.obj.name above the list.
I am not familiar with ux-data grid. If you are set on using it, you can ignore this answer, but I did a small search engine (purely for academic reasons) and i ended up using ng-grid to display the data. I followed the github documentation and ended up boot-strapping #scope.myDefs with the data I needed on the backend using a $scope.getMyData function. In the view all you really need to do is add input fields to get the data, and then a div class with your ng-grid. It's super cool because it ensures the code is modular, and there are no funky functions going on in the view with nested ng-repeats so on and so forth. The only part that gave me trouble was the CSS because I'm not bootstrap savvy yet :) Here's some docs if you end up interested in ng-grid at all.
http://angular-ui.github.io/ng-grid/

handlebars partial context

I have an array that contains the information for social buttons (href,alt,img). I created a partial that would cycle through the array and add the objects, here is what I have.
the array:
var social=[
{
"url":"http://twitter.com/share?text="+encodeURIComponent(this.title)+" "+encodeURIComponent('#grnsrve')+"&url="+domain+"&via="+twitterAcct,
"image":"IMG/media/twitter_16.png",
"alt":"twitter link"
},
{
"url":"http://twitter.com/share?text="+encodeURIComponent(this.title)+" "+encodeURIComponent('#grnsrve')+"&url="+domain+"&via="+twitterAcct,
"image":"IMG/media/twitter_16.png",
"alt":"twitter link"
},
{
"url":"http://twitter.com/share?text="+encodeURIComponent(this.title)+" "+encodeURIComponent('#grnsrve')+"&url="+domain+"&via="+twitterAcct,
"image":"IMG/media/twitter_16.png",
"alt":"twitter link"
}
];
The template:
social_partial = '<img src="{{image}}"/>';
The partial function:
Handlebars.registerPartial('addSocial',social_partial);
and the main template:
<div class="tip_social">
{{>addSocial social}}
</div>
I'm getting a 'Depth0 is undefined error'. I tried looking for documentation on parials getting a different context , but I have yet to find it.
edit here is a more complete fiddle of it
Same issue for me. Bit of digging and one face-palm later here's what I've found.
tl;dr answer
Be very careful about the context you pass to your partial. If you somehow pass a null, or empty object to your partial you'll get the depth0 is undefined error
Very slightly longer answer
JSFiddle Examples:
depth0 bug gets triggered http://jsfiddle.net/paulcollinsiii/dAmfc/
working version http://jsfiddle.net/paulcollinsiii/sMAkT/
The only thing that changed in the working version is I'm passing a valid context to the partial.
BTW a very helpful trick is to use the debug helper from this blog
Answerbot dictator "code example"
<script id="parent">
<table>
<thead>
<tr>
<th>One</th>
<th>Two</th>
</tr>
</thead>
<tbody>
{{#each collection}}
{{>myPartial nothingHere}} <!-- Broken context -->
{{/each}}
</tbody>
</table>
</script>
The broken context above will cause handlebars to die. For your problem try digging into that with a {{debug}} tag and see if that helps.
For a better code example please just take a look at the jsFiddles. Reposting all the code in here, formatting it to make it pretty looking and making the StackOverflow answerbot dictator happy was a bit much for doing this at work ^_~
I'm not sure what you're doing wrong as you haven't provided enough code to duplicate your problem. However, it isn't hard to make it work. First of all, your main template should be iterating over social and feeding each element to your partial, something like this:
<script id="t" type="text/x-handlebars-template">
{{#each social}}
<div class="tip_social">
{{>addSocial this}}
</div>
{{/each}}
</script>
And then you can get your HTML with something like this:
​var t = Handlebars.compile($('#t').html());
var html = t({social: social}));​​
Demo: http://jsfiddle.net/ambiguous/SsdbU/1/
Here is the only way I found:
Handlebars doc indicates that you need to use Block Helpers to pass different contexts. So here are two files: the main template and the script in node contening two contexts: the main context and the context for the social data. The script having the source for the partial template. The Block is #twitter_list and its associated registerHelper uses option.fn(other_context_object) which seems the only way to pass another context in handlebars.
Main template:
<html>
<head>
<title>Test content</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
</head>
<body>
<div id = main>
<!-- This is using the main context-->
<h2> Hello {{name}}!</h2>
<p>{{some_content}}</p>
<!-- This is using the partial template and its own context -->
<ul>
{{#twitter_list}}
{{>yourpartial}}
{{/twitter_list}}
</ul>
</div>
</body>
</html>
The javascript using Node js:
var handlebars = require('handlebars'),
fs = require('fs');
const main_context = { name: "world",
some_content: "Bla bla bla all my bla bla"};
const twitter_data={social:[
{
"url":"some url 1",
"image":"IMG/media/twitter_1.png",
"alt":"twitter link 1"
},
{
"url":"some url 2",
"image":"IMG/media/twitter_2.png",
"alt":"twitter link 2"
},
{
"url":"some url 3",
"image":"IMG/media/twitter_3.png",
"alt":"twitter link 3"
}
]};
var partial_source = '{{#each social}}<li><img src="{{image}}"/></li>{{/each}}';
handlebars.registerPartial('yourpartial', partial_source);
handlebars.registerHelper('twitter_list', function(options) {
//you need to use the options.fn to pass the specific context
return options.fn(twitter_data);
});
fs.readFile('maintemplate1.hbs', 'utf-8', function(error, source){
var template = handlebars.compile(source);
var html = template(main_context);
console.log(html)
});

Categories

Resources