emberjs arraycontroller - ember.js

I'm using the latest 4th pre release of ember. In my application I have some sections that are not connected to Router, but I would like to keep all application in one style and use ArrayController and Em.CollectionView for them.
I tried to make something like this:
var controller = Em.ArrayController.create({content: Em.A()});
Em.CollectionView.create({
controller: controller
});
controller.pushObject(Em.Object.create({
title: 'test'
}))
and then I got an error that "controller" does not have a container property.
Is it possible to use ArrayController without Em.Router?

Yes it is possible. I was not able to reproduce the error you specified, but did have to make a few changes to get things working.
var controller = Em.ArrayController.create({content: Em.A()});
controller.pushObject(Em.Object.create({title: 'dr plimpton'}));
controller.pushObject(Em.Object.create({title: 'raj'}));
controller.pushObject(Em.Object.create({title: 'howard'}));
controller.pushObject(Em.Object.create({title: 'leonard'}));
var myView = Ember.CollectionView.create({
tagName: 'ul',
content: controller,
itemViewClass: Ember.View.extend({
template: Ember.Handlebars.compile("{{view.content.title}}")
})
});
myView.appendTo('body');
Working example (based on ember-1.0.0-pre.4) here: http://jsbin.com/eticuw/1/edit

Related

Ember component cannot use access controller property via "needs"

I'm trying to change a controller's property from a component as follows(JSBIN example http://jsbin.com/gevuhu):
App.CategoryManagerController = Ember.Controller.extend({
selectedCategory: null,
});
App.BlogPostComponent = Ember.Component.extend({
needs: ['categoryManager'],
selectedCategory: Ember.computed.alias('controllers.categoryManager.selectedCategory'),
actions:{
selectedCategory: function (){
this.set('selectedCategory',1);
}
}
});
but getting the error Property set failed: object in path "controllers.categoryManager" could not be found or was destroyed.
Is it that we cannot use "needs" in components ?
Ember Components are completely isolated from surrounding context including controllers (see here). That's the bad news. The good news is that if you pass selectedCategory into the component, it will become 2-way bound, so any change to it in the component will be known by your controller.
So, your controller could be something like:
App.ApplicationController = Ember.ObjectController.extend({
needs: ['categoryManager'],
selectedCategory: Ember.computed.alias('controllers.categoryManager.selectedCategory'),
selectedCategoryChanged: function(){
alert("NEW CATEGORY: " + this.get('selectedCategory'));
}.observes('selectedCategory')
});
and then in your application template, you can say
{{ blog-post selectedCategory=selectedCategory }}
See a working example here
In later version like 2.2. We'll be writing this as:
App.ApplicationController = Ember.Controller.extend({
categoryManager: Ember.inject.controller("categoryManager")
});
and now, categoryManager will now have the controller named categoryManager.

How to get model properties

I have model:
App.Item = DS.Model.extend({
itemId: DS.attr('string'),
itemName: DS.attr('string'),
itemType: DS.attr('string'),
});
I successfully create some items from JSON. I can put them to page by {{#each items}}{{ itemName}}{{/each}}. But I don't know, how to get itemName in javascript.
I tried this:
var item = App.Item.find(1);
console.log(item.itemName);
--> undefined
I can't find anything useful from emberjs and ember-data docs. Can anyone help me?
Thanks
I tried this:
var item = App.Item.find(1);
console.log(item.itemName);
--> undefined
This is normal because the call to .find(1); is asyncronous and returns a promise and not the item you are expecting.
Therefore you should try:
App.Item.find(1).then(function(result) {
console.log(record.get('itemName'));
});
It also depends from where you are doing App.Item.find() if it's from inside a route you should wait until the afterModel hook is called to access your items:
App.FooRoute = Ember.Route.extend({
model: function() {
return App.Item.find(1);
},
afterModel: function(record) {
console.log(record.get('itemName'));
}
});
Also be aware that if you where calling find() without parameter then you will receive a RecordArray which you need to loop over to get access to your items. Also worth mentioning is that in ember you should always use .get() and .set() instead of the vanilla dot-notation otherwise you hijack the binding mecanism resulting in no updates in your view etc.
Note, if you are using the latest ember.js release (1.0.0) then the call to .find() should be made somewhat different. But that's not clear from your question.
Hope it helps.

Ember todos: an Ember.CollectionView's content must implement Ember.Array

I'm trying to get my head around Ember and going through the todos tutorial. I get stuck on the displaying-model-data step here
http://emberjs.com/guides/getting-started/displaying-model-data/
here's the javascript i copied and pasted from the tutorial:
window.Todos = Ember.Application.create();
Todos.Router.map(function () {
this.resource('todos', { path: '/' });
});
Todos.TodosRoute = Ember.Route.extend({
model: function () {
return Todos.Todo.find();
}
});
Todos.Store = DS.Store.extend({
revision: 12,
adapter: 'DS.FixtureAdapter'
});
Todos.Todo = DS.Model.extend({
title: DS.attr('string'),
isCompleted: DS.attr('boolean')
});
Todos.Todo.FIXTURES = [
{
id: 1,
title: 'Learn Ember.js',
isCompleted: true
},
{
id: 2,
title: '...',
isCompleted: false
},
{
id: 3,
title: 'Profit!',
isCompleted: false
}
];
Then here's my handlebars template:
...
{{#each controller}}
<li>
<input type="checkbox" class="toggle">
<label>{{title}}</label><button class="destroy"></button>
</li>
{{/each}}
And yet I get this error
Uncaught Error: assertion failed: an Ember.CollectionView's content must implement Ember.Array. You passed <(generated todos controller):ember257>
It looks to me like whatever default controller object Ember generates should be of type Ember.Array but it is not happening for some reason. I am wondering if it is a problem with ember-data?
I am using all the files from the starter kit which are
ember 1.0.0 rc5
handlebars 1.0.0 rc4
jquery 1.9.1
and ember-data, the only versioning indication i can tell is from a comment
// Last commit: 3981a7c (2013-05-28 05:00:14 -0700)
Is there a dependency problem someone knows about or did I do something wrong?
I wouldn't say its a problem with ember data, since that module is responsible only for talking to the api and giving you clever model objects.
You were right in saying ember is generating the wrong type of controller. By default Ember will probably generate a Controller, when what you need is an ArrayController. To get around the issue, simply create an empty controller like this
Todo.TodosController = Em.ArrayController.extend({});
The guide does say that ember creates an ArrayController, but perhaps it doesn't anymore!? let me know if it works by explicitly creating an arraycontroller. If it does we can let the ember team know.
I had this exact same issue today walking through the Getting Started Guide but it appeared to be due to a typo.
According to the documentation, the generated controller is supposed to be of type ArrayController. I dug into the Ember source and found the Ember.generateController method that generates the controller depending on the context. I set a break point and found that when Ember was trying to create a controller for the "Todos" route, the context was undefined, so the basic controller was generated.
Working backward from there, I set a breakpoint on the model function of my router to see what it was returning but found it was not being called at all. At this point, I began to get suspicious that I had done something wrong. And that is when I noticed that I had named the TodosRoute as TodosRouter (as you have in your original question). Changing the name to TodosRoute correctly called my model function and everything worked as expected. It was not necessary to include the line that explicitly created the TodosController as an ArrayController.
While it appears you had it correct in your question, I wanted to post this here in case someone else has the same issue.
Adding the line Gevious suggested corrected this issue for me. For clarification my router.js file now looks like this:
Todos.Router.map(function(){
this.resource('todos', {path: '/'});
});
Todos.TodosRoute = Ember.Route.extend({
model: function () {
return Todos.Todo.find();
}
});
Todos.TodosController = Em.ArrayController.extend({});

propertyBinding Controller - view

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.

Ember.js pre4, how to do the previous pre2 connectOutlet stuff

In pre2, suppose I had this application code, outside the router:
var controller = App.MyController.create();
controller.content = [...];
App.get('router').get('applicationController').connectOutlet({
outletName: 'modal',
controller: controller,
viewClass: App.MyView,
context: controller
});
That is, I fill an outlet named 'modal' added to the 'application' template, with my data.
Now, in pre4 I have no reference to the controllers created by the router. How would you fill an outlet from outside the router?
I could ask the router for a transition, but I don't want to modify the URL, as I'm just opening a modal over the current content.
EDIT:
This is what I came up with for a temp fix, by looking up the application view from the App.Router.router object.. obviously it's a dirty hack, anyone know the best & right way to do it in pre4?
var controller = App.MyController.create();
controller.content = this.get('content');
var theView = App.MyView.create();
theView.set('controller', controller);
App.Router.router.currentHandlerInfos[0].handler.router._activeViews.application[0].connectOutlet('modal', theView);
If you just need to add your view into the app you can use my solution in this question:
What's the right way to enter and exit modal states with Ember router v2?
But if you need to add it too an outlet, you can do it by sending an event to the router and just render it in the event without transitioning it to another route.
events: {
showModal: function(){
this.render('modal', {into: 'index', outlet: 'modalOutlet', controller = this.controllerFor('modal')});
}
}
See fiddle for an example:
http://jsfiddle.net/Energiz0r/gChWa/1