I am trying to observe another childcontroller.
I have the following router:
this.resource('generics', {path: '/gens'}, function() {
this.resource('generic', {path: '/:generic_id'}, function() {
this.route('prepare'); // Objectcontroller
this.route('sent'); // Objectcontroller
});
});
I have an observer in the sent controller, however it does not work. I have currently: 'controllers.sent.id' to get the id prepared in the prepare controller.
If I do a needs property with generic.prepare. It shows this error:
#needs must not specify dependencies with periods in their names (generic.sent)
I have also tried to use setupController to add the id to the sent controller properties, however the observer is worthless then.
it would be genericPrepare, but prepare/sent should never exist at the same time. You'd probably be better off sending the object to generic and then having sent grab the property off of it. Why are you against setting it up during setupController? The route will always hit setupController of sent before it's visible to the end user.
Related
In a simple, standard ember.js route ...
this.resource('foo', function () {
this.route('show');
this.route('new');
});
I have a foo template that shows a new button (link to foo/new route) and the foo.bar data of the foo objects (with links to foo/show in a sorted way using the foo controller)
import Ember from 'ember';
export default Ember.Controller.extend({
sortProperties: [ 'bar:desc' ],
sortedList: Ember.computed.sort('model', 'sortProperties')
});
The routes foo/show and foo/new do the obvious, nothing special.
I don't need or want a "real" foo/index route, since all my navigation needs are catered for in the foo route and template. Therefore I want to transitionTo from foo/index whenever I enter that route to the first (as in given by the sorting above) foo/show route (or foo\new if there is nothing to show).
I've learned that "the ember way" is to transitionTo out of the route, but never ever out of the controller.
Now, my problem is, that I get access to the unsorted data very easily (it's the model of my parent route, that I can access via modelFor) and I can sort this "manually", but I'd rather use the already sorted data from my parent's controller. Now, I'm at a bit of a loss which hook to use in the route to be sure that my parent route's controller's computed properties are ready - and it doesn't feel right. So, what would be the ember way to do this?
Edit: Non-working example at http://emberjs.jsbin.com/hujoxigami/2/edit?html,js,output ...
Use the afterModel hook as you mention in one of your comments.
App.FooIndexRoute = Ember.Route.extend({
afterModel: function(posts, transition) {
if (posts.get('length') > 0) {
this.transitionTo('foo.show', posts.get('firstObject'));
}
}
And there is nothing wrong with sorting your model before you return it from the FooRoute. (Even better if you can receive it already sorted from your API/data storage)
App.FooRoute = Ember.Route.extend({
model: function() {
// not fully functional, just illustrative
return [{id:1, name:"Hans", detail:"german"},
{id:3, name:"Henri", detail:"french"},
{id:2, name:"Harry", detail:"english"}].sortBy('name');
}
});
The sorting properties in the controller remain relevant if you want to provide sorting controls for the user.
I have the following router:
Router.map(function() {
this.resource('cart', function() {
// order routes
this.route('shipping');
this.route('checkout');
this.route('payment');
this.route('thanks');
});
});
On route Cart I have a cartTotal computed property. I want to have it update when a variable in route shipping changes.
My computed property in the cart controller:
needs: ['cartShipping'],
cartitemsTotal: function() {
// logic
return totalCosts;
}.property('model.#each.price', 'controllers.cartShipping.shippingMethod'),
However this gives an error: Error while processing route: cart <app#controller:cart::ember404> needs [ controller:cartShipping ] but it could not be found Error: <app#controller:cart::ember404> needs [ controller:cartShipping ] but it could not be found
Any idea what is going wrong here?
You can't use the needs property on child controllers, only parent controllers. You want to use the cart.shipping controller, but what if your user is at the cart.checkout route? In that case, the cart.shipping controller won't be active (and might not even exist if the container hasn't instantiated it yet).
For almost everything in Ember.js, you can reach up the hierarchy but you can almost never reach down. This is one of those cases. You probably need to restructure your routes to do what you want.
I hope this is not stating the obvious, have you defined "cartShipping"?
I have found that the controllers need to be define in order to pull properties such as shippingMethod
App.CartShippingController = Ember.Controller.extend({
shippingMethod: 'method-here'
});
I have an EmailsController (ArrayController), which stores all the emails. I have an EmailController (ObjectController) that has a parameter that stores if the actual Email is selected or not. I am trying to implement a button in the emails template, that selects or deselects all the Emails. So somehow I need to notify the EmailController via an action of the EmailsController and change the EmailController's isChecked parameter.
I am trying to use the itemController, the needs, and the controllerBinding parameters, but nothing works.
Here are the controllers:
App.EmailsController = Ember.ArrayController.extend({
needs: ["Email"],
itemController: 'Email',
checkAll: true,
actions: {
checkAllEmails: function() {
this.toggleProperty("checkAll");
console.log(this.get("checkAll"));
}
}
});
App.EmailController = Ember.ObjectController.extend({
needs: ["Emails"],
controllerBinding: 'controllers.Emails',
isChecked: true,
checkAllChanged: function() {
//this should execute, but currently it does not
this.set("isChecked",this.get('controller.checkAll'));
}.property("controller")
});
Here is the corresponding jsFiddle: http://jsfiddle.net/JqZK2/4/
The goal would be to toggle the selection of the checkboxes via the Check All button.
Thanks!
Your mixing a few different mechanisms and your using a few wrong conventions. It's not always easy to find this stuff though, so don't fret.
Referencing Controllers
Even though controllers are created with an Uppercase format, the are stored in the lowercase format and your needs property should be:
needs: ['emails'],
You then access other controllers through the controllers property:
this.get('controllers.emails.checkAll')
Computed Properties
Computed properties can be used as a getter/setter for a variable and also as a way to alias other properties. For example, if you wanted the isChecked property on the Email controller to be directly linked to the value of the checkAll property of the Emails controller, you could do this:
isChecked: function() {
return this.get('controllers.emails.checkAll');
}.property('controllers.emails.checkAll')
Although computed properties can do much more, this basic form is really just a computed alias, and there is a utility function to make it easier:
isChecked: Ember.computed.alias('controllers.emails.checkAll')
Observables
An observable basically creates a method that will be called when the value it observes changes. A computed alias would cause all items to uncheck or check whenever you clicked on any one of them, since their isChecked property is linked directly to the checkAll property of the parent controller. Instead of your checkAllChanged method identifying as a property it should use observes:
checkAllChanged: function() {
this.set("isChecked",this.get('controllers.emails.checkAll'));
}.observes("controllers.emails.checkAll")
This way when the checkAll property changes on the parent controller, this method updates the isChecked properties of all items to its value, but if you uncheck or check an individual item, it doesn't affect the other items.
Bindings
Bindings are somewhat deprecated; from reading issues on the Ember github repository I believe the creators of Ember seem to favor using computed properties, aliases, and observables instead. That is not to say they don't work and if your goal was to avoid having to type out controllers.emails every time, you could create one like you did (I wouldn't call it controller though, cause thats really quite ambiguous):
emailsBinding: 'controllers.emails'
Using a computed alias instead:
emails: Ember.computed.alias('controllers.emails')
You could then change your observer to:
checkAllChanged: function() {
this.set("isChecked",this.get('emails.checkAll'));
}.observes("emails.checkAll")
Heres an updated version of your jsFiddle: http://jsfiddle.net/tMuQn/
You could just iterate through the emails, changing their properties from the parent controller. You don't need to specify needs or observe a variable.
App.EmailsController = Ember.ArrayController.extend({
itemController: 'email',
actions: {
checkAllEmails: function() {
this.forEach(function(email) {
email.toggleProperty("isChecked");
});
}
}
});
Also, you typically don't set initial values like you did with isChecked = true; I believe that's creating a static shared property on the prototype (not what you intended). Instead, set the property on init, or pass it in from your original json data.
See the code: http://jsfiddle.net/JqZK2/5/
I have a controller in Ember like so:
App.TasksController = Ember.ArrayController.extend({
search: function(term){ ... }
})
And I have the relative view, with a custom text field, as such:
App.TasksView = Ember.View.extend({
searchField: Ember.TextField.extend({
keyUp: function(){ this.get('controller').search() }
})
})
I however get an error saying that there is no such method.
I was wondering:
How can I correctly call the method defined in the controller from the view?
How can I debug which is the currently active controller? If I console.log(this.get('controller')) I get an object, but it is very confusing to navigate and to understand exactly which controller is that.
the scope of this on the text field isn't the same scope as the tasksview, so it doesn't have access to the controller.
Honestly a better way to handle this is to bind the textfield value to something and add an observer to it. When it changes fire off a search (or probably better would be to debounce it so you aren't firing off requests every single character they type)
http://emberjs.jsbin.com/IRAXinoP/3/edit
I'm trying to use the 'needs' feature to allow one controller to obtain a value from another. Here's a JSFiddle that shows a stripped-down version of my app before binding a value: http://jsfiddle.net/kevinwh/WRxnE/4/
App.ApplicationController = Ember.ObjectController.extend({
init: function() {
this._super();
},
dishClicked: function() {
console.log('clicked');
this.incrementProperty('clickCount');
}
});
App.DishController = Ember.ObjectController.extend({
needs: ['application'],
init: function() {
this._super();
},
//clickCountBinding: 'controllers.application.clickCount'
});
Basically, my ApplicationController has a clickCount property that is updated (by an action) whenever one of the Dish links is clicked. Clicking on a link also activates the DishRoute via linkTo.
Now I'd like the contained DishController to also have access to ApplicationController's clickCount. So I add the 'needs' property and a clickCountBinding property (which will have to be uncommented in the JSFiddle). Then, when I click on a link I get a complaint:
assertion failed: Cannot delegate set('clickCount', 0) to the 'content' property of object proxy : its 'content' is undefined.
Apparently the binding is being activated before the model content is set on the controller. Since the controller is being set up by the linkTo, my DishRoute.model() and DishRoute.setupController() methods are not invoked. Also, the DishController.init() method isn't even called before the binding error happens.
I considered the possibility that I should just stick a content member object into the class (commented out in the JSFiddle), but doing that gives a bizarre result: the click count is incremented separately for the different links. Interesting, but not what I'm after.
So - how do I share the clickCount value across these controllers? Is there some other way to set up the content in the DishController so that the binding will work?
You've just slightly misunderstood the error message.
The issue is that you've subclassed the ApplicationController from ObjectController even though it doesn't have an underlying content object to proxy to, you should just user Ember.Controller in this case.
That being said, if you have a counter you should probably default it to zero anyway.
App.ApplicationController = Ember.Controller.extend({
clickCount: 0,
dishClicked: function() {
console.log('clicked');
this.incrementProperty('clickCount');
}
});
App.DishController = Ember.ObjectController.extend({
needs: ['application'],
clickCountBinding: 'controllers.application.clickCount'
});