Ember - How do you preselect items in multiselect when using hasMany attribute? - ember.js

I am currently having issues preselecting items in the multiselect view. I'm using ember 1.3 with ember-data 1.0.0 beta 6 and ember-data-django-rest-adapter.
App.Article = DS.Model.extend({
title: attr(),
description: attr(),
authors: hasMany('author')
});
App.ArticleRoute = Ember.Route.extend({
model: function(params) {
return this.store.find('article', params.article_id);
},
setupController: function(controller, model) {
controller.set('content', model);
}
});
App.ArticleController = Ember.ObjectController.extend({
needs: ['authors'],
allAuthors: function() {
return this.store.find('author');
}.property()
});
Template:
{{input authors as='select'
multiple='true'
collection='allAuthors'
selection='authors'
optionValuePath='content.id'
optionLabelPath='content.name'}}
I'm not sure why this is not working because when I output allAuthors and authors using #each in the template, I'm getting the data that I should.
Is there something I'm missing to do?
Thanks in advance for the help.

I usually prepopulate this kind of data in the route using a promise:
App.ArticleRoute = Ember.Route.extend({
model: function(params) {
return this.store.find('article', params.article_id);
},
setupController: function(controller, model) {
this.store.find('author').then(function(authors) {
controller.set('allAuthors', authors);
// or maybe controller.get('allAuthors').addObjects(authors);
});
controller.set('content', model);
}
});
App.ArticleController = Ember.ObjectController.extend({
needs: ['authors'],
allAuthors: []
});
Not sure if this is the best way to do this, but it's what worked for me.

Related

Ember.js Masonry rendering doesn't wait for Data Models to be generated

I am trying to build a masonry view of the top selling Items in a hypothetical eCommerce Site but Masonry is being rendered before the Data Models can be generated over RESTAdapter. Here are is my Ember.js code:
App.Userprofile = DS.Model.extend({
loggedIn: DS.attr('boolean'),
name: DS.attr('string'),
totalItems: DS.attr('number'),
});
App.ApplicationRoute = Ember.Route.extend({
setupController: function(controller) {
this.store.find('userprofile', 'bat#man.com').then (function(userprofile) {
controller.set ('model', userprofile);
});
}
});
App.ApplicationAdapter = DS.DjangoRESTAdapter.extend({
host: HOST,
namespace: 'api'
});
App.ApplicationView = Ember.View.extend({
elementId: '',
classNames: ['container','fullwidth'],
templateName: 'application'
});
App.Cloud = DS.Model.extend({
item: DS.attr('string'),
numberItems: DS.attr('number'),
rank: DS.attr('number')
});
App.CloudAdapter = DS.DjangoRESTAdapter.extend({
host: HOST,
namespace: 'api',
});
App.CloudController = Ember.ObjectController.extend({
needs: ['application'],
cloudSize: function() { // Determines the size of the div
var cloudsize = Math.round (this.get('model.numberItems') * 5 / this.get('controllers.application.totalItems')) + 1;
var divName = "score" + cloudsize.toString();
return divName;
}.property('model.numberItems', 'controllers.application.totalitems')
});
App.ItemcloudRoute = Ember.Route.extend({
setupController: function(controller) {
this.store.findAll('cloud').then (function(itemcloud) {
controller.set ('model', itemcloud);
});
}
});
App.ItemcloudController = Ember.ArrayController.extend({
needs: ['cloud', 'application'],
sortProperties: ['rank'],
});
App.ItemcloudView = Ember.View.extend({
elementId: 'magicgrid',
classNames: ['cloudcontainer'],
templateName: 'itemcloud',
didInsertElement: (function() {
this._super();
Ember.run.scheduleOnce('afterRender', this, this.applyMasonry);
}).observes('controller.itemcloud'),
applyMasonry: function() {
setTimeout( function() { // settimeout to ensure masonry is called after data models are generate
console.log ("applyMasonry being called");
$('#magicgrid').masonry({
itemSelector: '.company',
isAnimated: true
});
}, 2000);
}
});
Here is the portion of the template file where itemcloud is generated.
<script type="text/x-handlebars" data-template-name='itemcloud'>
{{#each controller.model itemController="cloud"}}
<div {{bind-attr class=":company cloudSize"}}>
<div class="companylogo">
<img src="images/logos/color-logos/logo-01.jpg" />
</div>
<div class="count">{{numberItems}}</div>
</div>
{{/each}}
<div class="clearfix"></div>
</script>
Now, I am struggling to find a way to hold the Masonry rendering until after the data is fetched due to the asynchronous nature of the data fetching and the template rendering. My research says that using a View for the CloudController Objects would be useful, but am trying to figure out if there is something I am missing in my current design. Also, if someone can provide pointers to use Views correctly here for the CloudController Objects
Let me know if I need to provide any more clarifications. Thanks!
if you doing it in the setupController Ember assumes the model is already ready and continues rendering the page despite the response not coming back from the server.
The easiest way to do it is to return your model/promise in the model hook. Ember will wait on rendering the page until the model has been resolved.
App.ItemcloudRoute = Ember.Route.extend({
model: function(){
this.store.find('cloud');
}
});
The code above will do the same thing your code was doing, except Ember will wait for the find to resolve before creating and setting the model on the controller.
As per kingpin2k comments updating the answer to reflect the working code:
App.ApplicationRoute = Ember.Route.extend({
model: function() {
return this.store.find ('userprofile', 'bat#man.com');
},
setupController: function(controller, model) {
controller.set ('model', model);
}
});

emberjs using "needs" but other controller model empty

How do I get the the model to load without going to the route first?
App.UsersRoute = Em.Route.extend({
model: function() {
return ['bob', 'sue', 'tom'];
},
setupController: function(controller, model) {
controller.set('model', model);
}
});
From another controller using
needs: "users"
and
this.get('controllers.users.content');
works fine as long as I visit the UsersRoute first.
Load it in the topmost route that will need it, thusly:
App.SomeOtherRoute = Em.Route.extend({
setupController: function(controller, model) {
controller.set('model', model);
this.controllerFor('user').set('model', ['bob', 'sue', 'tom']);
}
});
Note that if you are using ember-data or epf or ajax, that the model will be a promise. You can't set the model on a controller to be a promise so you would do:
setupController: function(controller, model) {
controller.set('model', model);
return this.get('store').findAll('user').then(function(users) {
this.controllerFor('users').set('model', users);
});
}
Note in the second one I'm using UsersController, not UserController, because you seem to want a collection of users not a single user.
I hit this same problem this past weekend and the following worked for me:
App.SomeOtherController = Ember.Controller.extend({
needs: ['users']
});
App.SomeOtherRoute = Ember.Route.extend({
setupController: function( controller, model ){
this._super( controller, model );
controller.set( 'controllers.users.model', ['bob', 'sue', 'tom'] );
}
});
if it is an ajax call / ember data then you need something like this:
App.SomeOtherController = Ember.Controller.extend({
needs: ['users']
});
App.SomeOtherRoute = Ember.Route.extend({
setupController: function( controller, model ){
this._super( controller, model );
this.get('store').findAll('user').then(function(users) {
controller.set( 'controllers.users.model', users );
});
}
});
However, a colleague pointed out to me during our code review today that if I need to do this I have probably structured my routes / resources / models incorrectly. In other words an outer route should not depend on an inner route's model. So I am now thinking about going back and refactoring this so that the users model is part of the model for the outer route and then I can use that in my inner route controller.

Ember.js - IndexRoute's model

I've been over the guide and some questions around the net. This has to be simple but I'm just missing something..The worst is that other stuff is working out great but not something that should be real trivial (imho).
I have a basic Ember app set up. I have an index template defined, as a landing page. There are elements I want it to reference right off the bat from a 'model' i have as just a javascript object in the script.
var company = {
id: '1',
name: 'Some Inc.',
email: 'guy#some-inc.com'
};
App.IndexRoute = Ember.Route.extend({
setupController: function(controller, company) {
// Set the IndexController's `title`
controller.set('title', "Publisher Dashboard");
controller.set('model', company);
}
});
In my HTML I have within the index template:
<script type="text/x-handlebars" data-template-name="index">
...
<span class="name">{{name}} ( {{email}} )</span>
I don't know if things have changed over versions. I have seen different syntax do/claim to do the same stuff. I'm using v1.0.0.
You have to declare a model that you are going to use for the IndexRoute
App.IndexRoute = Ember.Route.extend({
model: function() {
return 'SOMETHING' //could be 'company'
},
setupController: function(controller, company) {
// Set the IndexController's `title`
controller.set('title', "Publisher Dashboard");
controller.set('model', company);
}
});
SetupController model uses the model that is returned by the model hook on the route. So if you return 'company' in the model hook. You will have access to the that company object.
have you tried doing this ?
setupController: function(controller, company) {
this._super(controller, model);
// Set the IndexController's `title`
controller.set('title', "Publisher Dashboard");
controller.set('model', company);
}
I added one line, which is to call super.

Load additional data into an EmberData model that is still in the store

I have a list of products that will be loaded under the /products route, from there you can navigate to a single product under the /products/:product_id. This is my models and the route:
var Product = DS.Model.extend({
page_title: DS.attr('string'),
image: DS.attr('string')
});
var ProductComment = DS.Model.extend({
contents: DS.attr('string')
});
var ProductRoute = Ember.Route.extend({
model: function(params) {
return App.Product.find(params.product_id)
},
setupController: function(controller, model) {
controller.set('content', model);
}
});
On the product page I want to load the products and additionally the comments for a product. As I use an external Api I cant load the id of the comments into the product model. So now I want to load the comments in to the ProductsController. I tried like described in this SO but it doesn't work. I'm using EmberDatas RESTAdapter.
I came up with solution. In the modelAfter hook of the products route, check if the comments are loaded into the model using this.get('product_comments').content.length. If not, load the data using App.ProductComment.find({product_id: this.id}) and store them into the model.
App.ProductRoute = Ember.Route.extend({
afterModel: function(model) {
model.ensureComments();
}
});
Product = DS.Model.extend({
page_title: DS.attr('string'),
image: DS.attr('string'),
product_comments: DS.hasMany('App.ProductComment'),
ensureComments: function() {
var productcomments = this.get('product_comments');
if (!productcomments.content.length) {
App.ProductComment.find({product_id: this.id}).then(function(result) {
productcomments.pushObjects(result)
});
}
}
});

Ember.js - how to fetch additional record details?

Struggling with populating Ember with data.
I'm using Rails as the backend, and when I hit /contacts.json (ContactsRoute), it returns a list of id, first, last -- works as expected.
However, when visiting a detail view (ContactRoute), I would like to hit /contacts/1.json and fetch details like email address, anniversaries, etc. But since I have a dynamic segment the model hook is skipped and none of the associations are available.
Question: what is the best approach for fetching additional data in a list/detail scenario?
Models:
App.Contact = DS.Model.extend({
firstName: DS.attr('string'),
lastName: DS.attr('string'),
company: DS.attr('string'),
emails: DS.hasMany('App.Email'),
});
App.Email = DS.Model.extend({
contact: DS.belongsTo('App.Contact'),
emailAddress: DS.attr('string'),
});
Route:
App.Router.map(function() {
this.resource('contacts');
this.resource('contact', {path: 'contacts/:contact_id'});
});
App.ContactsRoute = Ember.Route.extend({
init: function() {},
model: function() {
return App.Contact.find();
}
});
App.ContactRoute = Ember.Route.extend({
model: function(params) {
return App.Contact.find(params.contact_id);
}
});
Thanks in advance.
I just posted an answer to a similar problem here: https://stackoverflow.com/a/18553153/1254484
Basically, in your App.ContactRoute, override the setupController method:
setupController: function(controller, model) {
controller.set("model", model);
model.reload();
return;
}
I'm using this with the latest ember-data (commit ef11bff from 2013-08-26).