How to preselect a value on an Ember.select view - ember.js

I'm trying to preselect a value on an Ember.select view. My select works fine, but I don't know how to preselect a value dynamically - or isn't it possible at the moment with Ember 1.8-beta and Ember Data Beta-11?
This is my select:
{{view select
class="uk-width-1-1"
content=services
optionLabelPath="content.name"
optionValuePath="content.id"
prompt="Service"
selectionBinding="selectedService"
}}
It works fine when I try to get the current active value with this.get('selectedService'), but when I try to set a specific customer in my controller (e.g. to pre-fill an edit form), nothing happens:
var service = timetracking.get('service');
this.set('selectedService', service);
These are my models:
App.Timetracking = DS.Model.extend({
duration: DS.attr('number'),
day: DS.attr('date'),
notice: DS.attr('string'),
project: DS.belongsTo('project', {async: true}),
service: DS.belongsTo('service', {async: true}),
user: DS.belongsTo('user', {async: true})
});
App.Service = DS.Model.extend({
name: DS.attr('string'),
description: DS.attr('string'),
timetrackings: DS.hasMany('timetracking', {async: true}),
archived: DS.attr('boolean')
});

so you set the value of the select box, by setting "selectedService" to the instance that you want to have selected
http://emberjs.jsbin.com/vuhefa
you seem to be doing the same thing
var service = timetracking.get('service');
this.set('selectedService', service);
but since service is an async relationship it will return a promise and therefore not be the correct actual model try this insted
var context = this;
var service = timetracking.get('service').then(function(service){
context.set('selectedService', service);
});
its a rule that async relationships return promises and embedded relationships return objects

In your template can simply use service from your controller/model as follows:
{{view select
class="uk-width-1-1"
selection=service
content=services
optionLabelPath="content.name"
optionValuePath="content.id"
prompt="Service"
}}
The selection option specifies the property to use for populating the initial selection. It will use that object along with optionLabelPath and optionValuePath to configure the <select> element in the DOM.
When an option is chosen by the user, that same selection property will be updated, ie service on your controller/model.
IMPORTANT: Since you are using an async belongsTo for the service relation on your model, you need to help the select view to use the content of the promise proxy representing the relationship.
Do this by using an alias on your controller:
App.TimetrackingController = Ember.ObjectController.extend({
service: Ember.computed.alias('content.service.content')
});
There is an more on this over on the Ember issue.

Related

Unexpected text being rendered by Ember

I have models
// models/group
export default DS.Model.extend({
parent: DS.belongsTo('parent'),
items: DS.hasMany('item', {async: true}),
quantity: Ember.computed.sum('items.#each.quantity'),
});
// models/item
export default DS.Model.extend({
...
quantity: DS.attr('number')
});
And in my template (with controller.model set to parent) I try to render
{{#each group}}
{{quantity}}
{{/each}}
and expect a list of numbers, but instead what's rendered is a list of text like <spa#model:item::ember1036:165>
I'm guessing that the async promise is only resolved after rendering, but then why does it not update?
I don't believe sum will pull properties from each item in a collection. I believe it has to be a collection of numbers.
quantities: function(){
return this.get('items').getEach('quantity');
}.property('items.#each.quantity'),
quantity: Ember.computed.sum('quantities'),

Getting data from DS.PromiseArray in EmberJS

I'm new to emberJS and I'm having some trouble working with promises.
Here is my router:
this.resource('menus', function(){
this.resource('menu', {path: '/:menu_id'}, function(){
this.resource('submodule', {path: '/:submodule_id'});
});
});
});
I have nested routes, and the child route returns a menuss object based on a given id.
Here is my MenuRoute:
App.MenuRoute = Ember.Route.extend({
model: function(params){
return this.store.find('menuss', params.menu_id);
}
});
Here are my models:
App.Menuss = DS.Model.extend({
name: DS.attr('string'),
subModule: DS.hasMany('submodule', {async:true})
});
App.Submodule = DS.Model.extend({
name: DS.attr('string'),
content: DS.attr('string')
});
The 'subModule' attribute of Menuss model contains an array of Submodule model id's.
Inside my menu template, I'm receiving a menuss object and I want to display the SubModules each menu item has.
However, when I call {{this.subModule}}, it returns <DS.PromiseArray:ember488>. How can I get the contents from this subModule array?
I looked at some similar questions where they say use the then() method, but I can't seem to figure it out here.
In the template you'll want to iterate the property since it's an array, ember/handlebars will deal with the synchronicity of the PromiseArray.
{{#each item in subModule}}
{{item.name}}
{{/each}}

Ember.Select works sometimes but assertion fails

In my ember.js app, I have a 'user' model that has a "hasMany" relationship to 'group'. A user can be a member of zero or more groups. To allow the user to select the groups, I am using the built-in Ember.Select view.
If I load my users via the route /users, I can see the user and the groups to which that user is assigned. If I go to the edit route (/users/1/edit), I use Ember.Select to show the universe of all groups, along with the selection of that user's "selected" groups. Unfortunately, when I transition via the /users route, none of the groups are selected. If I refresh the page on the edit route, I see the groups correctly selected as I expect.
Another thing to note is that I don't see any errors when transitioning from /users to /users/1/edit (with no selected groups). However, when I refresh directly from the /users/1/edit route, the selection works correctly, but I see the following in the console (I am including a bit of the stack):
Assertion failed: The content property of DS.PromiseArray should be set before modifying it ember.js:394
(anonymous function) ember.js:394
Ember.assert ember.js:53
Ember.ArrayProxy.Ember.Object.extend._replace ember.js:16284
Ember.ArrayProxy.Ember.Object.extend.replace ember.js:16291
Ember.EnumerableUtils.replace ember.js:1829
Ember.Select.Ember.View.extend._changeMultiple ember.js:27933
Ember.Select.Ember.View.extend._change ember.js:27859
Ember.Select.Ember.View.extend._triggerChange ember.js:27902
sendEvent ember.js:2334
Any pointers would be helpful!
user_model.js:
Usermanagement.User = DS.Model.extend({
authenticateExternally: DS.attr(),
email: DS.attr(),
enabled: DS.attr(),
firstName: DS.attr(),
lastName: DS.attr(),
password: DS.attr(),
systemExternalAuthenticationEnabled: DS.attr(),
selectedGroups: DS.hasMany('group', {
async: true
}),
username: DS.attr(),
meta: DS.attr(),
fullName: function() {
return '%# %#'.fmt(this.get('firstName'), this.get('lastName'));
}.property('firstName', 'lastName'),
});
user_edit_template.hbs: (snippet)
<div class="field form-group">
<div class="fieldLabel">Groups</div>
{{view Ember.Select
multiple="true"
class="form-control"
selectionBinding="selectedGroups"
contentBinding="controllers.groups.allGroups"
optionLabelPath="content.name"
optionValuePath="content.id"}}
</div>
groups_controller.js:
Usermanagement.GroupsController = Ember.ArrayController.extend({
allGroups: function() {
return this.store.find('group');
}.property()
});
EDIT: Forgot to mention, Ember v1.0.0, Ember-data v1.0.0-beta3
The error is complaining about the select mucking with your model (user.selectedGroups) before it's finished loading.
The reason none are selected is probably because they are probably different objects. You might iterate over each item in the selected items and the allGroups options and check out the ember guid on it, if they are different items then that's why it's not showing them as selected.
Just out of curiosity, can you try setting the controller in the application route's setupController?
App.ApplicationRoute = Em.Route.extend({
setupController: function(controller, model){
this._super(controller, model);
this.controllerFor('groups').set('model', this.store.find('group'));
}
});
{{view Ember.Select
multiple="true"
class="form-control"
selectionBinding="selectedGroups"
contentBinding="controllers.groups.model" //instead of allGroups
optionLabelPath="content.name"
optionValuePath="content.id"}}

How to add multiple selection into a many-to-many model in Ember.js?

I have this small app where I'm trying to add the fruits selections of a multiple Ember.Select into an attribute of a model, "myfruits" of Person Alice. However, things are broken.
Perhaps my model is set up incorrectly.
This is the Ember.Select handlebars in the html:
{{view Ember.Select
multiple="true"
contentBinding="App.fruits"
valueBinding="pickedFruits"
}}
This is the model:
App.Person = DS.Model.extend({
name: DS.attr('string'),
myfruits: DS.hasMany('App.Fruit')
});
App.Fruit = DS.Model.extend({
kind: DS.attr('string'),
likedBy: DS.hasMany('App.Person')
});
This is the function that tries to save the multiple selection:
pickThem: function(){
var input_fruits = this.get('pickedFruits');
// should I create a Fruit object for each input_fruits?
var aperson = App.Person.createRecord({
name: "Alice",
myfruits: input_fruits
});
aperson.save();
}
I feel like the problem might be I'm not creating the Fruit objects. But I'm not sure how to make it work with the many-to-many relationship between Person and Fruit.
I guess what you need to do is as you already mentioned to create a App.Fruit record for every selected fruit and add it to the newly created App.Person.
Basically the important bit is:
App.PersonController = Ember.ArrayController.extend({
pickThem: function(){
var aperson = App.Person.createRecord({name: "Alice", myfruits: []});
this.get('pickedFruits').forEach(function(item){
aperson.get('myfruits').pushObject(App.Fruit.createRecord({kind:item, likedBy:[aperson.get('id')]}));
});
aperson.save();
}
});
Then provide a model for your person template:
App.PersonRoute = Ember.Route.extend({
model: function() {
return App.Person.find();
}
});
and in your template you can loop over the person records and inside that loop over their respective fruits:
{{#each model}}
{{name}} likes are:
{{#each myfruits}}
{{kind}}
{{/each}}
{{/each}}
Have a look at this updated jsbin.
You should however reset your local store adapter's data to avoid multiple entries after each application initialization. I've done it by creating a pseudo random suffix for the namespace of the LSAdapter, but this could be anything you find more convenient.
App.LSAdapter = DS.LSAdapter.create({
namespace: 'app-emberjs-'+Math.floor(Math.random()*1000)
});
Hope it helps.
Edit
After reading your last comment and just to show how it looks like in the chrome debugger tools that the LSAdapter stores the data. Have a look at the below screenshot. Here I've reloaded 2 times the app, and as expected two namespaces are created. If you have the same namespace every time thing are going to overlap resulting in some unexpected behavior.

duplicated data when navigating without reloading page using ember-data

I'm using ember.js 1.0.0-pre4, ember-data revision 11.
I have the following model:
App.DbProcess = DS.Model.extend({
pid: DS.attr('number'),
backendStart: DS.attr('string'),
transactionStart: DS.attr('string'),
queryStart: DS.attr('string'),
stateChange: DS.attr('string'),
waiting: DS.attr('boolean'),
state: DS.attr('string'),
query: DS.attr('string')
})
With the following route:
App.HomeDbProcessesRoute = Ember.Route.extend({
model: function() {
return App.DbProcess.find();
}
})
I then have a template which uses {{#each controller}}{{/each}} to render all the processes retrieved. However if I navigate to other pages (without reloading the page) and returning back to the processes page, the processes will be retrieved again and the duplicates are rendered on page.
EDIT: I also tried this, but it didn't work:
DS.RESTAdapter.map('App.DbProcess', {
primaryKey: 'pid'
})
I had the same issue now and here is my little hot-fix:
{{#if id}}
<div>
{{title}}
</div>
{{/if}}
In the template I render item from store only if it has id set (only those are coming from databse). But You propably solved it already!
(using revision 12)
Turns out you can do something like this to customize the primary key globally
App.Adapter = DS.RESTAdapter.extend({
url: document.location.protocol+'//url-api.com',
serializer: DS.RESTSerializer.extend({
primaryKey: function(type) {
// If the type is `BlogPost`, this will return
// `blog_post_id`.
var typeString = (''+type).split(".")[1].underscore();
return typeString + "_id";
}
})
})