Can't get property/model array in selection binding until too late - ember.js

I have an Ember Select view in my Ember CLI project that is bound to a dynamic model (an array of trips) that is created in setupController. Once loaded up the selection needs to default to a particular one indicated by tripId.
The method called in selectionBinding (selectedTrip) should do this but it can't seem to get the tripArray from model.
I've called console.debug(this.get('model')) and I can see tripArray there filled with entries but console.debug(this.get('model.tripArray')) returns undefined. It's almost like an illusion. At this point tripId transforms into null and the starting position for the select view is the prompt.
It's only until all the components are loaded that the controller can access tripArray. What am I doing wrong?
Controller:
export default Ember.ObjectController.extend(Ember.Validations.Mixin,{
trips: function(){
return this.get('model.tripArray');
}.property('model.tripArray'),
selectedTrip: function() {
var tempTrips = this.get('trips');
var trip;
console.debug(this.get('model.tripArray'));
if(tempTrips){
for(var i = 0; i < tempTrips.length; i++){
if(tempTrips[i].id === this.get('model.tripId')){
trip = tempTrips[i];
return;
}
}
}
return trip;
}.property('trips')
}
Route:
export default Ember.Route.extend({
beforeModel: function(){
this.controllerFor('application').checkSuperLogin();
},
setupController: function(controller, model){
this._super(controller, model);
controller.set('model', model);
$("#loading-spinner").modal("show");
$.getJSON("http://website.herokuapp.com/trips", function (data) {
console.log(data);
controller.set('model.tripArray', data);
}).done(function(){
$("#loading-spinner").modal("hide");
});
}
});
Select view:
{{view Ember.Select
contentBinding="controller.trips"
valueBinding="model.tripId"
optionValuePath="content.id"
optionLabelPath="content.locationName"
prompt="-"
classNames="form-control input-md"
selectionBinding="controller.selectedTrip"
}}

Related

How to set default values for query params based on dynamic segment?

I am creating universal grid for some entities. For that I added this in routes:
this.route('record', { path: '/record' },function() {
this.route('index', {path: '/:entity'});
this.route('view', {path: '/:entity/:record_id'});
});
and created new "index" route:
export default Ember.Route.extend({
entity: '',
queryParams: {
sort: {
refreshModel: true
}
},
beforeModel: function(transition) {
var entity = transition.params[this.get('routeName')].entity;
this.set('entity', entity);
},
model: function(params) {
delete params.entity;
return this.store.findQuery(this.get('entity'), params);
},
}
my controller
export default Ember.ArrayController.extend({
queryParams: ['sort'],
sort: ''
}
how can I set default value for the "sort" based on dynamic segment?
For example in my settings I store sort values for all entites:
'settings.user.sort': 'email ASC',
'settings.company.sort': 'name ASC',
I tried to define "sort" as computed property, but its "get" method is called in time when I can't get a value of dynamic segment from currentHadlerInfo or from route.
Also defining of the property "sort" as computed property has strange effect, for example, when I define it as
sort: 'email ASC'
in my template it is displayed via {{sort}} as expected (email ASC).
but when I return a value from computed property, I see empty value in my template and this affects on a work of components (I can't get current sorted column)
What can I do?..
Here is a rough implementation of setting the sort properties based on dynamic segment values. The code will look like
<script type="text/x-handlebars" data-template-name="index">
{{#link-to 'sort' model.settings.user.sort}}Sort{{/link-to}}
</script>
<script type="text/x-handlebars" data-template-name="sort">
<ul>
{{#each arrangedContent as |item|}}
<li>{{item.val}}</li>
{{/each}}
</ul>
</script>
var settings = Em.Object.create({
settings: {
user: {
sort: 'val ASC'
}
}
});
App.Router.map(function() {
this.route('sort', {path: '/:sortParams'});
});
App.SortRoute = Ember.Route.extend({
params: null,
model: function(params) {
this.set('params', params);
return [{val:1}, {val:5}, {val:0}];
},
setupController: function(controller, model) {
this._super(controller, model);
var props = this.get('params').sortParams.split(' ');
var property = [props[0]];
var order = props[1] === 'ASC'? true : false;
controller.set('sortProperties', property);
controller.set('sortAscending', order);
}
});
The working demo can be found here..
As you can see I access the params object in the model hook and store it on the route. In the setupController hook I access the params and set the required values on the controller.

Setting a belongsTo attribute via an action on controller not working

I have the following models, customer:
App.Customer = DS.Model.extend({
//Other Attributes
area: DS.belongsTo('area', {async: true})
});
and area model -
App.Area = DS.Model.extend({
name: DS.attr('string'),
zip_code: DS.attr('number')
});
And I use an Ember.Select view to show a dropdown of the area a customer is in -
{{view Ember.Select viewName="select_area" content=possible_areas optionValuePath="content.id" optionLabelPath="content.name" value=area.id prompt="No area selected" selectionBinding="selected_area"}}
and the controller which wires up everything together -
App.CustomerController = Ember.ObjectController.extend({
possible_areas: function() {
return this.store.find('area');
}.property(),
selected_area: null,
actions: {
save: function() {
var selected_area_id = this.get('selected_area.id');
console.log(selected_area_id);
var selected_area = this.store.find('area', selected_area_id);
var self = this;
selected_area.then(function(area) {
console.log(area.get('id'));
var updated_customer = self.get('model');
updated_customer.set('area', area );
console.log(new_customer.get('area.id'));
updated_customer.save()
.then(function(customer) {
//success
},
function() {
//failure
});
});
}
}
});
Now here is the weird thing. Upon calling the 'save' action the first time, the line updated_customer.set('area', area ); fails with the error
Uncaught Error: Assertion Failed: Cannot delegate set('id', ) to the 'content' property of object proxy <DS.PromiseObject:ember551>: its 'content' is undefined.
Upon calling 'save' action immediately after that, the save goes through without any error and area of the customer is updated successfully. Although the dropdown shows selected_area to be null.
How do I prevent the first save from erroring out?
I am using ember-data 1.0.0-beta.6.
Since you have the association defined in your Customer model, I would remove the selected_area property from the controller and use ember-data's associations instead. Bind to the "area" association in the Ember.Select by using the selectionBinding property.
{{view Ember.Select viewName="select_area"
content=possible_areas
optionValuePath="content.id"
optionLabelPath="content.name"
prompt="No area selected"
selectionBinding="area"}}
This way, the area attribute will change when the user interacts with the select menu.
This has the added benefit of cleaning up your save action since we're binding directly to the area association for the Customer.
App.CustomerController = Ember.ObjectController.extend({
possible_areas: function() {
return this.store.find('area');
},
actions: {
save: function() {
this.get('model').save().then(this.onDidCreate.bind(this), this.onDidFail.bind(this))
}
onDidCreate: function() {
// Fullfilled callback
}
onDidFail: function() {
// Rejected callback
}
}
});
However, the possible_areas property won't be populated when the template first renders since this.get('area') returns a promise. I would wait to render the select until the promise settles. You can do this in the routes model hook since it waits until promises settle to render the template.
App.CustomerRoute = Ember.Route.extend({
route: function(params) {
return Ember.RSVP.hash({
customer: this.store.find('customer', params.customer_id),
possible_areas: this.store.find('area')
});
},
// Since the route hook returns a hash of (hopefully) settled promises, we
// have to set the model property here, as well as the possible_areas property.
setupController: function(controller, model) {
controller.set('model', model.customer);
controller.set('possible_areas', model.possible_areas);
}
});

How to get an array containing the values of an attribute for an Ember model?

For example if I have a Person model with the "name" attribute, what can I call in Ember.js with Ember Data that returns an array of all the names in the Person model?
App.Person.find().then( function(data) {
var namesArray = data.getEach('name');
});
UPDATE RE: COMMENT (what if i want to do this from setupController...)
setupController: function(controller, model) {
App.Person.find().then( function(data) {
controller.set('variablename', data.getEach('name') };
});
}
App.PersonsRoute = Ember.Route.find({
setupController: function() {
// Get all persons
this.controllerFor('persons').set('content', App.Person.find());
}
});
App.PersonsController = Ember.ArrayController.extend({
allNames: function() {
var persons = this.get('content') || [];
return persons.getEach('name');
}.property('content.[]')
});
In short, when you have a collection (an array of objects) and you want to build a new array of the values of a certain property, use getEach. getEach('foo') is an alias for mapProperty('foo').

Computed property in handlebar #if not updating

I am trying to use the following template:
<script type="text/x-handlebars" data-template-name="login">
{{#if logged_in}}
Logged in
{{else}}
Not logged in
{{/if}}
</script>
with the model:
App.Login = DS.Model.extend({
access_token: DS.attr('string'),
logged_in: function() {
return (this.get('access_token') != null);
}.property('access_token')
});
to display the user's logged-in state.
The access_token is being set via an async callback in the Route's setupController:
App.LoginRoute = Ember.Route.extend({
setupController: function(controller, model) {
controller.set('content', model);
// call async login method
window.setInterval(function test() {
model.set('access_token', 'MY_ACCESS_TOKEN');
console.log(model.get('access_token'));
}, 5000);
},
model: function() {
return App.Login.find();
}
});
The problem is logged_in never seems to change (even though the model.set line is executed and 'access_token' is updated). Am I doing something wrong or should I be filing a bug?
Full code: http://jsfiddle.net/Q8eHq/
You are setting the model to App.Login.find() which returns an enumerable, not a single object. One way to do it, is to set the model to a single object:
App.LoginRoute = Ember.Route.extend({
model: function() {
return App.Login.find(1);
}
});
Or if you are going to use a dynamic route (e.g. users/login/9):
App.LoginRoute = Ember.Route.extend({
model: function(params) {
return App.Login.find(params.id);
}
});

Ember View is rendering too early

I have a Route for creating new documents, making a copy of an existing document.
App.DocumentsNewRoute = Ember.Route.extend({
model: function (params) {
this.modelParams = params; // save params for reference
return App.Document.createRecord();
},
setupController: function (controller, model) {
// use the params to get our reference document
var documentModel = App.Document.find(this.modelParams.document_id);
documentModel.one('didLoad', function () {
// once loaded, make a serialized copy
var documentObj = documentModel.serialize();
// and set the properties to our empty record
model.setProperties(documentObj);
console.log('didLoad');
});
}
});
I added some logs in my View.
App.DocumentView = Ember.View.extend({
init: function () {
this._super();
// fires before the didLoad in the router
console.log('init view');
},
willInsertElement: function () {
// fires before the didLoad in the router
console.log('insert elem');
}
});
And this is my template
{{#if model.isLoaded }}
{{ view App.DocumentView templateNameBinding="model.slug" class="document portrait" }}
{{ else }}
Loading...
{{/if}}
The problem it seems is that my model isLoaded, but not populated when my template is rendered, so the templateNameBinding doesn't exist at this point, and doesn't seem to be updated when the data gets populated.
Should I use something else than model.isLoaded in my template, or should I force a re-render of my template, and if so, how and where? Thanks!
It seem that you are overcomplicating the things. It should be:
EDIT
I misunderstood your question in first read, so
App.DocumentsNewRoute = Ember.Route.extend({
model: function (params) {
var originalDoc = App.Document.find(params.document_id),
newDoc = App.Document.createRecord();
originalDoc.one('didLoad', function () {
newDoc.setProperties(this.serialize());
});
return newDoc;
},
setupController: function (controller, model) {
controller.set('content', model);
}
});
If everything is right, ember will re-render things automatically..