Using ArrayController with multiple models - javascript

I have a Route which loads multiple models:
App.AppRoute = Ember.Route.extend({
model: function() {
return Ember.RSVP.hash({
models1: this.store.find('model1'),
models2: this.store.find('model2'),
models3: this.store.find('model3'),
})
}
});
I want to extend the ArrayController for this page:
App.AppController = Ember.ArrayController.extend();
And the error is thrown:
Error: Assertion Failed: ArrayProxy expects an Array or Ember.ArrayProxy, but you passed object
My question is how to write a controller which can handle multiple model objects.

Really an array doesn't make since. You have 3 different arrays.
So it would be an object controller, and you can access each item as an array in the application template or wherever it's appropriate.
App.AppController = Ember.ObjectController.extend();

Related

Rendering templates with different models into named outlets in Ember.js

I want to render 2 lists of items, both templates that each over the model.
I tried to render the main template first and then the nested ones into some named outlets of the main template, which works.
But when I try to give the nested ones a different model (they all use different model arrays), I get an error:
The value that #each loops over must be an Array.
You passed '<DS.PromiseArray:ember451>' (wrapped in (generated homepage controller))
Here is the code:
renderTemplate:function(){
this.render('homepage');
this.render('roomlist',{
'into':'homepage',
'outlet':'roomlist',
'model':this.store.find('room')
});
this.render('activitylist',{
'into':'homepage',
'outlet':'activitylist',
'model':this.store.find('activity')
});
}
Edit:
Alternative idea I had, was to use {{render "roomlist" rooms}} after this.set("rooms", this.store.find("room"); in the model-hook instead of the renderTemplate-hook. But it threw almost the same error:
The value that #each loops over must be an Array.
You passed (generated roomlist controller)
You should resolve the models you will use in the model hook, and then you can use them in your templates much easier:
something like this:
model: function() {
return Ember.RSVP.hash({
rooms: this.store.find('room'),
activities: this.store.find('activity')
});
},
setupController: function(controller, model) {
this.set('rooms', model.rooms);
this.set('activities', model.activities);
controller.set('model', model.rooms);
},
renderTemplate: function(){
this.render('homepage');
this.render('roomlist',{
'into':'homepage',
'outlet':'roomlist',
'model':this.get('rooms')
});
this.render('activitylist',{
'into':'homepage',
'outlet':'activitylist',
'model':this.get('activities')
});
}

Ember route or controller expose data model as json

I am trying to work with Ember.js
Can I expose my data model as JSON through a route or controller?
I have an object like this saved in the store:
this.store.createRecord('Person', {
id: 1,
name: this.get('name'),
email: this.get('email')
});
I want to expose this data from a route or controller as JSON object. I don't want to use any view.
Is it possible to do this?
Thanks for help!
EDIT
My route is:
App.ResultRoute = Ember.Route.extend({
model: function() {
return this.store.find('person', 1);
}
});
There is '1' because I want only this record.
In this way It works and I see in the view the {{name}} and the {{email} of the Person object.
I want to see only the JSON, I tried to do how you suggest me :
App.ResultRoute = Ember.Route.extend({
afterModel: function (model) {
model.get('content').forEach(function (item) {
console.log(item.get('content'));
});
}
});
But I receive this error:
Uncaught Error: Assertion Failed: Error: More context objects were passed than there are dynamic segments for the route: error
What is my error?
The way I would do this would be, I would have an api in my model which would return a plain json object to whoever asked it. So the Person model would have a getPersonDetails method which will hide all the internal details, including the attributes and associations and whatever else, and return the state of the person object it is invoked upon.
So, for example, if you wanted to display a table of persons or something, you would do a createRecord, and just ask the newly created person object for it's details.
Start from the beginning of this guide. http://emberjs.com/guides/routing/specifying-a-routes-model/ It will show you how to specify a model for a route.
Then, read this entire guide on controllers: http://emberjs.com/guides/controllers/
In general, you would access that data from the route's model hook with:
this.store.find('person') // All records
If you wanted to access that first object as JSON, you could do:
var person_JSON = this.store.find('person').then(function (persons) {
//The persons records are now available so you can do whatever you want with them
console.log(persons.objectAt(0).get('content'));
});
You could also iterate over all records and strip out the content to produce raw json without the Ember wrapping... Just depends on what you need to really do.
Really the best place to put this would be the route's afterModel hook, though. You wouldn't be working with a promise, as Ember would have dealt with that for you:
afterModel: function (model) {
model.get('content').forEach(function (item) {
console.log(item.get('content'));
});
}
Hope that helps.
Edit: Since you have one record try this:
afterModel: function (model) {
console.log(model.get('content'));
}

Ember sort an enumerable array or add an new object to a regular array

I have a todo list.
The main template has an input for entering new todos and an outlet.
The outlet is for the todos.index template which displays each todo. I have made them srtable using jquery sortable. I sort them using a model property.
todos route:
model: function(){
return this.store.filter("todo", {}, function(todo){
if(todo.get("user") !== null && parseInt(todo.get("user").get("id")) === user_id)
return todo;
});
todos index route:
model: function(){
return this.modelFor('todos').sortBy('idx');
}
controllers:
App.TodosController = Ember.ArrayController.extend();
App.TodosIndexController = Ember.ObjectController.extend();
But when I add this .sortBy() method my returning array of objects is no longer live and any new todos i create arent added to the template. (they do get created and are in ember data and in my db, but they just arent being added to the template) - When you do sortBy the live array of ember data gets copied into an immmutable regular array.
If I leave out the sortBy() my new objects populate my template just fine.
any ideas of how to sort an array but keep it enumerble or how to refresh the template?
sortProperties is not working - I dont know why. My list of todos is populated by the objectController.
EDIT -
My problem was my own fault. I thought I had this
App.TodosIndexController= Ember.ObjectController.extend();
But what I actually had was:
App.TodoController = Ember.ObjectController.extend();
This left me without an explicit controller to do the sorting for me, so for me the solution was to create a new arrayController:
App.TodosIndexController= Ember.ObjectController.extend();
and use that to do the sorting. Though it wasnt the solution to my problem, my issue was my own obliviousness, it is the right way to sort items so I am going to mark #Kingpins answer as correct.
Make your controller sorted, and iterate over the controller (not the model) in your template.
App.TodosIndexController = Ember.ArrayController.extend({
sortProperties: ['idx'],
sortAscending: true
});
return your model to just the modelFor
model: function(){
return this.modelFor('todos');
}
Or you could create a computed property on the controller and watch the model for updates, updating the computed property
App.TodosIndexController = Ember.ArrayController.extend({
sortedTodos: function(){
return this.get('model').sortBy('idx');
}.('model.[]')
});
and again, return your model to just the modelFor
model: function(){
return this.modelFor('todos');
}
The other way to manually update the template, is to just get the model for TodosIndexController and manually use pushObject to add the new todos to it. It's just an array of items.

Ember.js: dependencies between two controllers failing

I am trying to access one of two models in a controller that uses needs on a sibling controller. My router looks like the following:
App.Router.map(function() {
this.route('login');
this.route('mlb.lineups', {path: 'tools/mlb/lineups'})
this.resource('mlb.lineups.site', { path: 'tools/mlb/lineups/site/:site_id' });
});
The mlb.lineups route definition looks like the following:
App.MlbLineupsRoute = Ember.Route.extend({
model: function() {
var self = this;
return Ember.RSVP.hash({
sites: self.store.find('site')
})
},
setupController: function(controller, models) {
controller.set('model', models.get('sites'));
},
afterModel: function(models) {
var site = models.sites.get('firstObject');
this.transitionTo('mlb.lineups.site', site);
}
});
The reason I am using Ember.RSVP.hash({}) here is I plan on adding another model to be retrieved after I retrieve the site model.
Now in my MlbLineupsSiteController I am trying to access the sites model with the following:
App.MlbLineupsSiteController = Ember.ArrayController.extend({
needs: "mlb.lineups",
sites: Ember.computed.alias("controllers.models.sites")
});
This is the error I'm getting in my Ember console: needs must not specify dependencies with periods in their names (mlb.lineups)
What's the best way to make the sites model from the MlbLineups controller available in my MlbLineupsSiteController?
Note:
#NicholasJohn16's answer isn't valid anymore. It always gives an error that controller couldn't be found. Generally you should also never use needs property and always use Ember.inject.controller if you have to make your controllers dependent on each other. I'd also recommend using services instead of dependencies between controllers. It's easier to maintain code which contains communication between controllers through services, than controller directly accessing other controller's properties. You might not always be aware of such access, and using services gives you another layer of security.
Solution:
Tested in Ember.js 1.10.0-beta.4. Use following code in Controller to reference nested controller in needs:
needs: ['classic/about']
Then you can access it later using:
const aboutController = this.get('controllers.classic/about');
const aboutProperty = aboutController.get('customProperty');
Works as expected. Basically you need to replace dots with slashes.
It should be:
needs:" MlbLineupsSite "
Basically, the name of the controller you want to include, minus the word controller.
Everything else you posted should work.

Ember.js with EmberFire Object - How to use with array attributes?

I read a lot of, still can't solve by myself. I've an Ember Route, with az EmberFire model.
App.NewRoute = Ember.Route.extend({
model: function() {
return EmberFire.Object.create({
ref: window.ref
});
},
setupController: function(controller, model) {
controller.set('model', model);
})
After that for example I've in the controller:
App.NewController = Ember.Controller.extend({
actions: {
save: function() {
this.get('model').set('questions', Ember.A([]));
this.get('model').get('questions').pushObject(Ember.Object.create({title: 'foo'}));
this.get('model').get('questions').get('lastObject').set('title', 'bar');
this.get('model').set('title', 'foobar');
}
}
});
When the save action called only the title change is made on fire base.
Can someone please explain how can I changed the array too?
I would change your model from an EmberFire.Object.create to an EmberFire.Array.create. Instead of having an object with an array as a property. EmberFire is a simple wrapper that bridges ember objects to firebase objects. I don't think it works with nested arrays inside an object yet as I just started working with the library.
If you need a property identified to that array in an object, create a second object EmberFire.Object and add a property questions that references this EmberFire.Array firebase object instead.
So your save action would look more like this:
var array = this.get('model');
array.pushObject(Ember.Object.create({title: 'foo'}));
array.get('lastObject').set('title', 'bar');

Categories