I'm trying to test my app with qunit and sinon. I want to test some behaviour of the controller including its setup on a transition. For example in 'setupController' in a given route.
However the problem is that the controller is automatically created and I only know that you can get it with a 'controllerFor' call in the route. How can I get at the auto generated controller(s) for a given page from the outside, e.g. App.xxxxx
Note that I tried doing a 'transitionTo' passing the controller as the second argument but this means that 'setupController' never gets called so I can't test the scenario I wanted to. If there were a way to force setupController to be called even when you pass the second argument this would do the job
Thanks for any help
Related
I have a case where I am passing a property to child component wrapped inside a mut helper. Then I am updating this value in child component, and send action into parent component using closure action.
Then if I read this value in parent component with get, I get correct value. But if I pass this property to action from template, I get not updated value.
I've provided an Ember-Twiddle to illustrate this problem (check out console.log).
Am I missing something in this solution? Or is it working as expected? Please take a look at onInnerUp action in index controller to see what I am trying to do.
I know this is very messy description, hope someone will understand what am I struggling with
It is working as expected. Here is why?
When the template index.hbs is rendered at some point it creates an action via the action helper with the current value assigned to controllerVal. In your case; after the initial render that value is 0.
The action will only be recreated after the template is re-rendered. In your twiddle you are not waiting for the template to re-render and immediately triggering the action (which has already been created with the previous value of controllerVal). Even though the controllerVal is increased automatically (due to two-way-binding) when value within my-component.js is increased; the action is not yet re-created and holds the previous value; hence you get previus value that was assigned to controllerVal as parameter to your action. I hope I manage to explain what is going on in a simple way.
In fact Lux has already explained the way to overcome this situation in his comment above; but I have created a modified twiddle for you. In this twiddle I am not immediately triggering the action; but delaying it till the next rendering is finished via Ember.run.scheduleOnce(queue, target, method). I used afterRender queue in order to wait for rerendering of the template. You can look further for Ember run loop if you like. This might be a good starting point I guess. My best regards.
I'm trying to figure out why an Ember component is not working (and trying to learn about component lifecycles in the process). The component in question is Ember-cli-mapbox. It uses nested components. You can have a mapbox-map component, and within that component you can have several mapbox-marker components. Now, how it is supposed to work, is the mapbox-map component initialises the map, and then passes a block to the child marker components. The child marker components then references the map that got passed down to them. An example of the components in use (which come from the component docs):
{{#mapbox-map mapId='ember-cli-mapbox.7c3914f2' as |map|}}
{{#each positions as |position|}}
{{mapbox-marker map=map coordinates=position.coordinates}}
{{/each}}
{{/mapbox-map}}
Now, the components get set up using the didInsertElement hook, which makes sense to me since the DOM needs to be in place before the mapbox-map component can bind to a element in the dom. It doesn't work like that though. The didInsertElement of the child components get executed before the didInsertElement hook in the parent component. So, the marker tries to reference the map before it was created. I figured this out by putting console.logs in the component init code. I can't find much documentation on component lifecycles. didInsertElement does get referenced in the API docs here, but it seems that the newest API docs are actually out of date and don't reference a bunch of other hooks described here. The latter link says that life cycle events happen in the following order:
didInitAttrs
didReceiveAttrs
willRender
didInsertElement
didRender
Now, things get weird. When I replace didInsertElement in the components with didInitAttrs, it fires in the correct order. The didInitAttrs hook on the parent component fires first, followed by the child component didInitAttrs hooks. Problem with this is, the DOM isn't ready yet, so it doesn't help much. I also can't put the map binding event in the Ember runloop, since it need to be returned and passed as a block to the child elements.
So, my questions are:
Why, when using didInsertElement on components, do the hooks get executed in the order they do? (children, then parents)
How did this component ever work in the way it's currently written?
Am I supposed to use the above mentioned hooks if they're not mentioned in the official API docs?
I've recreated the addon in an Ember Twiddle here. Child hooks get called before the parent hooks, causing the component to break since map is undefined when the hook is called. This happens on Ember 1.13.8 as well as 1.13.9.
Why, when using didInsertElement on components, do the hooks get
executed in the order they do? (children, then parents)
This was changed in version 1.8. It was previously parent, then children but this often required people to use some complicated method of waiting for children to render to do certain things. Changing the order made learning Ember simpler.
See https://github.com/emberjs/ember.js/issues/5631 for more information.
How did this component ever work in the way it's currently written?
I have not used this addon, and have no idea if it works or not. I fixed your twiddle to work, however: http://ember-twiddle.com/4c3e55d0a66ead378bdf
Am I supposed to use the above mentioned hooks if they're not
mentioned in the official API docs?
These hooks are not mentioned because the documentation is still catching up to changes in Ember. Feel free to use them if you'd like.
I am displaying a list of Document titles on the site's sidebar area. To achieve this I created a documents property on the App.SidebarController
Document = require '../models/document'
SidebarController = Ember.Controller.extend
documents: (->
Document.find()
).property()
module.exports = SidebarController
This works fine, when I create a new Document in my application, this sidebar property get's updated automatically and I don't know why. It works no matter what I put in the .property() function. e.g.
.property('Document')
.property('App.Document')
Can somebody explain how this works? What would be the correct thing for the property() function to observe?
So you are basically saying that is works, but you would like to know why.
Here is my guess: When your controller gets rendered, your computed property gets computed for the first time and an instance of an array is returned. When you create a new Document, i think this very same array is updated (= Document is added). So it does work despite the fact, that you do not specify a dependent key on property(). This is because the right array is already in place. As i said just a guess, but this seems like an understandable explanation.
And what would be the correct thing to do?
IMHO one should not use such Data Access methods directly in a computed property. Instead you should call this Data Access method inside a Route and then assign it to the controller. This way you make sure, that the find() method gets executed when needed.
Playing around with ember, I found that sometimes the model is stored on the controller's content property, sometimes the model is directly available on the controller as well. I do not understand however, when this is the case.
Let me explain it by an example which I found when assembling my ember MVC.
Setup A - The start
I defined a custom Member object, corresponding MemberRoute, MemberView classes and a template with the name member.
The Member object had some attributes such as id, nickname, etc.
NOTE: no controller of the form MemberController was defined, thus by ember's convention, it provides the controller on its own.
Setup B - The customization
Same as setup A, but now there is a MemberController defined that contains some action methods that are triggered from within the template.
The strange behaviour (resp. what I do not completely understand)
in setup A, I can refer to the Member's attributes directly with {{id}} or {{nickname}}.
in setup B, I have to use {{content.id}} or {{content.nickname}}
As documented in ember's documentation, MemberView does
setupController : function(controller, member) {
controller.set('content', member);
},
So, could somebody help me to understand why the difference and where the difference is? Currently, my guess would be either
that the context of the template is different (possibly there is a code piece missing in the setup of the controller?)
or
the default controller that is provided by ember automatically, has some additional magic that is not directly avaiable for customized controllers.
Any help to understand this is highly appreciated. It already took my quite a while to come as far as this. I first thought it could be the modularization introduced by the project setup with requireJS (well, I still think that could have a influence). Ember is v1.0pre4.
Thanks in advance!
Patrick
So, could somebody help me to understand why the difference and where the difference is? Currently, my guess would be either
that the context of the template is different (possibly there is a code piece missing in the setup of the controller?)
or
the default controller that is provided by ember automatically, has some additional magic that is not directly avaiable for customized controllers.
It's hard to say for sure without seeing your code, but my best guess is that your MemberController extends Ember.Controller. The default provided by ember (in this scenario) would have been an Ember.ObjectController. If that's what you want, change your MemberController definition to:
App.MemberController = Ember.ObjectController.extend({
myProperty: 'value'
});
An objectController acts as a proxy to it's content property, typically that is an ember model. So if things are wired up correctly you should never need to access a model via the 'content` property. If you ever see something like:
{{content.id}} or {{content.nickname}}
it's a sign that you should change to an ObjectController. See EMBER GUIDES: REPRESENTING A SINGLE MODEL! for a more detailed explanation.
an ObjectController acts as proxy to the object set to the controller's content. When no controller is defined, Ember will create a controller for you and set its content by default to whatever object is returned by the model() function, if defined, in the route. The behaviour should be the same whether you define your own controller or let Ember define one for you
The default context in the template is the controller itself i.e. this = an instance of your controller or the generated one. When you try to access nickname in that context, Ember will first try to resolve it against the controller itself and if nothing is found, it resolves it against its content, i.e the object if you already manually set it to the controller's content.
Finally, there is no default implementation of the model() function in the Route except when you're using dynamic urls, say /foo/id that resolves against /foo/:id, Ember uses the id provided to load a Foo object with the id provided, thus providing a default implementation to the model() function. At the end it boils down to the same mechanism, only automated for your convenience.
I suggest you listen to this for more insights on how things are automated for you by Ember. But when it comes to the content being displayed, there is no magic you have to manually wire the content of the controller.
I am working on an Ember app where I wish a button click to transition to a new route passing an object as the context for that route.
The problem is, no matter how I try and pass that context, it is always lost by the time I get to the route I am transitioning to.
Investigating (http://jsfiddle.net/fxbXM/2/) it seems that the the context is passed to the first route the router hits as it works to the requested route. (See the output of the connectOutlets function from each route level)
This seems to confirm what I thought I had found when debugging my actual app: in the triggerSetupContext function of Ember there are three enterStates: [Router, 'root', 'create'] and there are three contexts: [myObject, null, null]
If the contexts were in the opposite order then I would get what I wanted where I wanted it!
Am I making some fundamental error in the way i'm passing the object as a context? Should I be using another approach?
A bit tricky to realize what does'nt work... while debugging, I found this: in routers, only states with dynamic segments have a context, so in the create route, I added ":widget_id", and it seems to work. see http://jsfiddle.net/Sly7/EqyUa/
It seems that if you don't have any dynamic segment in your route, you can't pass a context to it. It would be great if I can have confirmation of that...
Update
Searching on github repo, I found this issue: #1118. In the last comment, Yehuda explain why this does not make sense to pass a context to connectOutlets without dynamic segment.