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

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

Related

Loading associated resources in Ember

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.....

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.

How to access FIXTURES data from the Ember.Handlebars.helper?

This is how I have created my ember FIXTURE:
window.App = Ember.Application.create();
App.ApplicationAdapter = DS.FixtureAdapter;
App.Category = DS.Model.extend({
name: DS.attr(),
parent_id: DS.attr()
});
App.Category.FIXTURES = [
{
id: 1,
name: 'user1',
email: 'user1#gmail.com',
parent_id: 0
},
{
id: 2,
name: 'user2',
email: 'user2#gmail.com',
parent_id: 1
}
];
Here is a part of my ember view where 'parent-title' is a helper:
{{#each category in controller}}
<tr>
<td>{{category.name}}</td>
<td>{{parent-title category.parent_id}}</td>
<td>Edit/Delete</td>
</tr>
{{/each}}
What I want is that during listing if the parent_id is 0 it should print 'master' else the name of parent category. In my exapmle parent of user2 is id=1 show it should print 'user1'.
Below is the helper I have used:
Ember.Handlebars.helper('parent-title', function(parent_id){
if (parent_id > 0) {
var parent = category.findBy('id', parent_id);
return parent.name;
} else {
return 'master';
}
});
I know if I replace the line App.Category.FIXTURES = [ with var Category = [ I can get it done but I want ot do it with FIXTURES.
I can tell you that accessing data like that is a bad idea. If I were you, I would change parent_id to be a relationship, not an attribute (since that's really what it is). Then you can access the parent's name in templates with category.parent.name. Making it a relationship also gives you a few other luxuries.
But if you want to maintain backward compatibility, try using a computed property.
App.Category = DS.Model.extend({
name: DS.attr(),
parent_id: DS.attr(),
parent: function() {
return DS.PromiseObject.create({
promise: this.get('store').find('category', this.get('parent_id'))
});
}.property('parent_id'),
parent_name: function() {
return this.get('parent.name');
}.property('parent.name')
});
EDIT: If you want to change that into a relationship (which I think it should be), it's fairly simple. Instead of DS.attr, you use DS.belongsTo. For instances.
App.Categor = DS.Model.extend({
name: DS.attr(),
// I dropped the _id part because it's no longer an ID
parent: DS.belongsTo('category', { inverse: null })
});
This tells Ember-Data to interpret the ID you give in the parent field as another category object. So category.get('parent') will return another category object, not a number. But in your case, to make it work, you'll have to convert all of the 0 IDs to null IDs. I wasn't sure if that was possible, which is why I recommended the computed property.
EDIT: To display master in case of a null parent, use the Handlebars if expression.
{{#if parent}}
{{parent.name}}
{{else}}
master
{{/if}}

Backbone: do I need to change the update URL for Rails RESTful

I have a simple model in Backbone.js with few configurations:
budget.Group = Backbone.Model.extend({
url: '/groups'
})
...now when I do something like this:
budget.Group.save({ id: 1, name: 'Food' });
... it attempts to PUT it to the following URL:
/groups
.. but my (Rails) app would post/put it to:
/groups/1
Do I need to configure my model to update to the correct url? Thanks
You should be doing
var budget = new budget.Group({ id: 1, name: 'Food' });
budget.save();
Backbone will do the rest for you (and use the correct URL).
As the two answers before mentioned you must instantiate your Model :
var budget = new budget.Group({ id: 1, name: 'Food' });
budget.save();
But you have also to change the url property to urlRoot
budget.Group = Backbone.Model.extend({
urlRoot: '/groups'
})

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.

Resources