I'm starting with Ember, and I wanted to know if its possible to do this.
My server model of a book:
Book = {
name: 'string',
author_id: 'number'
}
But in my Ember side, I wanted to have something like this:
Book = {
name: DS.attr('string'),
author: DS.belongsTo('author' , {via: 'author_id'})
}
Is this possible?
Yes, that's possible. You don't define that on the relationship though, you implement transformation behavior in your serializer. So rather than telling Ember that your server calls that relationship something different, you just convert the relationship to the format Ember wants before it's loaded into the store.
For instance, if you're using the RESTSerializer, you can override the keyForRelationship hook.
App.BookSerializer = DS.RESTSerializer.extend({
keyForRelationship: function(key) {
if (key === 'author') {
return 'author_id';
} else {
return key;
}
}
});
This will tell the serializer to get the data for the author relationship from the author_id field in your JSON. It'll also ensure that when it sends JSON back to your server, it converts the author relationship back to the author_id property when serializing.
If you're not using the RESTSerializer, you can probably find the serializer you're using on the Ember Data API documentation page and your serializer will mostly likely have the same method or a very similar method.
Related
I am using Ember 2 and Ember-data 2 and trying to access the raw json payload the standard RESTAdapter fetches from my REST Api and keeps in store. Can't find anything regarding this in the documentation or elsewhere. Is the only option to create a custom RESTAdapter?
The way that I would do it is to add a separate property to your model, then override your serializer.
First your model:
export default DS.Model.extend({
rawJSON: DS.attr()
// Your other attributes...
});
Your serializer (I'm using the JSONSerializer as an example, but other serializers should be fairly similar):
export default DS.JSONSerializer.extend({
normalize(typeClass) {
// Simulate the extra attribute by adding it to the hash
hash.rawJSON = JSON.parse(JSON.stringify(json));
// Then let the serializer do the rest
return this._super.apply(this, arguments);
},
serialize(snapshot, options) {
// Let the serializer create the JSON
const json = this._super.apply(this, arguments);
// Remove the extra attribute we added
delete json.rawJSON;
return json;
}
});
You can choose to do this for all of your models by overridding the application serializer, or just a particular model by overriding the serializer for just that type.
I have a model hook that returns a list of reviews from the data store. On that same page, I have a review form. When the form is submitted, I call an action on the controller and I need to call createRecord() on the store. Normally I would pass a created record from a model hook but my model hook is already taken. What is the best approach here?
It would be a lot better to create this record in the model hook and use it across the form, e.g. for making validations if needed. You can use Ember.RSVP.hash for having multiple models fetched in model hook:
model: function() {
data = {
reviews: store.find("review"),
newReview: store.createRecord("review")
};
return Ember.RSVP.hash(data);
}
Than, in your setupController:
setupController: function(controller, model) {
// all your data is in model hash
controller.set("model", model.reviews);
controller.set("newReview", model.newReview);
}
Thanks to that, you would have your newReview object from the beginning in the controller. You can set bindings directly to it in your form and make validations on the fly. It's better way, as it does not need to copy data from the form to the object by hand, but rather take advantage from ember bindings system, like that.
All you would have to do in your action would be:
actions: {
save: function() {
this.get("newReview").save();
}
}
When the form is submitted, I call an action on the controller and I need to call createRecord() on the store. Normally I would pass a created record from a model hook but my model hook is already taken.
I don't know what you mean by "pass a created record from the model hook". Pass from where to where? If you want to create a record, just create it in the action. Then you can update the controller's model with the new record:
// controller
actions: {
addReview() {
function create() { return store.createRecord('review', ...); }
function push(review) { model.pushObject(review); }
var store = this.get('store');
var model = this.get('model');
create() . save() . then(push);
}
}
Or something similar.
If I define the model for an ArrayController in an Ember.Route like this...
model: function() {
return this.modelFor('customer').get('memberInvitation');
},
...then when I create a record using createRecord and save later then model does not show the new objects in the view.
But, when I set the model by filtering records from the store like this:
model: function() {
var customer = this.modelFor('customer');
var memberInvitationIds = customer.get('memberInvitations').mapBy('id');
var idsParam = memberInvitationIds.join(',');
return this.store.filter('member-invitation', {ids: idsParam}, function(memberInvitation) {
return !memberInvitation.get('isNew');
});
},
...then it does update the view.
Is there a right way to do this using the hasMany relationship? If not, what is the right way?
Using the relationship should work fine. Here's why it wasn't.
The relationship was polymorphic, and the parent class did not have the hasMany relationship, so when the memberInvitation was created the hasMany relationship on the customer didn't realize it.
So, if the binding to a relationship seems broken, check the inverse side to ensure that everything looks right (especially if you have a polymorphic relationship).
I have a ember model like this:
var attr = DS.attr,
hasMany = DS.hasMany,
belongsTo = DS.belongsTo;
App.Message = DS.Model.extend({
message: attr(),
user: belongsTo('user'),
inquiry: belongsTo('inquiry')
});
I want to createRecord like this:
var createMessage = this.store.createRecord('message', {
'message': "test message",
'user': 1,
'inquiry': 1
});
createMessage.save();
I know this question has been asked several times but I can't find any clear solution. can someone please describe me why this error happening and what will be the solution.I am using Ember: 1.6.1 and Ember-data: 1.0.0-beta.5
When I use createRecord() with relationships, I manually construct each of the objects. There are a few different ways to do this:
For an example of adding elements to a hasMany relationship, see Ember.js & Ember Data: How to create record with hasMany relationship when the parent and child don't exist yet
Or here's another example when dealing with a belongsTo relationship:
var message = this.store.createRecord('message');
var user = this.store.createRecord('user');
message.set('user', user);
Ultimately, Ember may serialize this DS.Model to only have a number for the id, but internally, it needs an actual link to an actual object.
UPDATE: (credit to #Craicerjackless)
If the user does exist and you have the id:
var user = this.store.find('user', 1).then(function(user){
message.set('user, user);
});
This gets the user by the id "1" and then once the user has been found sets it as the user for the model.
When using the linkTo helper in a handlebars template, Ember sets up the correct URL for the link with the help of the serializer I have added to the route:
serialize: function(slug, params) {
var name, object;
object = {};
name = params[0];
object[name] = slug;
return object;
}
And when I click the link, Ember transitions to the correct page with the correct slug and everything, but it doesn't have the correct data, and it says that. I believe it's because what I pass to my linkTo statement as second parameter is just the slug and not the whole model.
Is it possible to get Ember to simply fetch the data as it would if I just typed the URL into the address bar instead of relying on the model (that is not) passed to the linkTo statement?
UPDATE
I have tried this inside the activate method on my route, but now it seems the problem is that the rendering has to wait until this is done.
activate: function() {
this.context.isLoaded = false;
this.model(this.context.query.slug);
}
Any ideas? Maybe even with a prettier solution?
The solution I came up with at last, with help from some guys on IRC, was to use the setupController hook, like you mention, Darshan, and the serializer like this:
CustomRoute = Ember.Route.extend({
setupController: function(controller, model) {
var modelName = this.routeName.substr(0, 1).toUpperCase() + this.routeName.substr(1),
slug = model;
if (model.hasOwnProperty('slug'))
slug = model.slug;
controller.set('model', App[modelName].find({'slug': slug}));
},
serialize: function(slug, params) {
var name, object;
object = {};
name = params[0];
object[name] = slug;
return object;
}
});
This way, you can supply just the slug of the route as the second parameter to the linkTo helper instead of a model, and the serializer will set the URL up properly, and then the setupController will check if the model has a property slug, which properly means it's a proper model, and if it does not, it just guesses that the model is simply the slug, and then it will use the DS.Model.find method to return a promise to the controllers model store.
Because setupController is called everytime a route is entered, where as the model hook is only called sometimes, the DS.Model.find method will be used everytime to fetch the data via the promise, and voila - fetch data each time you enter a route.
This assumes that you use Ember.Data and that your model object is called App.*route name* starting with a capital letter, but it can easily be modified to fit whatever need one might have.
For all of the routes in my app I now subclass (extend) from this route thus getting my desired behaviour for all of my routes.
You can try using the slug name in the Route and then loading data for the Route using findQuery instead of find.
App.Router.map(function() {
this.resource('product', { path: '/product/:slug' });
});
App.ProductRoute = Ember.Route.extend({
model: function(params) {
return App.Product.query({name:params.slug});
}
});