Currently I'm passing a custom keyword into the component like so (as I loop over each model in my array controller)
{{#each thing in controller}}
{{my-thing foo=controller}}
{{/each}}
Then inside my component I can add a custom attributeBindings and bind to "foo" but I'd like to think I can get access to the parent controller (from inside the component itself)
How else can I get this from inside the component in ember 1.8+ ?
Check out targetObject:
If the component is currently inserted into the DOM of a parent view, this property will point to the controller of the parent view.
In the Component, you can use this.get('targetObject'); to get the Controller of the parent view.
Related
sendAction() in an Ember.Component bubbles to controller by default which is expected. But i have 2,3 actions which i rather need to send to corresponding view which is using the component. In templates we set action to view using target=view. Can we do that?.
Update: Currently as a work around I am sending my view object to component which from there calls view.send() to send the action. But i feel this is not correct.
Ok after some thinking I believe i know that you mean. If you have a component and you have a action it will be handled by the component itself. If you want to send a action outside the component you would use sendAction.
Now to target the view of which holds your component since your component is base on a view, you can probably do this.get('parentView') to get the parent view and then chain send('nameOfAction')
So it would be this.get('parentView').send('nameOfAction') from within the component action and it will then trigger the action on the parent view of which the component is embedded.
So in your component you could have:
App.DemoCompComponent = Ember.Component.extend({
actions: {
internalTrigger: function() {
//just an alert to make sure it fired
alert('Internal action was caught on component');
//this should target parent view component is in
this.get('parentView').send('viewTriggerTest');
}
}
});
Now lets say you have you component in the index template you could:
The template would be:
<script type="text/x-handlebars" data-template-name="index">
<h2>Inside Index Template</h2>
{{demo-comp}}
</script>
The Index View Code would be:
App.IndexView = Ember.View.extend({
actions: {
viewTriggerTest: function() {
alert('View Trigger Test Worked on Index!');
}
}
});
Here is a jsbin
http://emberjs.jsbin.com/reyexuko/1/edit
For latest Ember 2.9 the recommended approach is to pass a closure action to the child component. The property target and parentView are private ones.
I've created a table in my app by iterating through a list of rows like this:
{{#each visible_page itemController='ScheduledReport'}}
.....
{{/each}}
Now, I'm trying to abstract this table so that I can use it for more than just these reports. So, I want to define the itemController in the ArrayController rather than inlining it in the template:
App.ReportsScheduledController = App.TableController.extend(
{
itemController : 'scheduledReport'
However, defining the itemController in the ReportsScheduledController -- which inherits from Ember.ArrayController -- doesn't render each row in the table.
Any idea what I'm doing wrong in this setup?
Thanks!
You need to iterate over the controller in the template for that to work. Iterating over properties in the controller doesn't apply the wrapping of the item controller.
I've got a fairly simple form but it should never carry any state with it. I started reading an older discussion about how you can use itemControllerClass to get a "singleton" class created each time you enter the route.
If I want to use this how would I plug this into the template and wire it up from the parent controller?
Here is what I'm guessing you would do from the javascript side
App.FooController = Ember.Controller.extend({
itemControllerClass: 'someclass_name'
});
The only "must have" is that I need to have access to the parent route params from the child singleton controller.
Any guidance would be excellent -thank you in advance!
Update
Just to be clear about my use case -this is not an ArrayController. I actually just have a Controller (as shown above). I don't need to proxy a model or Array of models. I'm looking for a way to point at a url (with a few params) and generate a new instance when the route is loaded (or the parent controller is shown).
I'm doing this because the template is a simple "blank form" that doesn't and shouldn't carry state with it. when the user saves the form and I transition to the index route everything that happened in that form can die with it (as I've cached the data in ember-data or finished my $.ajax / etc)
Anyone know how to accomplish this stateless behavior with a controller?
I'm betting you're talking about this discussion. It's one of my personal favorite discoveries related to Ember. The outcome of it was the itemController property of an ArrayController; I use it all the time. The basic gist of it is, when you're iterating over an array controller, you can change the backing controller within the loop. So, each iterating of the loop, it will provide a new controller of the type you specify. You can specify the itemController as either a property on the ArrayController, or as an option on the {{#each}} handlebars helper. So, you could do it like this:
App.FooController = Ember.ArrayController.extend({
itemController: 'someclass'
});
App.SomeclassController = Ember.ObjectController.extend({});
Or, like this:
{{#each something in controller itemController='someclass'}}
...
{{/each}}
Within the itemController, you can access the parent controller (FooController, in this case), like:
this.get('parentController');
Or, you can specify the dependency using needs, like you ordinarily would in a controller. So, as long as the params are available to the parentController, you should be able to access them on the child controller as well.
Update
After hearing more about the use case, where a controller's state needs to reset every time a transition happens to a particular route, It sounds like the right approach is to have a backing model for the controller. Then, you can create a new instance of the model on one of the route's hooks; likely either model or setupController.
From http://emberjs.com/api/classes/Ember.ArrayController.html
Sometimes you want to display computed properties within the body of an #each helper that depend on the underlying items in content, but are not present on those items. To do this, set itemController to the name of a controller (probably an ObjectController) that will wrap each individual item.
For example:
{{#each post in controller}}
<li>{{title}} ({{titleLength}} characters)</li>
{{/each}}
App.PostsController = Ember.ArrayController.extend({
itemController: 'post'
});
App.PostController = Ember.ObjectController.extend({
// the `title` property will be proxied to the underlying post.
titleLength: function() {
return this.get('title').length;
}.property('title')
});
In some cases it is helpful to return a different itemController depending on the particular item. Subclasses can do this by overriding lookupItemController.
For example:
App.MyArrayController = Ember.ArrayController.extend({
lookupItemController: function( object ) {
if (object.get('isSpecial')) {
return "special"; // use App.SpecialController
} else {
return "regular"; // use App.RegularController
}
}
});
The itemController instances will have a parentController property set to either the the parentController property of the ArrayController or to the ArrayController instance itself.
I have a graphHolderController that renders this template:
{{#each controller.graphs}}
{{view sidePanelView}}
{{view graph}}
{{/each}}
In the SidePanelView, I want to call a method on SidePanelController, but I find that in SidePanelView, this.get('controller') returns an instance of the graphHolderController. How can I call a method on the SidePanelController from the SidePanelView?
The problem is that {{view}} helper does render the view you passed as first parameter, but binds the same controller as in the parent view.
Move your method to graphHolderController and send the model as parameter for that action if you need to know which graph the action comes from.
If you want a view to be managed by its own separate controller, I'd recommend using the render helper: {{render sidePanel}}. This will render the side_panel template, backed by the sidePanelView and a singleton sidePanelController.
If the sidePanelController needs to manage state for a particular model, pass that model in as a context: {{render sidePanel this}} (where this represents a model). This will generate a new non-singleton sidePanelController to manage just this model. Since you're rendering within an {{each}} block, I'd guess you want to take this approach.
My model "content.id" contains a string, e,g "123":
{{view Em.TextArea idBinding="content.id"}}
Instead of just setting the id of this view to "123", I'd like it to be "message-123", basically customizing the string being used. Sadly, Ember does not allow bindings to be functions, which would solve my problem (I could define such a function on the controller).
What's the best way to achieve this?
You could define a computed property in the controller (or elsewhere):
The controller
MyApp.ApplicationController = Ember.Controller.extend({
content: "a-content",
editedContent: function() {
return "message-" + this.get('content');
}.property('content')
});
The view
MyApp.FooView = Ember.View.extend({
tagName: 'p'
});
The template (where content is a String, here)
{{#view MyApp.FooView elementIdBinding="editedContent"}}
{{content}}
{{/view}}
And the JSFiddle is here.
EDIT
How can the view see the property editedContent since it belongs on the ApplicationController controller?
The router, after started, automatically render the ApplicationView, or its template when there is no ApplicationView defined. If you want more detail, I suggest you to read the Ember guide: Understanding the Ember.js Router: A Primer.
And {{editedContent}} directly get the controller editedContent property, because the default view context is its controller, as you can read in Ember Blog - 1.0 Prerelease:
The {{#view}} helper no longer changes the context, instead maintaining the parent context by default. Alternatively, we will use the controller property if provided. You may also choose to directly override the context property. The order is as follows:
Specified controller
Supplied context (usually by Handlebars)
parentView's context (for a child of a ContainerView)