Loading associated resources in Ember - ruby-on-rails

I'm working on my second Ember project, and first using Rails on the backend. I'm struggling with loading associated data through my API in a nested route. The association is simple: a folder has many media_files in my backend (I'm aware snake case is against convention but trying to work around it).
When I do the following, I get the correctly nested route (folders/show/media_files) and no complaints when loading the template, but the data is empty in the console and doesn't render in the template.
Thank you for your time.
Here is my routing:
Router.map(function() {
this.route('folders', function(){
this.route('show', {
path: ':folder_id'
}, function() {
this.route('media_files', {resetNamespace: true}, function (){
});
});
});
});
Here are my associations:
Folder:
import DS from 'ember-data';
export default DS.Model.extend({
name: DS.attr('string'),
media_files: DS.hasMany('media_file')
});
Media Files:
import DS from 'ember-data';
export default DS.Model.extend({
name: DS.attr('string'),
duration: DS.attr('number'),
createdAt: DS.attr('date'),
folder: DS.belongsTo('folder')
});
Here is the call to the media files index:
import Ember from 'ember';
export default Ember.Route.extend({
model: function(){
return this.modelFor("folders/show").get("media_files");
}
});
My URL is folders/folder_id/media_files, same as in my API. The JSON there looks like this:
{
media_files: [
{
id: 513009,
url: null,
project_id: 999,
batch_id: 1268,
duration: 30556,
rush: false.....

Related

Accessing Ember data has-many relationship on template

I have a rails back-end, serving data as per the JSON API standards (jsonapi-resources gem). I have two models: Contact and PhoneNumber.
class Contact < ApplicationRecord
has_many :phone_numbers
end
class PhoneNumber < ApplicationRecord
belongs_to :contact
end
This is the JSON response for the API endpoint.
{
"data": [
{
"id": "6",
"type": "phone-numbers",
"links": {
"self": "http://localhost:3000/phone-numbers/6"
},
"attributes": {
"name": "byeworld",
"phone-number": "9212098"
},
"relationships": {
"contact": {
"links": {
"self": "http://localhost:3000/phone-numbers/6/relationships/contact",
"related": "http://localhost:3000/phone-numbers/6/contact"
}
}
}
}
]
}
These are my Ember models:
for contact:
import DS from 'ember-data';
export default DS.Model.extend({
nameFirst: DS.attr('string'),
nameLast: DS.attr('string'),
email: DS.attr('string'),
twitter: DS.attr('string'),
phoneNumbers: DS.hasMany('phone-number', {async: true, inverse: 'contact'})
});
for phone-number:
import DS from 'ember-data';
export default DS.Model.extend({
name: DS.attr('string'),
phoneNumber: DS.attr('string'),
contact: DS.belongsTo('contact', {async: true, inverse: 'phoneNumbers'})
});
This is my route handler:
import Ember from 'ember';
export default Ember.Route.extend({
model(params) {
return this.store.findRecord('contact', params.contact_id, {include: "phone-numbers"});
},
(...)
});
I am unable to access the phone-numbers for a contact on the template by doing:
{{#each model.phoneNumbers as |phone|}}
{{phone.name}}
{{/each}}
Also, the data for the phone-numbers exists in the ember console. What am I missing?
There is open known issue in ember-data version 2.14.3. so please downgrade your ember-data version to 2.13.2 . that might solve your issue.
Refer this ember-data open issue - https://github.com/emberjs/data/issues/4942
model.PhoneNumbers => model.phoneNumbers
If this is not just this typo, try extending your model serializer / adapter from the JSON API adapters / serializers (or do it on you application serializer / adapter if all your models use it):
import DS from 'ember-data';
export default DS.JSONAPIAdapter.extend({});
and:
import JSONAPISerializer from 'ember-data/serializers/json-api';
export default JSONAPISerializer.extend({});

Ember.js Error | Cannot read property 'some' of undefined

I'm using Ember for the front end and I am doing basic testing to see if I can properly render my data before adding components. I have two resources 'Topics' and 'Ratings' and I have added both a route and a model hook for these resources. When I type http://localhost:4200/topics, I am able to see all of the topics being rendered on the template. However, when I type http://localhost:4200/ratings, I receive an error on the console saying:
ember.debug.js:32096TypeError: Cannot read property 'some' of undefined
at error (route.js:21)
at Object.triggerEvent (ember.debug.js:28580)
at Object.trigger (ember.debug.js:53473)
at Object.Transition.trigger (ember.debug.js:53287)
at ember.debug.js:53107
at tryCatch (ember.debug.js:53806)
at invokeCallback (ember.debug.js:53821)
at publish (ember.debug.js:53789)
at publishRejection (ember.debug.js:53724)
at ember.debug.js:32054
Which is strange because in my rails console, I am receiving a HTTP: 200 response. Is there some error within the code of my routes? I made sure to mirror ratings similar to topics. Or is this an association issue? Both a USER and a TOPIC have many ratings. I provided snippets of my code below:
Application Route:
import Ember from 'ember';
export default Ember.Route.extend({
auth: Ember.inject.service(),
flashMessages: Ember.inject.service(),
actions: {
signOut () {
this.get('auth').signOut()
.then(() => this.transitionTo('sign-in'))
.then(() => {
this.get('flashMessages').warning('You have been signed out.');
})
.catch(() => {
this.get('flashMessages')
.danger('There was a problem. Are you sure you\'re signed-in?');
});
this.store.unloadAll();
},
error (reason) {
let unauthorized = reason.errors.some((error) =>
error.status === '401'
);
if (unauthorized) {
this.get('flashMessages')
.danger('You must be authenticated to access this page.');
this.transitionTo('/sign-in');
} else {
this.get('flashMessages')
.danger('There was a problem. Please try again.');
}
return false;
},
},
});
Rating Model:
import Model from 'ember-data/model';
import attr from 'ember-data/attr';
import { belongsTo } from 'ember-data/relationships';
export default Model.extend({
score: attr('number'),
user: belongsTo('user'),
topic: belongsTo('topic')
});
Rating Route:
import Ember from 'ember';
export default Ember.Route.extend({
model(params) {
return this.get('store').findRecord('rating', params.id);
},
});
```
Ratings Route:
import Ember from 'ember';
export default Ember.Route.extend({
model() {
return this.get('store').findAll('rating');
},
});
Router:
import Ember from 'ember';
import config from './config/environment';
const Router = Ember.Router.extend({
location: config.locationType,
});
Router.map(function () {
this.route('sign-up');
this.route('sign-in');
this.route('change-password');
this.route('users');
this.route('topics');
this.route('topic', { path: '/topics/:id'});
this.route('ratings');
this.route('rating', { path: '/ratings/:id'});
// Custom route in topics controller that will call NYT API or generate random-show
//topic. This is a GET request essentially
this.route('random-show');
});
export default Router;
SOLVED! Read the DOCS, and used EXPLICIT INVERSNESS:
https://guides.emberjs.com/v2.5.0/models/relationships/
Apparently, Ember needs help understanding when you have multiple has Many or Belong to for the same type.

Handlebars template won't render ember data object

I've built an ember front-end app that consumes an API made with rails in another application. The ember app is successfully requesting and receiving data from the rails api and one of my handlebars template (the index page that displays a list of all of the Graduates from my Graduates model) is working fine. The page meant to display individual graduates however is not able to render data about those individual graduates. Although when I open the ember tool in the developer console in my browser (Chrome), that data is present.
I'm new to ember and I've been trying to solve this for 2 days but am totally stumped, any help would be greatly appreciated!
app/adapters/application.js:
import DS from 'ember-data';
export default DS.ActiveModelAdapter.extend({
namespace: 'api/v1',
host: 'http://localhost:3000'
});
app/models/graduate.js:
import DS from 'ember-data';
export default DS.Model.extend({
firstName: DS.attr('string'),
lastName: DS.attr('string'),
cohort: DS.attr('string'),
currentJob: DS.attr('string'),
bio: DS.attr('string'),
news: DS.attr('string'),
website: DS.attr('string'),
picture: DS.attr('string'),
createdAt: DS.attr('date'),
updatedAt: DS.attr('date')
});
routes/graduates/index.js:
import Ember from 'ember';
export default Ember.Route.extend({
model: function() {
return this.store.find('graduate');
}
});
routes/graduate.js:
import Ember from 'ember';
export default Ember.Route.extend({
model: function(params) {
return this.store.find('graduate', params.graduate_id);
}
});
app/router.js:
import Ember from 'ember';
import config from './config/environment';
var Router = Ember.Router.extend({
location: config.locationType
});
export default Router.map(function() {
this.resource('graduates', function() {
this.resource('graduate', { path: '/:graduate_id' });
});
});
environment.js:
/* jshint node: true */
module.exports = function(environment) {
var ENV = {
contentSecurityPolicy: {
'connect-src': "'self' http://localhost:3000",
'default-src': "'none' http://localhost:3000",
'script-src': "'self'",
'font-src': "'self' http://localhost:3000",
'img-src': "'self' http://localhost:3000",
'style-src': "'self' http://localhost:3000 'unsafe-inline",
'media-src': "'self' http://localhost:3000"
},
modulePrefix: 'flatbook-front',
environment: environment,
baseURL: '/',
locationType: 'auto',
EmberENV: {
FEATURES: {
// Here you can enable experimental features on an ember canary
build
// e.g. 'with-controller': true
}
},
APP: {
// Here you can pass flags/options to your application instance
// when it is created
}
};
if (environment === 'development') {
// ENV.APP.LOG_RESOLVER = true;
// ENV.APP.LOG_ACTIVE_GENERATION = true;
// ENV.APP.LOG_TRANSITIONS = true;
// ENV.APP.LOG_TRANSITIONS_INTERNAL = true;
// ENV.APP.LOG_VIEW_LOOKUPS = true;
}
if (environment === 'test') {
// Testem prefers this...
ENV.baseURL = '/';
ENV.locationType = 'none';
// keep test console output quieter
ENV.APP.LOG_ACTIVE_GENERATION = false;
ENV.APP.LOG_VIEW_LOOKUPS = false;
ENV.APP.rootElement = '#ember-testing';
}
if (environment === 'production') {
}
return ENV;
};
app/templates/graduate.hbs:
<h1>{{firstName}} {{lastName}}</h1>
Should be
// app/templates/graduate.hbs
<h1>{{model.firstName}} {{model.lastName}}</h1>
Your template
<h1>{{firstName}} {{lastName}}</h1>
shows firstName and lastName properties from current controller (class Ember.Controller), but they are not set.
See the history of question here: http://emberjs.com/deprecations/v1.x/#toc_objectcontroller.

Ember.js route is not performing a new request when params change

I'm working in my first web app with Ember.js backed with a Rails for API.
I have the following nested resources:
this.resource('selection_processes', function() {
this.resource('selection_process', { path: '/:selection_process_id' }, function() {
this.resource('candidate', { path: '/candidates/:candidate_id' });
});
})
So, when I access selection_processes/1 it's getting all of its candidates. Thats ok, but the problem is when I click on another selection process link Ember does not perform a new request, rendering no data in my templates. Btw, the API is returning the correct objects.
The only way I got this working was including all objects in my serializers, making Ember getting all the data of the whole nested resources in a single request. But this seems to be a lazy practice and "heavy".
By the way, here are my routes:
Safira.SelectionProcessesRoute = Ember.Route.extend({
model: function () {
return this.store.find('selection_process');
}
});
Safira.SelectionProcessRoute = Ember.Route.extend({
model: function (params) {
return this.store.find('selection_process', params.selection_process_id);
}
});
Safira.CandidateRoute = Ember.Route.extend({
model: function (params) {
return this.store.find('candidate', params.candidate_id);
}
});
UPDATE
Here are my models
Safira.SelectionProcess = DS.Model.extend({
beginDate : DS.attr('date'),
endDate : DS.attr('date'),
title : DS.attr('string'),
description : DS.attr('string'),
steps : DS.hasMany('Safira.Step', {async: true})
});
Safira.Step = DS.Model.extend({
realization: DS.attr('date'),
title: DS.attr('string'),
candidates: DS.hasMany('Safira.Candidate', {async: true}),
selection_process : DS.belongsTo('Safira.SelectionProcess')
});
Safira.Candidate = DS.Model.extend({
name : DS.attr('string'),
email : DS.attr('string'),
confirmation_token : DS.attr('string'),
step : DS.belongsTo('Safira.Step')
});
you have to pass same data in both cases.(when you get a collection or individual items).
I think you are returning partial data in case of collection.
When you go through a link it assumes it already has data for that model so it won't send a new request. IMO you should be able to access partial data of that model received in collection.
if the model is big, split it into multiple models. it require server side change also.

EmberJS: change url for loading model (ember-data)

I have problems with ember-data. For example, I've created a project at http://localhost/~me/test
In my project I've created a store and a model as follows:
... init stuff here ...
var attr = DS.attr;
App.Person = DS.Model.extend({
firstName: attr('string'),
lastName: attr('string'),
});
App.Store = DS.Store.extend({
revision: 11,
adapter: DS.RESTAdapter,
});
Now when I search (somewhere in my route) for a person like this
var person = App.Person.find(params);
The http://localhost/persons?post_id=10 is called. This one does not exist of course. I would've expected something like http://localhost/~me/test/persons?post_id=10. Even better would be http://localhost/~me/test/persons.php?post_id=10 How can I change this url ?
This is as of Ember Data Beta 3
To take care of the prefix, you can use the namespace property of DS.RESTAdapter. To take care of the suffix, you'll want to customize the buildURL method of DS.RESTAdapter, using _super() to get the original functionality and modifying that. It should look something like this:
App.ApplicationAdapter = DS.RESTAdapter.extend({
namespace: '~me/test',
buildURL: function() {
var normalURL = this._super.apply(this, arguments);
return normalURL + '.php';
}
});
MilkyWayJoe is right, in your adapter you can define the namespace.
App.Adapter = DS.RESTAdapter.extend({
namespace: '~/me/test'
});
This would work too:
App.Person = DS.Model.extend({
url: '~me/test/persons',
firstName: attr('string'),
lastName: attr('string'),
});
Or if you want to use a namespace and .php path:
App.Adapter = DS.RESTAdapter.extend({
namespace: '~/me/test',
plurals: {
"persons.php": "persons.php",
}
});
App.Person = DS.Model.extend({
url: 'persons.php',
firstName: attr('string'),
lastName: attr('string'),
});
The plurals bit is to make sure Ember Data doesn't add an 's', e.g. person.phps

Resources