After I call render on my jsRender template, it seems to be consumed, and thus is removed from the DOM. This is frustrating as I have a page where the template needs to be rendered several times depending on user interaction.
console.log($('#tpl'));
$('#container').html($('#tpl').render(json));
console.log($('#tpl'));
The second console.log is an empty array, and I can confirm the template no longer exists using the DOM inspector and the exception that jsRender throws: Uncaught JsRender Error: Unknown template: "#tpl" -- the page must be reloaded to re-inject the template into the DOM.
How can I persist the jsRender template between renderings?
I'm still not sure why it has to be consumed and simply can't stay in the DOM after rendering the first time, but I found a workaround. If anybody knows the reason for removing the template from the DOM, I'm still interested.
Update: Actual answer (Thanks, Boris)
My template was within my #container element, so the html() method was of course overwriting it. Silly me.
Workaround Neat little trick anyway
Using this 'variant' example, I saved the template in a local variable. Then I call render on the variable name instead of the jQuery selector:
var tpl = $.templates('#tpl');
.
.
.
console.log(tpl);
$('#container').html(tpl.render(json));
console.log(tpl);
This has also managed to preserve the template across renderings.
I also had a similar problem today where I had two target divs and two script block templates in the body. My problem was that I hadn't closed the div element tags correctly (too much xaml) and the result was the second template was never rendered as it couldn't be found.
Here's a JsFiddle showing the correct usage (rather than the /> self-closing syntax):
http://jsfiddle.net/jgoldsmith/XvvPC/
Hope that helps someone else.
Related
edited
This question is in addition to previous Handlebars with Backbone template not rendering problem in which browseser was not rendering forms at all now the problem was solved for the form but returns another error which is possibly also with the rendering.
I have an app with Backbone on front-end and Express in back-end with express-handlebars templates and I was trying to change from Underscore templating to Handlebars while still leaving backbone logic behind. So the way I did it - installing handlebars via Bower and then required it.
app.ContactView = Backbone.View.extend({
//....
template: Handlebars.compile( $('#tmpl-contact').html() ),
//....
});
instead of
app.ContactView = Backbone.View.extend({
//....
template: _.template( $('#tmpl-contact').html() ),
//....
});
But it returns errors and I don't really get what causes them.
When I try to load a page, for example:
http://192.168.33.10:3000/contact/
It doesn't shows any errors in the browser. But if I move to:
http://192.168.33.10:3000/about
which doesn't even have Backbone logic behind it returns an error:
You must pass a string or Handlebars AST to Handlebars.compile. You
passed undefined
Which means that template is compiled before the DOM loads on the page. But my Backbone script is loading after html script e. g. after the {{{body}}} element. Also if there is a problem with an order I should get this error on every, not only when moving to another.
So I think the cause is some kind of a conflict between front-end handlebars and express-handlebars on a server. How to solve this, preferably that templates to be rendered via expHbs instance?
If the only change that you made was:
template: Handlebars.compile( $('#tmpl-contact').html() ),
to:
template: _.template( $('#tmpl-contact').html() ),
it wouldn't cause the problems you're describing. As the error message told you "You passed undefined", and undefined wouldn't have worked with the Underscore code either.
It seems more likely that you changed something else, and that change caused the issue you described. However, without more code (ideally a JS Fiddle) it's hard to say what.
Problem solved by loading each Backbone part as a separate file for its route.
There's a piece of code in my template that's repeated multiple times through it... So I want to DRY it.
It's a simple piece of code with no logic on it.
First, I though about creating an array with the title and values for a loop, but then I remembered Handlebars does not allow me to do it.
Second, I though on partials. They're only a part of a template, afterall. But then I noticed it apparently ignores additional arguments. Thus, there's no way to alter it's contents besides what's already in my model. In this and in the prior case, I didn't want to mess my route/controller with template-only variables.
Third, my current solution is a Component, but it seems so dirty. However, it's quite troublesome to have a JS file with only an empty object; furthermore, components have no file structure, and I wanted this piece of code to live along with it's original template.
This is a simplification of what I wanted to achieve:
project/index.hbs
{{partial "counter_block" value=model.count_success title="Yeah!"}}
{{partial "counter_block" value=model.count_failures title="Boo :("}}
project/_counter_block.hbs
<div>
<h2>{{title}}</h2>
<span>{{value}}</span>
</div>
Is there any solution for this matter or is this just another case where Handlebars oversimplification causes coding issues?
That's when you create a component, the js file isn't necessary, you can just have a template components/counter-block
{{counter-block value=model.count_success title='Yeah!'}}
{{counter-block value=model.count_failures title='Booo!'}}
http://emberjs.jsbin.com/suxebehaqe/1/edit
I would like to define a function like this (in a component in this case):
doSomething: function() {
// do something
}.observes('yield')
Where the component handlebars file could basically be:
...
{{yield}}
...
Will this do what I want? That is, when the yield value changes, will the doSomething() function be called?
U should use a parameter not the yield content for this! Like:
call this from the template:
{{tool-tip value=myValue"}}
and the controller:
myValue: function() {
return 'Hi %#, welcome to %#'.fmt(this.get('val1'), this.get('val2'));
}.property('val1', 'val2');
instead of the solution that u probably wanted to use:
{{tool-tip}}
Hi {{val1}}, welcome to {{val2}}
{{tool-tip}}
Let me explain u the problems with your idea:
what u have there where the {{yield}} is, is a template. U could observe the template, but the template itself never changes. The template is a compiled handlebars template!
So there is not even the string Hi {{val1}}, welcome to {{val2}} anymore, but there is a compiled version from this. So a javascript function that will produce Hi Krutius, welcome to StackOverflow if u call it with the JSON { val1: "Krutius", val2: "StackOverflow" }.
U see, this function will always be the same!
So you have two problems:
The first is to get resulting HTML that u need to insert into the title attribute of your tooltip containing thing, or to give to the tooltip() function as the template property. This is tricky. A working solution is to still use the {{yield}} in a hidden tag, grab the html and put it into your tooltip. This will work (as long the values don't change) but is definitely a dirty solution. I think u maybe have already done this, and thats why you want to observe {{yield}}.
But its important to know that the view won't rerender! For each value there is a view created and some strange <script> tags inserted into the DOM, and then a observer is attached to that single value, and when the value changes the specific place in the DOM can be found due the <script> tags and the value will be updated.
This behavior will change in the future when HTMLBars will come up. So you can't relay on it. Maybe a better working way is to call the handlebars function yourself to get the html.
But u still have another problem. When will the result of the handlebars function change? For that you would need to know what property the generated handlebars function requires. You probably could do this by analysing it, but maybe the function is different with HTMLBars? And it would need a lot of hacking into the internal ways of ember rendering the HTML!
So, over all, just don't do it. Solve it by giving a single value to the component and render the HTML for this value yourself!
So I have an object like this {"templateName":"myTemplate","data":{"one":1}}
Here is my template:
{templateName}
{>"{templateName}":data/}
This does not render though (with no error message)...however, it works when I change it to this:
{templateName}
{>"myTemplate":data/}
It renders like this in the view:
myTemplate
[then here it shows myTemplate, rendered with data passed to it]
It renders perfectly and even shows the correct template name on top. I thought that putting the key in the quotes would work, but I guess I am misreading the dustjs guide. How can I accomplish this?
After doing a little digging, I believe I have found the problem. By using the syntax {>"{templateName}":data/}, you are changing the context from root to data. When this happens, templateName is no longer accessible when Dust tries to resolve the template name. So, Dust ends up searching for a template called "". I have filed an issue for this bug.
Having said that, if I were to write a book called "Dust: The Good Parts", I would leave contexts out of it (e.g. {#myData:myContext}). I have found that they cause more problems than they solve.
As a workaround, you can use this syntax:
{templateName}
{>"{templateName}"/}
And then your "myTemplate" would need to do something like:
{data.one}
Here is an example of this working.
As part of an attempt to port a fairly large/complex existing application to the Ember world, I'm generating and compiling named Handlebars templates dynamically, and associating views with them, using the technique:
var template = Ember.Handlebars.compile("some handlebars stuff");
Ember.TEMPLATES["myTemplate"] = template;
var view = Ember.View.create({
templateName: "myTemplate"
});
One of the things I'd like to do is be able to recompile new/different Handlebars template markup which overwrites the template named "myTemplate" and have it be accessible to views at that name.
I'm getting unexpected results trying to do this - a couple fiddles that illustrate the problems:
First fiddle - Shows what happens if you wait before rendering a view after the named template contents have changed.
Second fiddle - Shows what happens if there's no delay before rendering a view after the named template contents have changed.
There's obviously some magic under the hood that I'm not understanding. Can anyone shed some light on this?
UPDATE:
I went through the source code for Ember.View and the container module, and came to realize that I could solve the problem in the First fiddle by overriding the "template" computed property in a way that skips the container cache lookup. I've put up another fiddle here to demonstrate the solution I found.
This seems to be working the way I'd like it to - but - it feels like I might be fighting with the framework and "unhooking" from the container in a way that might bite me later. Is there a better, more Ember-esque way to accomplish what I'm trying to do? Will the hack I found break things?
UPDATE 2
I've also discovered that it's also possible to simply call
view2.get('container').reset();
before appending view2 in the First fiddle. Seems cleaner/safer, but is it "legal"? I've updated the First fiddle to illustrate this.
(in the second fiddle, both views show the second template)
This is because view1.appendTo($("#target")); just schedules the append, actual view rendering does not happen until end of the run loop. Before that happens, you've set Ember.TEMPLATES["myTemplate"] = template2;
(in the first fiddle, both views show the first template)
Pretty sure this is because ember container caches template fx, but not 100% on that. Checking...
I'm going to call this one answered. As I mentioned in my second comment, I'm using the solution shown in this fiddle in my project, along these lines:
mYiew.get('container').reset();
There's some discussion about the container not being intended to be used as an API here: https://github.com/emberjs/ember.js/commit/5becdc4467573f80a5c5dbb51d97c6b9239714a8 , but there doesn't seem to be any mention of using the container from Views for other use cases.
Also, a View's container can be accessed directly (at ".container") - meaning the devs haven't made it "hard" to get to the way they have for an Application's ".__ container __". This might suggest something about how they intend it to be used.
Since a View having the ability to clear its cache whenever it wants to doesn't seem to me to be unreasonable or a bad practice, I'm using the above mentioned solution...at least until someone sets me straight with a better idea (or a cache API).