Thanks in advance for any help.
I'm having a strange problem with a simple Ember app where, after deploying to Heroku, my models only make the REST call after the index route for the model is hit.
For example, I have two models: Resort and Forecast. Each have a belongsTo relationship, so every Resort has a Forecast and vice versa. In the resort template, there's a link to the corresponding forecast. When clicked, it properly routes to the forecast, however all the attributes in the forecast are undefined because it never made the API call to retrieve the forecasts JSON blob. I can watch the network tab in Chrome tools to verify this. When I navigate to /forecasts, the REST call is made, and the data is populated.
For whatever reason, all the API calls are made as I would expect. Once deployed to Heroku, this isn't the case.
This app is using ember-cli, and the relevant code follows:
/adapters/application.js
import DS from "ember-data";
var ApplicationAdapter = DS.ActiveModelAdapter.extend({
host: 'http://api.firstchair.io',
buildURL: function() {
var base;
base = this._super.apply(this, arguments);
return "" + base + ".json";
}
});
export default ApplicationAdapter;
/models/resort.js
import DS from 'ember-data';
export default DS.Model.extend({
name: DS.attr('string'),
state: DS.attr('string'),
latitude: DS.attr('string'),
longitude: DS.attr('string'),
region: DS.attr('string'),
token: DS.attr('string'),
weather: DS.attr('string'),
temperature: DS.attr('string'),
last_24_hours_snowfall: DS.attr('string'),
last_48_hours_snowfall: DS.attr('string'),
last_72_hours_snowfall: DS.attr('string'),
wind: DS.attr('string'),
conditions: DS.attr('string'),
baseDepth: DS.attr('string'),
humanReadableWeather: DS.attr('string'),
forecast: DS.belongsTo('forecast'),
fullDescription: function() {
return this.get('name') + ', ' + this.get('state');
}.property('name', 'state'),
currentSnowfall: function() {
return (this.get('last_24_hours_snowfall') || 0) + '"';
}.property('last_24_hours_snowfall'),
hasWind: function() {
return this.get('wind') > 0;
}.property('wind')
});
/models/forecast.js
import DS from 'ember-data';
export default DS.Model.extend({
startsAt: DS.attr('string'),
endsAt: DS.attr('string'),
weather: DS.attr('array'),
highs: DS.attr('array'),
lows: DS.attr('array'),
resort: DS.belongsTo('resort'),
days: function() {
var weather = this.get('weather');
var highs = this.get('highs');
var lows = this.get('lows');
if (!weather) { return []; }
return weather.map(function(currWeather, index) {
return {
weather: currWeather,
high: highs[index],
low: lows[index],
daysSince: index
};
});
}.property('weather', 'highs', 'lows')
});
/routes/resort.js
import Ember from 'ember';
export default Ember.Route.extend({
model: function(params) {
var resort = this.store.find('resort', params.resort_id);
console.log(resort);
console.log(resort.forecast);
return resort;
}
});
/routes/resorts.js
import Ember from 'ember';
export default Ember.Route.extend({
model: function() {
return this.store.findAll('resort');
}
});
/routes/forecast.js
import Ember from 'ember';
export default Ember.Route.extend({
model: function(params) {
console.log('hello');
return this.store.find('forecast', params.forecast_id);
}
});
/routes/forecasts.js
import Ember from 'ember';
export default Ember.Route.extend({
model: function() {
return this.store.findAll('forecast');
}
});
Is there something I should be doing to ensure that the data is loaded eagerly?
You can look at the code in its entirety at: https://github.com/firstchair-io/webapp
Any insight into what might be going wrong would be greatly appreciated. Thank you.
It sounds like the relationships are not being sideloaded from the backend server. So the records contain an array of ID's in the hasMany fields, but the data itself is not sent automatically. To cause Ember Data to load the associated records, set {async: true} on the relation.
Related
The master record, app/models/account:
import DS from 'ember-data';
export default DS.Model.extend({
username: DS.attr('string'),
emailaddress: DS.hasMany('emailaddresses'),
birthdate: DS.attr('date'),
gender: DS.attr('boolean')
});
The detail record, app/models/emailaddress:
import DS from 'ember-data';
export default DS.Model.extend({
account: DS.belongsTo('account'),
emailaddress: DS.attr('string'),
verificationcode: DS.attr('string'),
isverified: DS.attr('string')
});
The dummy JSON string from the server:
{"id":0,"username":"ikevin","birthdate":"Jan 30, 2017 1:34:38
PM","gender":true,"emailaddresses":[{"id":0,"emailaddress":"aaa#bbb.com","verificationcode":"AAAAAA","isverified":false}]}
The adapter /app/adapters/account.js
import ApplicationAdapter from './application';
export default ApplicationAdapter.extend({
urlForQueryRecord(query) {
if (query.profile) {
delete query.profile;
return `${this._super(...arguments)}/profile`;
}
return this._super(...arguments);
}
});
The route app/route/index.js:
import Ember from 'ember';
import RSVP from 'rsvp';
export default Ember.Route.extend({
model() {
return RSVP.hash({
walletbalance: this.get('store').queryRecord('wallet', {balance: true}),
instagramfriendscount: this.get('store').queryRecord('instagram', {friendscount: true}),
accountprofile: this.get('store').queryRecord('account', {profile: true})
});
}
});
And the app/templates/components/account-profile.hbs:
<div class="box">
<div class="title">Your Profile</div>
<div>Username: {{account.accountprofile.username}}</div>
<div>Email Address: {{account.accountprofile.emailaddess}}</div>
<div>Birthdate: {{account.accountprofile.birthdate}}</div>
<div>Gender: {{account.accountprofile.gender}}</div>
</div>
I think there are 2 problems here:
In the Chrome Ember plugin, the data for model type "emailaddress" is always 0. So, that means it is not loaded.
In the app/templates/components/account-profile.hbs, {{account.accountprofile.emailaddess}}is not referring to the correct field. Note: for now it is expected to display only 1 email address.
How do I resolve these problems to load and display nested records?
Thanks!
Yes, I resolved it myself:
I changed it to Nested Arrays (as specified here: http://thejsguy.com/2016/01/29/working-with-nested-data-in-ember-data-models.html)
So the string returned from the server becomes:
{"id":0,"username":"ikevin","birthdate":"Jan 30, 2017 2:01:14
PM","gender":true,"emailaddresses":[{"emailaddress":"aaa#bbb.com","verificationcode":"AAAAAA","isverified":false}]}
And in the .hbs:
<div>Email Address: {{account.accountprofile.emailaddresses.0.emailaddress}}</div>
It displays the email address!
Thanks!
I have this router.js:
import Ember from 'ember';
import config from './config/environment';
var Router = Ember.Router.extend({
location: config.locationType
});
Router.map(function() {
this.route('analyses', function() {
this.route('new', { path: 'new'});
this.route('show', { path: ':analysis_id' });
this.route('edit', { path: ':analysis_id/edit'});
this.route('dataFunctions', { path: ':analysis_id/dataFunctions', resetNamespace: true }, function() {
this.route('new', { path: 'new'});
});
});
export default Router;
and these 2 models
import DS from 'ember-data';
export default DS.Model.extend({
name: DS.attr('string'),
dataFunctions: DS.hasMany('dataFunction', {async: true}),
});
and
import DS from 'ember-data';
export default DS.Model.extend({
name: DS.attr('string'),
analysis: DS.belongsTo('analysis', {async: true})
});
The contents of routes/data-functions/index.js:
import Ember from 'ember';
export default Ember.Route.extend({
model() {
console.log(this.store.findRecord("analysis", id).get("dataFunctions"));
}
});
The contents of routes/analyses/index.js:
import Ember from 'ember';
export default Ember.Route.extend({
model() {
return this.store.findAll("analysis");
},
setupController(controller, model) {
controller.set("analyses", model);
}
});
The contents of routes/analyses/show.js:
import Ember from 'ember';
export default Ember.Route.extend({
model(params) {
return this.store.findRecord('analysis', params.analysis_id);
},
setupController(controller, model) {
controller.set("analysis", model);
}
});
When I navigate to /analyses/1/dataFunctions my analysis model is loaded (it is show in ember inspector) but I can’t seem to access it in my data-functions/index.js route. How do I go about this? I need the analysis model to extend findAll in my data-function adapter to change the url for a rails-api nested resource.
I tried using this.store.modelFor("analysis").get("id") but it errors saying get is not a funcion.
I am using Ember 2.0.1 and Ember Data 2.0.0. I am lost here, any help would be greatly appreciated.
It's returning no mode found because you're returning a log statement in the dataFunctions route. Give this a try.
export default Ember.Route.extend({
model(params) {
return this.store.findRecord("analysis", params.analysis_id)
.then( (analysis) => {
return analysis.get('dataFuncitons');
})
}
});
Ok, so went through the code there was a few issues. There was a typo in analysis, and the resetNamespace is making things act weird. Also removed some of the redundant path names.
Router.map(function() {
this.route('analysis', function() {
this.route('new');
this.route('show', { path: ':analysis_id' });
this.route('edit', { path: ':analysis_id/edit'});
this.route('dataFunctions', { path: ':analysis_id/dataFunctions'}, function() {
this.route('new');
});
});
});
Rename the dataFunctions model to data-function to reflect proper conventions, e.g. using singular and dasherizing.
The analysis model
export default DS.Model.extend({
name: DS.attr('string'),
dataFunctions: DS.hasMany('data-function', {async: true}),
});
The data-function model
export default DS.Model.extend({
name: DS.attr('string'),
analysis: DS.belongsTo('analysis', {async: true})
});
In an Ember 1.13.3 application I have this simple model :
export default DS.Model.extend({
name: DS.attr('string'),
description: DS.attr('string'),
link: DS.attr('string'),
acquired_skills: DS.hasMany('users', { async: true, inverse: 'acquired_skills' } ),
searched_skills: DS.hasMany('users', { async: true, inverse: 'searched_skills' } )
});
And I have this route :
import Ember from 'ember'
export default Ember.Route.extend({
model: function() {
console.log(this.store.find('skill', 1).get('name'));
return this.store.find('skill');
}
});
A request is sent at /skills/1 and this is the result :
{"skill":{"id":1,"name":"Ember","description":"JS Framework","acquired_skills":[1],"searched_skills":[1]}}
In the console, 'Ember' should be written but I have undefined.
Why I have no value for the name of the skill?
I have the same behaviour for all models and attributes.
This returns a promise:
this.store.find('skill', 1)
When you do .get('name'), it hasn't finished doing the ajax request. Instead try this:
this.store.find('skill', 1).then(function(skill){
console.log(skill.get('name'));
});
I'm trying to retrieve all the layouts for a given account.
/app/models/account.js
import DS from 'ember-data';
export default DS.Model.extend({
companyName: DS.attr('string'),
layouts: DS.hasMany('layout')
});
/app/models/layout.js
import DS from 'ember-data';
export default DS.Model.extend({
name: DS.attr('string'),
account: DS.belongsTo('account', { async: true })
});
/app/routes/layouts.js
import Ember from 'ember';
export default Ember.Route.extend({
model: function() {
return Ember.RSVP.hash({
layouts: this.store.filter('layout', { account_id: 1 }, function(layout) {
console.log(layout.get('account').content.id);
return layout.get('account').content.id === 1;
})
});
}
});
The console.log line is outputting the ID that I'm expecting (1). In Ember inspector I can see 5 layout models and under 'Belongs To' I can see: account : <DS.PromiseObject:ember960>. Clicking that brings up content : <batmics#model:account::ember600:1> and clicking that brings up the properties, including the correct ID.
But in my templates layouts is empty... and I've no idea why.
Incidentally, layouts: this.store.find('layout', { account_id: 1 }) works, but I need it to use the filter so that it's an active array.
Ember Data works with all its IDs as strings.
Changing your check to === '1' should get this going for you.
import Ember from 'ember';
export default Ember.Route.extend({
model: function() {
return Ember.RSVP.hash({
layouts: this.store.filter('layout', { account_id: 1 }, function(layout) {
console.log(layout.get('account').content.id);
return layout.get('account').content.id === '1';
})
});
}
});
I want to load multiple models on the same route. Right now, I am doing it using setupController. Basically, I am calling the server to fill up the models for conversations, subusers and currentsubuser.
Subusers returns a json array that is correctly loaded into the store.
The call for currentsubuser returns good json from the server (as shown here : http://gyazo.com/8edb741ba4638d3687f24323c171e7ab), but is not loaded correctly into the store (as shown here : http://gyazo.com/e3f1d2c1f404c29df38f5e236f4e55ab) unlike the subusers.
As you can see, I am using the very same code for loading the subusers and currentuser, but I don't know why the datas from currentsubuser can't be loaded into the store.
The models for currentsubuser and subuser are identical, they contain the exact same properties (FYI I tried sideloading the currentsubuser into the same payload as subusers without success so I created its own identical model):
App.Subuser = DS.Model.extend({
name: DS.attr('string'),
email: DS.attr('string'),
conversations: DS.hasMany('conversation'),
openedConvCount: DS.attr('number'),
profileImage: DS.attr('string'),
});
App.Currentsubuser = DS.Model.extend({
name: DS.attr('string'),
email: DS.attr('string'),
conversations: DS.hasMany('conversation'),
openedConvCount: DS.attr('number'),
profileImage: DS.attr('string'),
});
Route :
App.ConversationsRoute = Ember.Route.extend({
subusers: null,
currentsubuser: null,
model: function(params){
return this.store.find('conversation', { status : params.status});
},
setupController: function(controller, model){
this._super(controller, model);
if(!this.get('subusers') && !this.get('currentsubuser'))
{
this.set('subusers', this.store.findAll('subuser'));
this.set('currentsubuser', this.store.find('currentsubuser'));
}
this.controllerFor('subusers').set('content', this.get('subuser'));
this.controllerFor('currentsubuser').set('content', this.get('currentsubuser'));
},
queryParams: {
status: {
refreshModel: true
}
}
});
Router :
App.Router.map(function(){
//Routing list to raw namespace path
this.resource('conversations', { path : '/' }, function() {
this.resource('conversation', { path : '/:conversation_id'});
});
});
The call this.store.find('currentsubuser') expects json array with currentsubusers as root (not currentsubuser). That's why response is not loaded into the store.