Currently i am trying make use of a CollectionView in my router and ConnectOutlets. I am fine, if i am using the collection view helper but this is in contrast to my other implementations, in which i am always leveraging connectOutlets.
What i am basically trying is:
connectOutlets : function(router){
console.log("calling connectOutlets");
router.get("applicationController").connectOutlet({
viewClass : App.ItemsView,
controller : App.itemController,
context : content
})
}
App.ItemsView = Ember.CollectionView.extend({
itemViewClass : App.ItemView,
});
App.ItemsView is my Subclass of CollectionView. App.itemController is an ArrayController i instantiated manually. You can see the full fiddle here: http://jsfiddle.net/mavilein/qS3aN/12/
But actually this does not work. I am not seeing the items getting rendered. With the collection helper it works fine, but setting the binding in the view is too static for me.
Is the CollectionView not intended for use with connectOutlets?
It seems like you can do that, but two little tweaks need to be done in the ItemsView:
App.ItemsView = Ember.CollectionView.extend({
contentBinding: 'controller',
itemViewClass : 'App.ItemView',
});
Since a CollectionView rely on its content property, you have to bind it to its controller property. (this property is wired at connectOutlet time)
As you define App.ItemView after ItemsView, you need to refer to it as a string, in order to let Ember.js lookup it during the ItemsView instance creation.
fiddle: http://jsfiddle.net/qS3aN/29/
Related
Every google result is about an ArrayController sorting. Need a sorting mechanism without using ArrayController.
There is a model where there are sort params. Like say 'sortOrder' as one of the properties in the model (which will be from a back end).
Will be rendering this model using #each but this should do the iteration based on the sortOrder property and not the model's ID property.
In Ember 2.0 SortableMixin is deprecated and is on its way out too.
In the Controller (not the ArrayController) you may define a new computed property like SortedUsers1,2,3 below:
export default Ember.Controller.extend({
sortProps: ['lastName'],
sortedUsers1: Ember.computed.sort('model', 'sortProps'),
sortedUsers2: Ember.computed.sort('content', 'sortProps'),
sortedUsers3: Ember.computed('content', function(){
return this.get('content').sortBy('lastName');
})
});
The assumption above is that the model itself is an array of users with lastName as one of user properties. Dependency on 'model' and 'content' look equivalent to me. All three computed properties above produce the same sorted list.
Note that you cannot replace 'sortProps' argument with 'lastName' in sortedUsers1,2 - it won't work.
To change sorting order modify sortProps to
sortProps: ['lastName:desc']
Also if your template is in users/index folder then your controller must be there as well. The controller in users/ would not do, even if the route loading model is in users/.
In the template the usage is as expected:
<ul>
{{#each sortedUsers1 as |user|}}
<li>{{user.lastName}}</li>
{{/each}}
</ul>
Here is how I manually sort (using ember compare)
import Ember from "ember";
import { attr, Model } from "ember-cli-simple-store/model";
var compare = Ember.compare, get = Ember.get;
var Foo = Model.extend({
orderedThings: function() {
var things = this.get("things");
return things.toArray().sort(function(a, b) {
return compare(get(a, "something"), get(b, "something"));
});
}.property("things.#each.something")
});
You just need to include a SortableMixin to either controller or component and then specify the sortAscending and sortProperties property.
Em.Controller.extend(Em.SortableMixin, {
sortAscending: true,
sortProperties: ['val']
});
Here is a working demo.
In situations like that, I use Ember.ArrayProxy with a Ember.SortableMixin directly.
An ArrayProxy wraps any other object that implements Ember.Array
and/or Ember.MutableArray, forwarding all requests. This makes it very
useful for a number of binding use cases or other cases where being
able to swap out the underlying array is useful.
So for example, I may have a controller property as such:
sortedItems: function(){
var items = Ember.ArrayProxy.extend(Ember.SortableMixin).create({content: this.get('someCollection')});
items.set('sortProperties', ['propNameToSortOn']);
return items;
}.property()
Like so: JSBin
Uncaught Error: Assertion Failed: `<(subclass of Ember.ObjectController):ember947> specifies `needs`, but does not have a container. Please ensure this controller was instantiated with a container.
If for some reason a controller doesn't have a container, how can I provide it with one? Context is below, but that is essentially the question being asked.
The context is that there apparently is not a straightforward way of providing controllers for the individual items in Ember.CollectionView, a problem which is outlined at ember.js/issues/4137.
It seems the only way to get item controllers is to declare the them inline in the init method for an inline itemViewClass declaration of the CollectionView (as confirmed by the originator of that ticket):
var someCollectionView = Ember.CollectionView.extend({
itemViewClass: Ember.ListItemView.extend({
templateName: "foo-item",
init: function(){
var content = this.get('content');
var controller = Ember.ObjectController.extend({
// controller for individual items in the collection
actions: {
// actions specific to those items
}
}
}).create({
content: content,
});
this.set('controller', controller);
this._super();
}
})
});
So this works, however if you add a "needs" property to this controller, it gives the error about no container. These item controllers will be observing a property on an external controller, so I need the "needs". So how do I instantiate the controller with the container... or hack it in after instantiation?
Accessing App.__container__ is generally adviced against. All core objects like views, controllers, routes, should have been instantiated by the container. In that case they will also have a container property (plain JS property, not an Ember property), that you can use to instantiate other objects, which in turn will have access to the container.
So instead of
Ember.ObjectController.create(...)
try
this.container.lookupFactory('controller:object').create(...)
If container is undefined you'll have to go up the chain, and make sure whatever object you're calling this from is also coming out of the container.
It looks like you can do
...
}).create({
content: content,
container: App.__container__
});
this.set('controller', controller);
this._super();
}
})
});
The ember way:
According to ember's documentation about views' eventManagers, they must be created in the parent classes definition like so:
AView = Ember.View.extend({
eventManager: Ember.Object.create({
which encapsulates and isolates them from their parent view (AView).
The only way of accessing the context of events is through the view parameter that gets passed in along with each event
dragEnter: function(event, view) {
My situation:
I'm doing a lot of work with the various drag events inside a large view with many subviews, inputs, checkboxes, etc.
Following this form, my code is beginning to go to great lengths to determine which sub-view each event originated from, and then taking different paths to access the common parent controller:
drop: function(event, view) {
var myController;
if(view.$().hasClass('is-selected') ||
view.$().hasClass('list-map-container')) {
myController = view.get('controller.controllers.myController');
} else if(view.$().hasClass('ember-text-field')) {
myController = view.get('parentView.parentView.controller');
} else {
myController = view.get('controller');
}
// do work with myController
}
My hack:
In order to simplify I used the didInsertElement hook in the parent view to assign the desired controller as a property on the eventManager:
App.MyView = Ember.View.extend({
didInsertElement: function() {
this.set('eventManager.controller', this.get('controller'));
},
eventManager: Ember.Object.create({
controller: null,
// ...
This works to significantly simplify my event handlers:
drop: function(event, view) {
var myController = this.get('controller');
// do work with myController
My question:
My intuition tells me this hack-around isn't the best solution.
Perhaps I shouldn't be doing all the work in the eventManager? Rather move all this work to a controller and just forward the events from the view?
But if the eventManager is an acceptable workspace, then what is the best way to access the parent view's controller?
I know this is a late answer but this SO question appears as a result of google. Here is how I did this when searching through emberjs examples.
To access the view within the eventManager, you have to specify two argument in the event function handler :
eventManager: Ember.Object.create({
keyUp: function(event, view){
view = view.get('parentView'); // The view parameter might not be the current view but the emberjs internal input view.
view.get('controller'); // <-- controller
}
}),
Correct me if I'm wrong, but it looks like all the controller logic is encapsulated to a text-field--if so, I think a component might better suited for this use case. It's essentially a controller and view as one, and the eventManager's callbacks' view parameter gives you control over the component/controller itself.
If you need access to the component's parent controller, you might want to bind to events on the component from the parent controller, because the component really shouldn't know about anything outside its scope.
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
So, I am trying to get a simple propertyBinding to work with emberjs. Specifically, I have a controller with a content property, that gets updated under certain circumstances and a view, which needs that content array to draw some chart.
I have made the most basic example and it doesn't seem to work. My simple example is the following:
Appname.IndexController = Ember.Controller.extend({
value: 'bla'
});
Appname.IndexView = Ember.View.extend({
templateName: 'Index',
propertyBinding: 'Appname.IndexController.value',
didInsertElement: function() {
console.log('Indexview');
console.log(this.get('property'));
}
});
It is as simple as that, and it just does not work. What is really odd though, if I create another testcontroller (rather then extending it) e.g.
Appname.TestController = Ember.Controller.create({
value: 'jpopo'
});
the property binding works all of the sudden. But I just can not get it to work with the IndexController
(And in case the information is necessary, in the Applicaton.hbs I have an outlet)
Thanks for any help
Bindings work for instantiated objects, not for object definitions.
Appname.IndexController is the controller definition, not an instance. It is not what you want to bind to. The Ember.js app will create an instance of IndexController, and it's that created instance that you want to bind to:
To access the actual controller instance from its view, use controller.
Appname.IndexView = Ember.View.extend({
templateName: 'index',
propertyBinding: 'controller.value',
didInsertElement: function() {
console.log(this.get('property'));
}
});
Of course, that is if you follow Ember.js conventions.