I am using handle bars to loop through an array of objects that it gets from a get request:
<div id="content-placeholder"></div>
<script id="some-template" type="text/x-handlebars-template">
{{#each this}}
{{name}}
{{/each}}
</script>
<script>
var source = $("#some-template").html();
var template = Handlebars.compile(source);
var data = $.get('http://localhost:3000/teams.json');
console.log(data.responseJSON);
$("#content-placeholder").html(template(data));
</script>
The JSON is in the form:
[
0: {name: 'name1'},
1: {name: 'name2'},
...
]
The get request is returning the right data, but for some reason, the name is not being displayed in the html. Instead, different parts of the request are. Here is what the html looks like:
GETRESPONSEHEADER
GETALLRESPONSEHEADERS
SETREQUESTHEADER
...
There are no errors in the console. How do I fix this?
as stated at https://api.jquery.com/jquery.get/, $.get() function returns a superset of the XMLHTTPRequest object. So, what you are actually printing is names of this object's methods. The object returned by $.get() function implements a Promise with done() method, so we can execute our code asynchronously as follows:
<script>
var source = $("#some-template").html();
var template = Handlebars.compile(source);
var data = $.get('teams.json');
data.done(function (data) {
console.log(data.responseJSON);
$("#content-placeholder").html(template(data));
});
</script>
we also could do it as a callback function:
<script>
var source = $("#some-template").html();
var template = Handlebars.compile(source);
var data = $.get('teams.json', function (data) {
console.log(data.responseJSON);
$("#content-placeholder").html(template(data));
});
</script>
please note that data variable within callback or promise done() function is a local variable of their anonymous function and not the same as data returned by $.get() function.
plunker: http://plnkr.co/edit/zveLAFNWvaDTmLFdLkLW
Related
I'm trying to access an nested object within an lamda function. The object looks similar to this:
{
foo: function(){
return {
'bar': "success"
}
}
}
when I try to access it with {{foo.bar}} it returns an empty string. If I wrap it with {{#foo}}{{bar}}{{/foo}} it works. Is this intentional ? Is there any way i can make the first statement work ? The templates are already in use for mustache.php and i'm trying to make then work in the frontend as well.
Here is a working demonstration of the problem:
var obj = {
foo: function(){
return {
'bar': "success"
}
}
};
var source = document.getElementById("template").innerHTML;
var template = Handlebars.compile(source);
document.getElementById("output").innerHTML = template(obj);
<script src="https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/4.0.11/handlebars.js"></script>
<script id="template" type="text/x-handlebars-template">
nested: {{#foo}}{{bar}}{{/foo}} <br/>
dot notation: {{foo.bar}}
</script>
<div id="output"></div>
I'm having hard time making the Template.rendered code wait for the data to load from a mongo collection:
Template.chart.rendered = function () {
var yelp_data, ndx;
var template = this;
template.autorun(function(){
console.log('autorun is called');
template.subscribe("yelp", function(){
yelp_data = Yelp.find().fetch();
ndx = crossfilter(yelp_data);
});
});
}
I can see, that autorun is being called as expected, but, by the time it finished, yelp_data is still an array of 0.
Is there a way to force meteor wait until the data is loaded into a variable?
Okay, based on your comments, give this a shot:
Create a wrapper template, let's call it chartWrapper, that wraps your chart.
Shift your subscription to the chartWrapper template.
Wrap the chart template call inside chartWrapper's Template.subscriptionsReady helper.
Code:
<template name="chartWrapper">
{{#if Template.subscriptionsReady}}
{{> chart}}
{{/if}}
</template>
Template.chartWrapper.onCreated( function() {
var template = this;
template.subscribe("yelp");
});
Template.chart.onCreated( function() {
var yelp_data, ndx;
var template = this;
yelp_data = Yelp.find().fetch();
ndx = crossfilter(yelp_data);
});
I believe this will completely delay your chart template until the Yelp data is available.
I am struggling to figure out the basic pattern for populating a template with data from a call to an external API in Meteor.
These are the elements in play
A fresh Meteor project, created by running meteor create monkeyproject
The URL of an external API that returns a JSON array. Let's say it's example.com/api/getmonkeys. It returns an array of monkeys, each with a different name.
A Handlebar template called monkeyTemplate with an {{#each}} loop. Let's say it's this:
<template name="monkeyTemplate">
{{# each monkeys}}
One of our monkeys is named {{name}}. <br>
{{/each}}
<input type="button" id="reload" value="Reload monkeys" />
</template>
What I want to happen
When the page loads fill monkeyTemplate with monkeys from our external URL.
When the user clicks the button, call the external URL again to reload the monkeys.
The question
What is a standard pattern for doing the above in Meteor? At the risk of cluttering up the question, I'll include some starting points, as I understand them.
We can populate the template with whatever we return from our Template.monkeyTemplate.monkeys function. How do we fill it with content from an external URL, given that the page will load before the external request is finished?
We can get our JSON by using Meteor.HTTP.call("GET", "http://example.com/api/getmonkeys", callback ). Where do we put this request, and what do we put into our callback function in this situation?
We can control what happens on the server side and what happens on the client side by using the Meteor.isServer/Meteor.isClient conditions, or by putting our code into files called client and server folders. What code needs to be on the server side vs. the client side?
We determine what happens when the button is clicked by attaching a function to Template.monkeyTemplate.events['click #reload']. What goes into our callback function in this situation?
I will refrain from cluttering up the question with my crappy code. I am not looking for anyone to write or rewrite an application for me—I am just looking for the guidelines, standard patterns, best practices, and gotchas. Hopefully this will be instructive to other beginners as well.
I'm not sure if this is the "standard" template, but it serves the purpose pretty well.
Set up two data helpers for the template, monkeys and loading. First one will display the actual data once it's fetched, the latter will be responsible for notifying user that the data is not yet fetched.
Set up a dependency for these helpers.
In created function of the template, set loading helper to true and fetch the data with HTTP call.
In the callback, set the template data and fire the dependency.
html
<template name="monkeys">
{{#if loading}}
<div>Loading...</div>
{{/if}}
{{#if error}}
<div>Error!</div>
{{/if}}
{{#each monkeys}}
<div>{{name}}</div>
{{/each}}
<div><button class="monkeys-reloadMonkeys">Reload</button></div>
</template>
js
var array = null;
var dep = new Deps.Dependency();
Template.monkeys.created = function() {
reloadMonkeys();
};
Template.monkeys.events({
'click .monkeys-reloadButton': function(e,t) {
reloadMonkeys();
};
});
var reloadMonkeys = function() {
array = null;
dep.changed();
HTTP.get('http://example.com/api/getmonkeys', function(error, result) {
if(!error && result) {
array = result;
} else {
array = 0;
}
dep.changed();
});
};
Template.monkeys.monkeys = function() {
dep.depend();
return array ? array : [];
};
Template.monkeys.loading = function() {
dep.depend();
return array === null;
};
Template.monkeys.error = function() {
dep.depend();
return array === 0;
};
I'm using meteor 0.6.4. The problem i have is that the data context when rendering a template is sometimes undefined, so that, 'this' object is a reference to Window:
Template.task.time_left = function(){
debugger;
var nDate = this.due_date.getTime();
Exception from Deps recompute: TypeError: Cannot call method 'getTime' of undefined
The html code is wrapped inside an {{each}} handlebars statement:
<template name="tasks_lists">
{{#each tasks_list}}
...
{{#each task}}
{{> task}}
{{/each}}
...
{{/each}}
</template>
<template name="task">
...
<div class="text">{{due_date}}</div>
...
</template>
I read that this bug was solved in an earlier version of Meteor. What can I do to aviod the function being called with 'this' as Window.
You should use template.xxx.helpers instead, ie:
Template.task.helpers({
nDate: function() {
return this.due_date.getTime();
}
});
When you use it within helpers, this is the data context.
I used 'helpers' function and I have the same problem. 'this' object is sometimes the window object:
Template.task.helpers({
...
'time_left': function(){
debugger;
var nDate = this.due_date.getTime();
...
When using any of the three Meteor template callback functions, including the onRendered function, the this object is actually a template instance object. Although it is possible to retrieve the data context of the template through this object, it is recommended that you reference the template data context through the use of the Template.currentData() function. The documentation for this function can be found here.
The this inside a template helper will always points to window object.
You can access the data context in Template.rendered() or the event handler function. In the event handlers, it is passed as a second argument as function( event, template ), where the template is the current template object.
However I suggest you to use the template instance functions such as find(), findAll(), firstNode(), lastNode() rather than data context.
Template.task.rendered = function() {
if( !this.window ){ //check that 'this' is not a 'window' object
var el = this.find( 'div.text' ); // the div that holds due_date
//do something
}
}
Is there any way to get the object of the current iteration in Handlebars?
code:
<script id="HandleBarTemplate1" type="text/x-handlebars-template">
{{#each objArr}}
<img src="{{objField1}}"/>
<strong>Name:</strong> {{objField2}}
<input type="button" onclick="processObject({{.}});"/>
{{/each}}
</script>
I've mentioned processObject({{.}}) That is incorrect. That's where I need a replacement/Solution.Hope you get what I'm tryin' to say.
The contents of objArr might look like
var objArr = [{objField1:"smith.jpg",objField2:"Smith"},{objField1:"jane.jpg",objField2:"Jane"},...]
Template compilation code is:
var source = document.getElementById("HandleBarTemplate1").innerHTML;
var compiledTemplate = Handlebars.compile(source);
var html = compiledTemplate({objArr:objArr});
If I could get the reference to the Object, then It's so easy to process the data. Rather than passing a field to the function and searching through entire array to get the required object and then process it.
I Prefer a solution without a custom block helper/custom expression helper but if none exists, I'd rather go for a custom block helper. Any solution without searching through the entire array is welcome!
I would suggest a different route.
Knowing that you already have a reference to objArr, make a global or name-spaced variable pointing to it.
For example: window.objArr = objArr;
Create your click handler that does whatever you wish:
function processObject(key){
}
call that with your key inside your template:
< script id="HandleBarTemplate1" type="text/x-handlebars-template">
{{#each objArr}}
<img src="{{objField1}}"/>
<strong>Name:</strong> {{objField2}}
<input type="button" onclick="processObject({{objField2}});"/>
{{/each}}
</script>
Other alternatives:
Customer Handler.
Other alternatives:
If you can't create a reference to objArray, you might could store the properties of the object within data- attributes if you're using html5. processObject could then retrieve them.
I do this with a Handlebars helper and an array of context objects.
Somewhere we have an array of context objects, which will store a reference to each context object we need to reference:
var ContextObjects = [];
We need to register a Handlebars helper function that stores the current context object in the array for us when we place "{{obj}}" in the template. The helper returns the index of the object in the array, which is what gets rendered:
// Must be function and not lambda to preserve the correct 'this'
Handlebars.registerHelper("obj", function ()
{
var contextObject = this; // the current context object
var index = ContextObjects.indexOf(contextObject);
if (index < 0)
{
index = ContextObjects.length;
ContextObjects[index] = contextObject;
}
return index;
});
Next we need a function to retrieve the object from the index on the element:
// Get the context object for the current event (e.g. click or context).
function GetContextObject(event)
{
var $e = $(event.currentTarget);
var index = $e.attr("data-obj");
return ContextObjects[index];
}
We tie them together in the template something like this:
{{#each Items}}
<div data-obj="{{obj}}" onclick="DoSomething(GetContextObject(event));">...</div>
{{/each}}
Which may render something like this:
<div data-obj="0" onclick="DoSomething(GetContextObject(event));">...</div>
<div data-obj="1" onclick="DoSomething(GetContextObject(event));">...</div>
<div data-obj="2" onclick="DoSomething(GetContextObject(event));">...</div>
...