This is a branch off from this question: Handlebars.JS (w/ Dashbars) parse error "expecting open_endblock got inverse" (There's also a codepen therein.) I posted this there because I thought the problems were related, maybe (since {{else}} can be used with if's or each's,) but that turned out not to be the case.
Specifically:
I'm also having the problem of my outermost {{#each}} looping in such a way that only the last record returned is being output through the template. Everything logs in {{log this}} after that opening {{#each}}, but it's not even hiding in the HTML output somewhere.
So my table SHOULD have 4 rows, and objects 0 through 3 log to the console, but only the fourth item, item 3, is added to the table. Inner each's work as expected, as do any and all other iterator functions; I'm not sure why it's JUST the outer one that's failing. The JSON from which my array of objects is created validates in every single linter I've used, as does my JavaScript. Atom is supposed to have a handlebars linter, but it doesn't seem to actually...umm...work.
I AM using Dashbars with this, (with both its lodash.js and moment.js dependencies,) but this bug has existed since before I started using that library. FWIW, jQuery 2.1.3 IS installed, and loaded before any other library, and all of that is in the <head> tag. (I don't see where it would matter, but just in case.) And since JavaScript is involved, yes, I'm in Chrome. The version of Handlebars is the latest, 3.0.1, using the full version because my use-case doesn't allow pre-compiling. Another NB is that all of this is embedded in a .cfm file (not my choice,) so everything does get run through the CFML interpreter first.
So...I'd managed to outsmart myself again. I've created a fork of my original code, but HTTPS policy in my browser is keeping it from working =-\
The "money" differences are as follows (can't use SO's <ul> because I can't embed code in a bullet-block)
*I'd been trying to jam my template into a <tr> element, to avoid having Handlebars process more lines of code than I thought was necessary:
<tr class="searchResults" id="searchResultsHTML"></tr>
<script id="result-template" type="text/x-handlebars-template">
<!--- To accommodate for that this would all begin and end with a <tr>, I tried this: --->
{{#unless #first}}
<tr>
{{/unless}}
<!--- The rest of the template --->
{{#unless #last}}
</tr>
{{/unless}}
</script>
*In good-programmer fashion, I'd forked my actual file several times, trying different things. (Mostly did this to avoid endless git resetting and branching; I know git's meant for this sort of thing, but I just wanted to be able to refresh old and new tabs at once. Anyway!) Took out those {{unless}} blocks and...apparently, I'd misunderstood what that does (since the official documentation is a bit...scant...on that point. There's no TRUE inverse of {{#if}}) For the sake of those who don't want to bother clicking to the codepen:
<div class="searchResults" id="searchResultsHTML">
<script id="result-template" type="text/x-handlebars-template">
<table id="resultTable">
<tr>
<th>Personal Information</th>
<th>Education</th>
</tr>
<!--- The log tag DOES output all records, even the ones that aren't showing in the HTML. --->
{{#each this}} {{log #index}} {{log this}}
<tr>
<td>{{#with basicInformation}}
<p>{{MASTER_CUSTOMER_ID}} ({{CUSTOMER_STATUS_CODE}})<br />
{{SEARCH_NAME}}<br />
(<span class="lightBlue">ADD:{{ADDOPER}}–{{{d-format 'MM/DD/YYYY' (d-date 'YYYY-MM-DD' ADDDATE)}}}</span>)</p>
<p>DOB:{{{d-format 'MM/DD/YYYY' (d-date 'YYYY-MM-DD' BIRTH_DATE)}}}<br />
{{/with}}</td>
<!--- More table columns; six total --->
<td>{{#each education}}
<p>{{INSTITUTION_NAME}} ({{{d-format 'YYYY' (d-date 'YYYY-MM-DD' BEGIN_DATE)}}}–{{{d-format 'YYYY' (d-date 'YYYY-MM-DD' END_DATE)}}})</p>
{{else}}
<p>No education records found</p>
{{/each}}
</td>
</tr>
{{else}}
<tr class="alert largerError" id="errorMessageRow">
<td class="empty" id="errorMessage" colspan="6">Either there has been an error, or your search did not return any records from any datasource.</td>
</tr>
{{/each}}
</table>
</script>
Yes, I stuck with ColdFusion comments; since those are ONLY for my reference, or the poor next developer, they don't need to go to the client and that way Handlebars isn't taking valuable time rendering them. (I'm already annoyed Handlebars is going to have to churn through my header row and a bunch of other text.) This tool searches our databases for customer numbers that MIGHT represent accidental duplicate accounts, so if you search for John Smith...oi!
So for anyone who Googled their way over here...I'll be glad if this helps even one person avoid spinning their wheels the way I did.
Related
I'm following this Ember tutorial and I've suddenly run into an issue where my rental-listing.hbs template component stops rendering. It started when I began implementing the map service.
I don't understand where this could be happening. I've copied the code from parts of the GitHub repository that I thought were relevant but to no avail.
I have a rental.hbs template that looks like so:
<div class="jumbo">
<div class="right tomster"></div>
<h2>Welcome!</h2>
<p>We hope you find exactly what you're looking for in a place to stay.</p>
{{#link-to "about" class="button"}}
About Us
{{/link-to}}
</div>
{{outlet}}
Which in turn has a template component called rental-listing.hbs:
<article class="listing">
<a
onclick={{action "toggleImageSize"}}
class="image {{if this.isWide "wide"}}"
role="button"
>
<img src={{this.rental.image}} alt="">
<small>View Larger</small>
</a>
<div class="details">
<h3>{{link-to this.rental.title "rentals.show" this.rental class=this.rental.id}}</h3>
<div class="detail owner">
<span>Owner:</span> {{this.rental.owner}}
</div>
<div class="detail type">
<span>Type:</span> {{rental-property-type this.rental.category}} - {{this.rental.category}}
</div>
<div class="detail location">
<span>Location:</span> {{this.rental.city}}
</div>
<div class="detail bedrooms">
<span>Number of bedrooms:</span> {{this.rental.bedrooms}}
</div>
</div>
<LocationMap #location={{this.rental.city}}/>
</article>
The only thing I have added to the above is the line <LocationMap #location={{this.rental.city}}/> but it still doesn't work if I remove it.
The console shows me no errors and I can actually see I am getting the three dummy objects I want from Mirage:
So I'm definitely getting the objects and from what I see I'm doing everything necessary in the templates to render it but they aren't. Should I be looking somewhere else?
Are you able to provide a link to your example? By having each piece of the ember application you mention it would be best to answer definitely. I can give a general answer with strategies for debugging the template.
The conventions behind ember.js make understanding the "whys" frustrating at first and possibly opaque. Ember's handlebars implementation governs how values are populated and accessed within templates using very specific rules. Ember treats templates (handlebars files) differently depending on whether it is for a route or component. Component's have an isolated context and must receive values by explicit passing in or dependency injection. Then, you can use such values in a component's template by accessing those properties with {{this.somePassedInValue}}.
In the super-rentals app, it appears the rental index route invokes the components responsible for displaying the individual units. I found this in app/templates/rentals/index.hbs.
<li><RentalListing #rental={{rentalUnit}} /></li>
The route template iterates over the list of filteredResults. Each of these is the rentalUnit. A good first step would be to use the {{log}} helper to print out that the value of rentalUnit to ensure it is what you expect.
Alternatively, you could try cloning https://github.com/ember-learn/super-rentals and applying the changes you want to make step by step from the master branch. By doing so, you could easily undo a single change to see what caused something to not show up as expected.
<LocationMap #location={{this.rental.city}}/>
to be written as below
<LocationMap #location={{this.rentals.city}}/>
may be typo error.
also repeat this for there place in that template.
Because the model name in the console is rentals not rental
(First allow me to say that I'm just beginning to learn Node-RED concepts; I went through some beginners' guides at nodered.org, and now am trying to extend what I learned so far).
I'm trying to build a flow that starts with a simple JSON tree like
[{"position":"1", "title":"element #1"},
{"position":"2", "title":"element #2"},
{"position":"3", "title":"element #3"}]
To build that treee I use a template node, property is set to msg.payload.
The number of array elements (in theory) is dynamic. To make sure that this tree is true JSON I added a JSON node converting from String to JSON object.
Next I wish to parse that object into a dynamic html table. For this I used a JS function node that's looping through the object and embedding its elements into the according html elements like this:
var return="";
for(var i=0;i<=msg.payload.length-1;i++){
var row=msg.payload[i];
if(row){
return+="<tr>";
return+="<td>"+row.position+"</td>";
return+="<td>"+row.title+"</td>";
return+="</tr>";
}else{
return+="no object at index "+i.toString();
}
}
msg.payload=return;
return msg;
The output of the function then should be passed into a 2nd template like this:
<html>
<head></head>
<body>
<table border="1">
<tr>
<td>#</td>
<td>Title</td>
</tr>
{{ payload }}
</table>
</body>
</html>
I would have expected that the function's result is inserted into the static table of my template, and that happens indeed but not the way I hoped: somehow the html elements that got created by my function are not recognized as what they shoud be; instead I see that they are rendered as
<tr><td>1</td><
instead of
<tr><td>1</td>
etc.
Result is that the browser does not recognize those elements and prints them together with their contents outside my static table
Questions:
what do I need to do so that my 2nd template recognizes my computed string as a set of html elements?
or is this probably a concept not suitable for my purpose?
I'm assuming you are using handelbars for your templating engine. In that case use:
{{{ payload }}}
Instead of
{{ payload }}
However a more elegant approach would be this:
<html>
<head></head>
<body>
<table border="1">
<tr>
<td>#</td>
<td>Title</td>
</tr>
{{#each payload}}
<tr><td>{{this.position}}</td><td>{{this.title}}</td></tr>
{{/each}}
</table>
</body>
</html>
then just
return msg.payload
again thanks to #als9xd for pointing me into the right direction; his 2nd idea indeed sounds much more elegant but first I couldn't get it to work. After some trial-and-error and looking up documentation for the template node I finally came up with this: removed the function node from my original question and then altered the 2nd template to this code:
<html>
<head></head>
<body>
<table border="1">
<tr>
<td>#</td>
<td>Title</td>
</tr>
{{#payload}}
<tr>
<td>{{position}}</td>
<td>{{title}}</td>
</tr>
{{/payload}}
</table>
</body>
</html>
Difference to #als9xd's example is that I replaced {{#each payload}} with a simple {{#payload}}, plus omitted this when referencing the object keys.
Could this be due to different Node-RED versions?
Anyways this is starting to be much fun!
I'm trying to make an app that pulls our data from our existing website. As we have no known API, I have decided to use ajax with whateverorigin.org to pull the full HTML from our site, and parse it with jQuery from there.
Unfortunately, much of the data I need lies far below any unique ID's. With $(data.contents).find("#unique").text() I am able to retrieve data of the type:
<div id="unique">
<div>
Text I want
</div>
</div>
However, as the nesting gets deeper, like:
<span id="unique2">
<table>
<tbody>
<tr>
<td>
Text I want
that snippet always returns "".
I have tried chaining the levels together with .join, but that doesn't work either.
Code in context here, around line 40-50. The site I'm trying to access is wmbr.org. The id that works is #now_on_the_air, one of the id's that doesn't is #recent_plays. The log is empty.
I am using Handlebars (v 1.0.0) to fill a table in HTML.
However somehow it does not fill the table like I am used to.
Here is my template:
{{#if users}}
<table>
{{#each users}}
<tr>
<td>{{username}}</td>
<td>{{email}}</td>
</tr>
{{/each}}
</table>
{{else}}
<h3>No users found!</h3>
{{/if}}
So I does find users because I do not see the "No users found!" and when I call an empty object it does show the "No users found!".
When I do not use the table and print out these users like the next example. The usersnames and mail address' will show up in my HTML.
{{#if users}}
{{#each users}}
{{username}}<br/>
{{email}}<br/>
{{/each}}
{{else}}
<h3>No users found!</h3>
{{/if}}
Here is how my template is build in the javascript:
var htmlSource = $(data).html();
var template = Handlebars.compile(htmlSource);
var compiled = template(usersArray);
that.$el.html(compiled);
Now when I console.log the compiled object, it doesn't show the table already.
Do you know why it doesn't work and can you help me out?
EDIT:
I just tested some more and found that the data will show up in the HTML when I leave out the <table> tags. However the <tr> and <td> won't show up in html. The data in it will be shown.
EDIT 2:
I found out that it seems to be a jquery issue or javascript issue.
When I console.log the htmlSource the HTML template is changed to this:
{{#if users}}
{{#each users}}
{{/each}}
{{else}}
<h3>No users found!</h3>
{{/if}}
<table><tr>
<td>{{username}}</td>
<td>{{email}}</td>
</tr></table>
As you can see the table is moved outside the if statement.
I tried other jquery versions (2.0.2, 2.0.3, 2.0.1, 1.10.2) but this didn't work.
I tried using the innerXHTML script however this works the same as jQuery.
So my guess is that it might be a FireFox issue (though I tried 25 and 26), Chrome does the same... maybe something in EcmaScript??
I will let you know soon...
EDIT 3:
I created an html file with the html I need. With a script I get the specific section of html I need and place this in the data variable.
Now when console logging the data (console.log(data)) there is nothing wrong.
When console logging the data with jQuery, the html is altered: console.log($(data));
It seems something is going wrong there.. but only when using table tags.
Is this something jQuery can't handle? I don't know. I know how to overcome this issue by using the script tag... Though I would like to load that using require ;)
P.S. nemesv thanks for you're edits ;)
Make sure you're outputting your template in a tag so the browser don't try to parse it.
<script type="x-template" id="the-tpl">
{{#if users}}
<table>
{{#each users}}
<tr>
<td>{{username}}</td>
<td>{{email}}</td>
</tr>
{{/each}}
</table>
{{else}}
<h3>No users found!</h3>
{{/if}}
</script>
If there's no type attribute on the script tag, the HTML parser will find a bug and try to resolve it. From your bug details, it really looks like it is the case here.
Using require
As you noted you load using Require, than make sure you load your template using the text! plugin: require("text!path/to/template.html")
To be even fancier, you could delegate all the handlebars compilation to a handlebars template loading plugin so templates are precompiled inside your build - this is probably the best.
Either way, your issue is that your template get parsed by the HTML parser and that is breaking your template content. Make sure you load it as "text" via XMLHttpRequest or with require or inside a script tag correctly typed.
Simon's solution is the right one and problem is discussed in various comments.
I am just putting the pieces here.
as #rescuecreative pointed out some browsers remove empty <table> tags when the html is inserted in DOM.
The case here is a bit similar. The browsers do misbehave when they see invalid markup. but in this case its not because of missing tr, td inside table. its because of extra lines before tr in the table.
Your problem starts here.
var htmlSource = $(data).html();
whatever loading mechanism you are using, you inserting the template in the DOM before compiling it with handlebars.
This is what happens when you add uncompiled template to DOM.
{{#if users}} <!-- browser does not understand this and tries to print it as it is-->
<table> <!-- browser sees the table tag and immediately looks for child TR, tbody, thead -->
{{#each users}} <!-- but instead finds plain text and hence considers it as invalid markup -->
<tr> <!-- the same story is repeated again>
<td>{{username}}</td>
<td>{{email}}</td>
</tr>
{{/each}} <!-- such plain strings are taken out of table markup and placed before or after depending upon the browser, in case of chrome placed before the table -->
</table>
{{else}}
<h3>No users found!</h3>
{{/if}}
This is how chrome renders it -
{{#if users}}
{{#each users}}
{{/each}}
<table>
<tbody>
<tr>
<td>{{username}}</td>
<td>{{email}}</td>
</tr>
</tbody>
</table>
{{else}}
<h3>No users found!</h3>
{{/if}}
I dont have firefox with me as of now. but i am sure firefox has its own markup correction method.
after this when you take it out of DOM using jquery and compile
var htmlSource = $(data).html();
var template = Handlebars.compile(htmlSource);
it will just output this
<table>
<tbody>
<tr>
<td>{{username}}</td>
<td>{{email}}</td>
</tr>
</tbody>
</table>
if you want you can dump it after compilation to see.
var compiled = template(usersArray);
console.log(compiled);
that.$el.html(compiled);
This also explains why you are getting username and email when you strip out table markup in the original template.
Now to solve this issue either use text plugin as Simon pointed out. or place the template inline.
I too use requriejs with something along the following
define([
'underscore', // you can replace this with handlebars
'text!path/to/template.html'
], function(_, Tmpl){
return _.template(Tmpl); //and handlebar pre-compilation here
});
this way you can also replace this module with a pre-compiled template in your build process.
I have a simple View Model that has the traditional one-to-many relationship: Customers have Orders.
I'm creating a table such that each line has a Customer name with a line of Orders following it. It's all working great, but unfortunately few Customers have the same amount of orders, so building the table ends up looking like a sideways bar chart.
I know there is a standard solution to this, but I can't for the life of me figure it out. What I'm going for is to have every row contain the same amount of cells. I've considered putting dummy data into my model, but that just smells bad. I can get the maximum amount of orders possible, but I can't seem to figure out how to design a template that can use any of that information to create uniform tables. Here's what I have so far:
//#data is my ViewModel containing a few other things
<table id="ordersTable">
<!--header stuff-->
<tbody>
{{for #data.Customers tmpl='tmplRow' /}}
</tbody>
// OrderRow.tmpl.html
{{if #index % 2 == 0}}
<tr class="tableRowAlt1">
{{else}}
<tr class="tableRowAlt2">
{{/if}}
<td>{{>Name}}</td>
<td></td>
{{for Orders }}
<td>
{{>OrderId}}
</td>
{{/for}}
</tr>
EDIT: So I've decided to just force the numbe of columns, but now I'm getting some strange behavior on my new template:
// OrderRow.tmpl.html
{{if #index % 2 == 0}}
<tr class="tableRowAlt1">
{{else}}
<tr class="tableRowAlt2">
{{/if}}
<td>{{>Name}}</td>
<td>
{{>Order[0].Id}}
</td>
<td>
{{>Order[1].Id}}
</td>
<!--...-->
</tr>
It's rendering "Order.0 is null or not an object." I've verified that every row has data in it, but yet the template thinks it does not. I imagine this has something to do with the fact that I'm looking for array elements, but I'm not sure how to go about accessing the elements by index in a way that jsRender will accept.
This may not qualify as an answer but its too long as a comment.
I don't 100% follow what you want. I'm guessing that if you have 5 customers and one customer has 10 orders and the 4 other customers have 7 orders, then you want 3 empty td tags for the other 4 customers each.
The first thing you need to know is the length of the longest order list. Lets say its 10. You then need to pass that into the OrderRow template. Actually, you need the difference between the max and the number of orders of the current customer. Lets call that delta.
Now, after the {{for Orders}} loop, you have two choices. You can either add a single td with a span of delta or you can add delta number of empty td tags. You want to do either of these additions only if delta is greater than 0. Assuming ~max is passed in as the maximum number of orders than something like:
{{if (~max - Orders.lengh) > 0 }}
<td colspan='{{ ~max - Orders.length }}'></td>
{{/if}}
What I'm confused about is the end result is going to look the same. It will still have the orders as a sideways bar chart on the left hand side of the table.
You mentioned "a standard solution to this". Can you point to a rendered example somewhere?