Emberjs - unable to redefine named, explicitly compiled Handlebars template - ember.js

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).

Related

Component Hierarchies and Template file grouping

I'm refactoring my code into components, I"m finding that component hierarchies are working well. The only issue I'm running into is with the template naming requirements. I'd like to be able to have multiple templates in a components directory, that I can lookup dynamically. An example below of whats working and whats not:
side-bar/
|__component.js
|__template.hbs
|__sidebar-links/
|_component.js
|_template.hbs
|_sidebar-drawer/
|_component.js
|_template-one.hbs
|_template-two.hbs
|_...
So -- as a simplification, my sidebar-drawer component can make use of many templates (as partials, or sometimes childViews with the layout property dynamically set), swapping them out based on links. I'd ideally like to group them together. Instead, to do something like this i"m forced to keep them in the /app/templates directory, or treat them like components.
I think I can get this working by modifying the templates compiler (found [here]):
app._podTemplatePatterns = function() {
return this.registry.extensionsForType('template').map(function(extension) {
return new RegExp(extension + '$');
});
};
This compiles all templates having hbs extensions, regardless of location. But I'm then unable to look them up -- presumably i'd need to sub-class the Resolver as well. I'm fine to start going down that path, but a few questions:
Should I be hesitant about using the (seemingly) private method _podTemplatePatterns above?
Is it a bad idea to modify the resolver to accommodate this (e.g. performance impact?)
Any other reason this might be discouraged in ember-land?

Idiomatic way to use __container__.lookupFactory in Ember.js

In the notes of this commit, the Ember team have made it very clear that App.__container__.lookup() is not the way to get at controllers. Instead we should use the needs property.
I understand the rationale behind this, and the idiomatic way to access singleton controllers.
However, in my app, I have some cases where I need instance controllers. In that case, I am using App.__container__.lookupFactory() to get at the prototype which I can then create() or extend()
Is there a better way to do this (without using __container__?
Edit:
Here is an example use case.
App.MyContainerView = Ember.ContainerView.extend
...
addChildView: ->
#get("content").pushObject(App.MyChildView.create(...))
The above example will push a new view onto the stack (allowing views to be dynamically created)
However, these views will (may?) not have the right container (and other properties?) set due to being created using App.MyChildView.create(). This is especially true in cases where we are doing a partial integration of Ember into an existing app.
The way to create these views would instead be:
App.__container__.lookupFactory("view:my_child").create()
In which case everything would be ok.
Additional use cases exist, for creating instance controllers outside the context of the router.. but the idea is the same.
I don't know if you're still looking for an answer. I am also struggling with how to do things "the Ember way".
This answer put me on the right track, and should be relevant to your question:
"Please ensure this controller was instantiated with a container"
As for me, I had the same problem as in the above question: when I manually instantiated my App.AnyOtherController with App.AnyOtherController.create(...), then inside this controller, I could not access dependency injections (such as a session object that I make available to all my controllers and routes).
Instantiating the same controller this way solves the problem by giving the controller a container:
this.container.lookupFactory('controller:any_other').create(...)
You should be able to access this.container from any view, and I guess, any controller, as long as they have been given a container.
You can Ember.String.decamelize('AnyOther') to convert the CamelCase controller name to a suitable string.
More on containers here: http://ember.zone/beginning-to-understand-the-ember-js-container/
If it doesn't help you, I still hope this helps someone out there, as this container stuff is a bit tricky at first...

The story with Index Controllers, Views, Templates?

I've created the following route in Ember:
this.resource('password_reset', { path: '/password_reset' }, function() {
this.route("request");
this.route("claim");
});
The Ember debugger -- which I LOVE btw -- shows me this results in the following:
I have created two templates so far:
/templates/password_reset.hbs
/templates/password_reset/index.hbs
When I go to the URL http://my.server.com/#/password_reset I would expect that -- based on what the debugger's telling me -- that the 2nd template listed (aka, password_reset/index) above is used but in fact it uses the frist one. What doing? Anyone care to shed some light on this mystery?
Ok, I think it can be chalked up to a newbie question. The relationship between these two controllers/views/templates becomes far more clear when I put an {{outlet}} into the /password_reset template. Then I can see that the password_reset/index shows up as the outlet. The index, in effect, becomes the default outlet when a sub-route is not defined. Pretty basic but somehow I didn't get it until I bumped into a wall or two.

jsRender template disappears after rendering

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.

Are Django template tags cached?

I have gone through the (painful) process of writing a custom template tag for use in Django. It is registered as an inclusion_tag so that it renders a template. However, this tag breaks as soon as I try to change something.
I've tried changing the number of parameters and correspondingly changing the parameters when it's called. It's clear the new tag code isn't being loaded, because an error is thrown stating that there is a mismatch in the number of parameters, and it's evident that it's attempting to call the old function.
The same problem occurs if I try to change the name of the template being rendered and correspondingly change the name of the template on disk. It continues to try to call the old template. I've tried clearing old .pyc files with no luck.
Overall, the system is acting as though it's caching the template tags, likely due to the register command. I have dug through endless threads trying to find out if this is so, but all could find it James Bennett stating here that register doesn't do anything. Please help!
I have gone through the (painful) process of writing a custom template tag for use in Django
I agree that the process for writing the template tag in django is more elaborate than it needs to be.
But let me point you towards some of the third party apps, that when installed, a template tag is just another python function (or class).
http://github.com/alex/django-templatetag-sugar
http://github.com/codysoyland/django-template-repl
Firstly, I can't imagine what's complicated about inclusion tags. Writing a completely custom tag from scratch, yes: that's complicated. But inclusion tags are simply three lines of code and a template - what's complicated about that?
Secondly, all Python code in your Django project is loaded once by the server [*], and remains until it is restarted. The dev server usually detects changes and restarts itself to reload the code, but this doesn't always work. You should get into the habit of checking the console to see that it does restart, and doing it manually if necessary. Note that this has nothing whatsoever to do with caching.
[*] strictly speaking, once per process, but the dev server is single-process anyway.
In case anybody else encounters this: the exact caching mechanism is unclear, but restarting the django dev server fixes the problem.