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...
Related
Creating in-repo-addon in Ember.JS gives a lot of possibilities. Today I've stumbled into a case when I wanted to register all classes in directory (for example my-dir) into Dependency Injector (same way it's done with services by Ember, but I wanted to use some other namespace).
For example I want to allow developer to create multiple classes inside proxy directory, and register all of them in DI under proxy: namespace. So later we can use:
Ember.Component.extend({
myProxy: Ember.inject('proxy:my'),
});
You'll need to do this using an initializer. More details on this here: https://guides.emberjs.com/v2.12.0/applications/dependency-injection/
The hard part may be getting all proxy items in s folder to automatically register ...
Edit
Looks like I didn't spend enough time thinking about this. You should be able to do at least part of this easily. There are two parts to this.
Part 1
Ember currently uses the ember-resolver to handle lookups for various items. If you check the tests for the resolver you'll notice that you should be able to map in anything you want: https://github.com/ember-cli/ember-resolver/blob/master/tests/unit/resolvers/classic/basic-test.js
So in your case, if you do a Ember.getOwner(this).lookup('proxy:main') from within an Ember instantiated class (a route, controller or component for instance) it would look in app/proxy/main.js which your addon could be populating.
Details on the Ember.getOwner lookup are available here: https://emberjs.com/api/classes/Ember.html#method_getOwner
Part 2
So at this point you can lookup proxies (which would be doable in an init method). But if we want to get truly elegant we'd want to allow Ember.inject.proxy('main') syntax.
Doing so would involve calling a private method inside of Ember.inject in an initializer. Because that naming scheme is changing in the new Javascript modules RFC, it may be unwise to try to add this syntactic sugar ...
So I'd advise avoiding touching that private API unless it's really important to your app design.
I have created a mock application to illustrate the situation I am facing: Mock App
In this application; I have created a service with a single boolean property and a function to toggle that property (x); and two components (one to toggle the service's property; the other to observe the number of toggles and display it). The observer lies in toggle-observer. It is added directly to service's property as: myService.x. The code is not working as is; however if the comment at line 14 of toggle-observer.js is commented out; the observer starts working.
My question is that, do I need to perform a get to the whole path of a nested observer property to get it working? Is this the expected behavior? If so, can somebody explain why? My best regards.
Note: This is a mock example to illustrate the case; it is not related to anything I am designing in a real app. I am trying to avoid observers as much as possible; but I ran into this situation while trying out sth. and decided to ask it.
From ember guide service
Injected properties are lazy loaded; meaning the service will not be
instantiated until the property is explicitly called. Therefore you
need to access services in your component using the get function
otherwise you might get an undefined.
From ember guide, unconsumed computed properties do not trigger observers.
By combining the above two concepts, we can come to the below conclusion,
You haven't used myService any of the property inside toggle-observer component so it will be undefined until you explicitly call get function or use it in template.
Unless you use it x property in toggle-observer component, then it will not trigger observer. You need to consume it either in toggle-observer.hbs file or in init method.
Previously when I developed ember application I used App as my global object and every class was stored in this big object.
Like this
window.App.MyModel = Em.Object.extend({});
And in the browser console, I was able to do
App.MyModel.create();
So it was really easy for me to access MyModel class.
Now I started experiments with Ember-CLI, I don't have much experience with this kind of tools. I followed the documentations and I created my model Service like this.
var Service = Ember.Object.extend({});
export default Service
But now, how to access Service class from browser console?
Only way I found was:
App.__container__.resolve('model:service')
But I don't like it much. Is there better way? Btw, can you please explain how export works? Or is there some source (documentation, article) where I can study it?
Thanks a lot for response.
If you're aiming to have something available in most places throughout your application you'll typically want to register it on the container.
There are multiple ways to access the container, and you're correct in that the one you found was less than ideal. The running joke is "any time you directly access __container__ we need to add more underscores."
To directly control how things are registered on the container and injected into the container you typically want to use an initializer. Using initializers in ember-cli is super-easy, they're executed for you automatically.
Checking out the documentation you can see that you get access to the application's container as an argument which allows you to manipulate it in a safe manner.
Once you have access to the container you can use register and inject to make content easily available in particular locations. This was introduced here. One note, accessing things inside of the container from outside the context of your app (browser console) will require the usage of App.__container__ and that is the expected use pattern.
And the export you're running into is an ES6 module system construct, it's not Ember-specific. Playing with the ES6 module transpiler can give you a good sense of what goes in and what comes out in "we can do this today" type of JavaScript.
For ember 3.22, application classes can be accessed like so:
Ember.Namespace.NAMESPACES[1]._applicationInstances.values().next().value.lookup('service:state-events')
Note, you may need to modify the index in NAMESPACES[1] to be something other than 1. You can determine which namespace is your application when this returns true:
Ember.Namespace.NAMESPACES[1] instanceof Application
This approach is how ember-inspector accesses ember applications: https://github.com/emberjs/ember-inspector/blob/50db91b7bd26b12098cae774a307208fe0a47d75/ember_debug/main.js#L163-L168
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).
I need a way for ember router to route to a recursive path.
For Example:
/:module
/:module/:submodule
/:module/:submodule/:submodule
/:module/:submodule/:submodule/...
Can this be done with Embers router, and if so, how?
I've been looking for examples, tearing apart the source, and I've pretty much come to the conclusion, it's not possible.
In a previous question, someone had pointed me to a way to get the url manually and split it, but I'm stuck at creating the state for the router to resolve to.
As of now, in my project, I currently just use the Ember.HashLocation to setup my own state manager.
The reason for the need of this, is because the module definitions are stored in a database, and at any given point a submodule of a submodule, recursively, could be added. So I'm trying to make the Application Engine handle the change.
Do your submodules in the database not have unique IDs? It seems to me that rather than representing your hierarchy in the path, you should just go straight to the appropriate module or submodule. Of course the hierarchy is still in your data model, but it shouldn't have to be represented in your routing scheme. Just use:
/module/:moduleId
/submodule/:submoduleId
And don't encode the hierarchy in the routes. I understand it might be natural to do so, but there's probably not a technical reason to.
If your submodules don't have unique ids, it's maybe a little tougher...you could build a unique ID by concatenating the ancestor ids together (say, with underscores), which is similar to splitting the URL, but a little cleaner probably. I will say that Ember/Ember Data doesn't seem to be too easy to use with entities with composite keys--if everything has a simple numeric key everything becomes easier (anyone want to argue with me on this, please explain to me how!).
DO you mean like this:
App.Router.map(function(match) {
match('/posts').to('blogPosts');
match('/posts/:blog_post_id').to('showBlogPost');
});