I have a "container" component
# components/container-component.js
import Ember from 'ember';
export default Ember.Component.extend({
displayComponent: 'simple-box'
});
The template is
# templates/container-component.hbs
<div>
{{component displayComponent}}
</div>
Now, from within "container-component" instance. How can I access the computed "displayComponent" instance?
Any ideas on how I could go about it?
When I use the get method I get the string identification of the component
this.get('displayComponent')
# returns (String) 'simple-box'
There is not a built-in way of retrieving children components from the parent component's js file as far as I know. If you really want to achieve this, please refer to my twiddle I have prepared for you.
As you can see from the code; within the template parent passes itself to the child and child registers itself to the parent. Note that this is an illustration of code smell and this is something I never use in my projects.
Because, in my opinion what you are trying to achieve is a clear violation of many principles that ember is built upon. Why do you want to get the child property from the parent directly? You can register actions to child component and get notified for various events within the child component. Why do you need direct access? It is better for a component to be working independently by itself. In this kind of approach you are polluting parent's code with child component's internal design considerations. You can query DOM tree via jquery within parent but getting the child component instance created for you by Ember infrastructure is a sign that you are trying to design something that is out of Ember's approach. We can help you better if you post another question (or modify this one) to describe your intention. May be someone could lead you to a better design. Best Regards.
Related
I have an Ember component which is essentially a panel. There can be multiple instances of this panel on a page, but only one can be "active" at any given time. Each instance of the component must be aware if any of the other panels become "active" so they can remove their "active" state. I would really rather not move the JavaScript to make this happen to a parent component. Instead, I would like to keep it within this component. In Angular, I used to use a static variable to do this. What is best way to do this in Ember?
I would really rather not move the JavaScript to make this happen to a
parent component
Do you want to avoid having the parent component dealing with anything related to panel "activity"? If so, why?* If not:
Ember automatically gives each component's tag (unless it's a tagless component) an id that is accessible from the js code as elementId. You could create a property activePanelId on the parent component and pass it to all panels: {{pa-nel activePanelId=activePanelId}} and then check in each panel
{{#if (eq elementId activePanelId)}}
{{!whatever is different on the active panel}}
{{/if}}
or use it in the js code:
isActive: Ember.computed('activePanelId', function() {
return this.get('activePanelId')===this.get('elementId');
},
If the panel becomes active by an action related to itself (e.g. clicking on it), just set activePanelId to the elementId in the respective action - since the property activePanelId exists only once on the parent component, all other panels to which it is passed will take note.
If using the elementId feels to hacky, you might as well give each panel a distinct name and store the activePanelName in the calling component.
*If you really do not want the property in the parent component, you could move it to a service that you then inject into the panel components, but I cannot yet imagine a good reason for preferring that.
#arne.b 's anwser sums it pretty well, the best way of handling component's state with common parent data, but if you are dead serious about not using a parent component there is kind of way to get it done.
Example:
import Ember from 'ember';
export default Ember.Component.extend({
sharedObject: {},
sharedArray: [],
});
In the above component you can use sharedObject or sharedArray to exchange state with multiple instances of the component. Any changes in the object or array will be reflected to all the instances of the same component.
A sample Ember twiddle.
I have read that Ember2 is attempting to remove controllers. I was even linked to this RFC https://github.com/ef4/rfcs/blob/routeable-components/active/0000-routeable-components.md. However, I have been following the tutorial, and they insist on making a Controller. Do we still need to make Controllers or is this out of date?
Controllers are still needed (and thus haven't been deprecated) for two reasons: query parameters, and because components aren't routable yet. You can follow the tutorial's use of controllers without it causing you too much grief later on.
However, if you want to pull ahead of the tutorial, you can use components instead, barring the two caveats above. There is no way around using controllers for query parameters, but you can avoid the lack of routable components using this simple hack:
Let's say you're creating a Route called Dashboard. The tutorial will tell you to create corresponding Controller and Template as well. Go ahead and do that, but delete the Controller. Create a component called dashboard-main, move the logic from the Controller to the component.js file and the Template to the component's Template. Then, in the Dashboard Template, just refer to the component:
{{dashboard-main items=model foo=foo bar=bar ...}}
Depending on what you're doing in the Route, you still may need the setupController() method (that's still the only way you can move values other than the model from the route to the template so that they can be passed to the component), and of course your controller/component implementation may have other minor changes, but that's the basic gist of it.
To be most ready for when controllers are deprecated, you should avoid them by using components instead.
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.
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 seem to be getting more and more confounded at what appears, on the surface at least to be pretty basic architectural questions regarding building ember apps.
Which Controller type?
In the last month or so, I've seen people implement controllers through Ember.Controller, Ember.ArrayController, Ember.ObjectController, and Ember.ArrayProxy. Removing ArrayController and ArrayProxy (due to them being identical), what are common use cases between each type?
So far, i've been able to gather that:
ArrayControllers/Proxies should be used when you have n elements within the view you intend to control
ObjectControllers should be used when the view is simple enough to maintain it's state in a single object, or be a single instance of a model's object.
Controllers --- ? No idea.
What are some basic differences between the controller types? There doesn't seem to be concrete information on when to use which, and for which use case. The API docs are good at telling me the nitty gritty of each of them, but not WHEN to use each.
The relationship between a View and a Controller can be baffling
When a View is connected via a routes ConnectOutlets function call, what exactly happens between the controller and the view?
Are events tied into the view itself (which seems to be the case) and if so, where on earth do you interact with the controller singleton to perform CRUD-esq things on its properties? this.get('controllerName') doesn't seem to do the trick, and nearly each post or tutorial or code sample out there does this a different way.
Models that aren't
I realize that Ember Data looks to help solve some of the more irritating parts of dealing with data and keeping it in sync, but at a larger perspective, in the concept of "MVC", ember doesn't really seem to have a Model of any kind. It's just some object that gets subclassed from a specific thing and then tracked....somewhere? Somehow? Magical?
#trek sufficed that an Ember.Object could manage ajax'ing data and handling state on the client just fine, but if you look at something like the todomvc.com ember app, it uses a localStorage paradigm that is COMPLETELY different in implementation then everything i've looked at.
How EXACTLY should the 'Model' part of the MVC equation be done here?
Views make me want to murder children
There seems to be a significant number of ways to construct a "view" in terms of displaying markup to a user.
ContainerViews, using subviews / childviews
nested outlets
Handlebars templates + an outlet
using #each foo in controller
Injection through literals (template: Ember.Handlebars.compile('<h1>foo</h1>') etc)
With that in mind, what's the 'proper' way to build modular UI components with ember? This more than anything is a major pain point for the adoption of this framework for me.
I love the direction that Ember is going with application development on the web. The concepts seem simple, but the verbosity is that of Objective-C (which makes sense given it's lineage) but I swear to god I feel like i'm fighting the god damned framework more than i'm actually working on my application. The verbosity of the syntax and the lack of structured documentation outside of API documentation (which lets face it, 300k of javascript is a significant amount of code to throw some breakpoints down and try to debug your issues).
I realize the challenge that you guys are up against, but hopefully this at least makes you pause for a minute and think of how you could make life easier for the incoming developer who's worked with other frameworks (or hell, even worked within an MVC framework, like rails or django or backbone or angular) and say "this is how we think ember should be used".
Take some of the opinionated software design decisions and apply them toward the community. We'll do nothing but be cheerleaders for you if you do it, promise.
Please don't hurt any children. AFAIK the ember-core team are all over 18, so any ember-view-related frustration is clearly better directed towards adults. With that in mind...
Which Controller Type?
You've got the "what" right, but maybe missing the "why". Controller can be a little misleading, especially coming from rails. Think of these controller singletons as representing the state (in-memory) of your application.
Which kind of controller to use depends on what is required for that part of your application. For example, a back-of-napkin sketch for any app might have a topnav, postList, postDetails section. In ember, each is represented by one or more view/controller pairs. In this app I would expect to see ApplicationController and NavigationController extending Ember.Controler while postList would extend ArrayController and PostDetails would be an ObjectController.
You could do it all using just Ember.Controller but ObjectController and ArrayController are really useful for wrapping model data. Any non-trivial ember app will probably use all three.
The relationship between a View and a Controller
A controller's job is to provide the context in which the view will be rendered. Ideally you'd like to keep logic out of views, so a typical controller will have lots of computed properties to do things like:
transform data from the underlying model objects
sort/filter/select a list of objects
reflect application state
whats the deal with connectOutlets? This is where you should be using the requested route/context to decide which views/data should be plugged into the outlets of your application. The controller's connectOutlet method has a bunch of magic to make it easy, but maybe too much magic. What happens (afaik) when you call: parentcontroller.connectOutlet 'child' is
Ember creates an instance of ChildView
The {{outlet}} handlebars helper in parentController's view is bound to this childView instance
The childView is rendered with the router.childController singleton as it's context
where to do crud stuff?: Typically in an action on the router. This seems crazy at first. Think of ember router not like rails but as a stateManager that just happens to also handle routing. In near-future router API will change to make this more clear. Anyway, use router actions to do things like create model instances, commit/rollback transactions and trigger state change. This is easy to do if you use the handlebars {{action}} helper for links/buttons as it targets the router by default.
Views on the other hand should have logic for "reacting to browser events" - that means really low-level stuff like show/hide something on mouseover or integrate with 3rd party libraries to do effects and animations.
You might find this screencast helpful in understanding how to do CRUD-esq things:
http://blog.bigbinary.com/2012/09/06/crud-application-in-emberjs.html
Models WTF?
Agreed in Ember any object could be used as a 'Model'. I think #trek does a good job of demonstrating how one might accomplish this via Ember.Object. This works great for a simple app, and six months back maybe would've been your best bet as ember-data was really immature. I'm not clear on the history of ember's todomvc app, but for sure it was written months ago. For sure it should be updated, but meantime I'd not recommend using it to learn about current ember best-practices.
Instead, you should go with ember-data. Over the last few months it has really evolved and should be the default choice for any new, non-trivial ember app. #tomdale just gave a great presentation on this topic, I'd recommend having a look: https://speakerdeck.com/tomdale/ember-data-internals
what's the 'proper' way to build modular UI components with ember?
For building modular UI components:
ContainerViews, using subviews / childviews
Injection through literals (template: Ember.Handlebars.compile ...)
For building an individual application:
nested outlets
Handlebars templates + an outlet
using #each foo in controller
Building modular UI components is a totally different problem than building an application. Ember.View and it's subclasses were designed for this purpose. You can easily extend/combine them to compose widgets with custom behaviors and share those widgets across applications.
At least that's how i've seen it done. If they are for internal use could also reference handlebars templates instead of object literals, but if planning to distribute the object literals approach seems best.
A great real-world example of this is the ember-bootstrap project. I learned a lot about working with ember-views by reading through that project's source. http://emberjs-addons.github.com/ember-bootstrap/
TLDR
Pick controller that maps to type of data being represented
Controllers provide context for the view and remember application state
Use ember data for your models
Use subclasses of Ember.View to make components
Be nice to children