Ember data createRecord creates multiple records - ember.js

When I inspect Data in ember inspector I see multiple record one with id(int) and one without id(null).
Using ember 2.9.1
save: function(){
var title = this.get('title');
this.store.createRecord('post',{ title:title }).save();
this.setProperties({ title:''});
this.transitionToRoute('posts');
}
Node Backend
router.post('/', function(req, res, next) {
post.create({
title: req.body.data.attributes.title
}).then(function () {
res.set('Content-Type','application/vnd.api+json');
return res.send({
"msg": "post created successfully",
data:null
});
});
});

I'm surprised to see a post in the data store with an id from this operation. You should be returning (assuming a RestAdapter)
router.post('/', function(req, res, next) {
post.create({
title: req.body.data.attributes.title
}).then(function (post) {
res.set('Content-Type','application/vnd.api+json');
return res.status(201).json(post);
});
});
This will return the created post record that contains the id, which in turn will update the store appropriately.
I suspect the null id is from the current save, and the record with an id is from a previous save?

return res.status(201).send({
data:{
type:'post',
attributes:{
id:post.id,
name:post.title
}
}
});
The following code works properly.This is a known issue in ember data.

Related

Why does my Ember computed property on hasMany relationship not update?

I have an Ember Data model as:
export default DS.Model.extend({
name: DS.attr('string'),
friends: DS.hasMany('friend',{async:true}),
requestedFriendIds: Ember.computed('friends',function(){
return this.get('friends').then(function(friends){
return friends.filterBy('status','Requested').mapBy('id');
});
})
});
I have a route setup that uses it:
export default Ember.Route.extend({
model: function(params){
return Ember.RSVP.hash({
memberProfile:this.store.find('member-profile', params.memberprofile_id).then(function(memberProfile)
{
return Ember.RSVP.hash({
requestedFriendIds:memberProfile.get('requestedFriendIds'),
UserId:memberProfile.get('user.id'),
Id:memberProfile.get('id')
});
}),
});
}
});
},
And htmlbars that utilize the route model. My computed property is always correctly called on a reload, but isn't refreshed on a user action 'Add Friend', which changes the store by adding a friend and the profile.friends' record like this:
actions:
{
addFriend:function(profile_id,)
{
this.store.findRecord('member-profile',memberProfile).then(function(member){
var friend = this.store.createRecord('friend',
{
member:member,
status:'Requested',
timestamp: new Date(),
});
friend.save();
member.get('friends').pushObject(friend);
member.save();
}.bind(this));
}
}
Some notes: I've tried the computed property on 'friends','friends.[]'. My code base is Ember 2.0.1, with Ember.Data 1.13.12, and as such 'friends.#each' is deprecated. The underlying data is correctly updated in the backing store (EmberFire). I've debugged into EmberData and I see that the property changed notifications invalidation code is called. This is only a selection of the code...
What am I missing...? Is there a better way to approach this?
I think you should watch friends.[] instead of only friends:
requestedFriendIds: Ember.computed('friends.[]',function(){
return this.get('friends').then(function(friends){
return friends.filterBy('status','Requested').mapBy('id');
});
})
And you could probably put your action in your route and manually refresh model (it might be issue with promise result not binding to changes in CP). So, in your route:
actions: {
addFriend(profile_id) {
this.store.findRecord('member-profile', memberProfile).then(member => {
let friend = this.store.createRecord('friend',
{
member:member,
status:'Requested',
timestamp: new Date()
});
friend.save();
member.get('friends').pushObject(friend);
member.save();
this.refresh();
});
}
}
The most important part is using this.refresh() in Ember.Route.

ember-cli, ember-simple-auth: Cannot re-register: `store:main`

currently I'm changing my project from ember to ember-cli and run in a issue I can't get rid of. I am really not sure where the problem is, since I also updated ember and ember-data.
I get the Error:
Uncaught Error: Cannot re-register: `store:main`, as it has already been resolved.
I'm trying to load a user via the simple-auth Session.
import UserSession from '../session/user';
export default {
name: 'user-session',
before: 'simple-auth',
after: 'store',
initialize: function(container, application) {
container.register('session:user', UserSession);
}
};
and the session:
import Session from 'simple-auth/session';
export default Session.extend({
setup: function(authenticator, content, trigger) {
// Do not let setup trigger
this._super.call(this, authenticator, content, false);
// Lookup user and trigger events ourselves
var store = this.container.lookup('store:main');
console.log(store.find);
var self = this;
store.find('user', content.userId)
.then(function(user) {
self.set('user', user);
self.trigger('sessionAuthenticationSucceeded');
}, function() {
console.log('ERROR: Could not resolve user of session!');
});
}
});
the store.find is there but then the error breaks it.
I also tired to inject the store like this:
Ember-Simple-Auth currentUser example help required but had the same result.
Further I tried to make it via the instance-initalizer for ember-data beta.19.
I do stuff like this:
Session from '../session/user';
export default {
name: 'user-session1',
after: 'ember-data',
initialize: function(container, application) {
var store = container.lookup('store:main');
}
};
but this ends up in:
Uncaught TypeError: container.lookup is not a function
Using:
DEBUG: -------------------------------
ember.debug.js:4874DEBUG: Ember : 1.12.1
ember.debug.js:4874DEBUG: Ember Data : 1.0.0-beta.19.2
ember.debug.js:4874DEBUG: jQuery : 1.11.3
ember.debug.js:4874DEBUG: Ember Simple Auth : 0.8.0
ember.debug.js:4874DEBUG: -------------------------------
Thx for the help
------------------ EDIT --------------------------------------------
I updated my instance-initializers based on #Artych comment where I get the store.
I removed the custom session from simple-auth and tried it with reopening
ENV['simple-auth'] = {
//session: 'session:user', ...
My function:
initialize: function(application) {
var store = application.container.lookup('store:main');
Session.reopen({
setCurrentUser: function() {
console.log('never get here');
var accessToken = this.get('access_token');
var self = this;
if (!Ember.isEmpty(accessToken)) {
//never gets here, doesn't matter if I take other variables
}
}.observes('access_token', 'id', 'userId', 'user_id')
});
}
the problem now is that it never goes into "setCurrentUser". I still can logout and in. my autenticator:
authenticate: function(credentials) {
return new Ember.RSVP.Promise(function(resolve, reject) {
var ttl = 30*60*1000; // Request login for 30 minutes
var data = _.extend(credentials, {ttl: ttl});
Ember.$.ajax({
type: 'POST',
url: ENV.api + '/users/login',
data: data,
dataType: 'json'
})
.then(function(response) {
console.log(response);
Ember.run(null, resolve, response);
}, function(xhr, status, error) {
Ember.run(null, reject, error);
});
});
},
returns:
Object {id: "xI3sPSsgdOiHLd8DcFyuOE42KhbuO8gi8BjWBJRrgHgeCESWoma99C2RtvC6tnxG", ttl: 1800000, created: "2015-07-02T14:00:06.600Z", userId: 1}
As you can see, I added a bunch of observed variables: observes('access_token', 'id', 'userId', 'user_id') which I saw in different other questions but nothings helps.
Any idea on this?
Thx
I Solved it.
Simple don't use Ember Data 1.0.0-beta.19.2. I upgraded to 1.13.4 and I was able to get the store like before.

Trying to use jsonp with ember-data, and unable to use Ember App's store.createRecord in custom adapter

I'm trying to use ember-data with jsonp by overridding DS.RESTAdapter's findAll (based on the answer to this question).
App.ApplicationStore = DS.Store.extend({});
App.Event = DS.Model.extend({
name: DS.attr('string')
});
App.EventAdapter = DS.RESTAdapter.extend({
findAll: function() {
var events = [];
$.ajax({
url: '...',
dataType: 'jsonp',
success: function(response) {
response.results.forEach(function(event) {
events.addObject(App.ApplicationStore.createRecord('event', event));
}, this);
}
});
return events;
}
});
App.EventsRoute = Ember.Route.extend({
model: function() {
return this.store.find('event');
}
});
I first tried using events.addObject(App.Event.create(event)), but ember returned an error: "You should not call create on a model. Instead, call store.createRecord with the attributes you would like to set".
The issue is, App.ApplicationStore.createRecord is undefined, so I'm stuck without a way to instantiate Events. Anyone know what's going on? If there's a completely different approach to getting jsonp to work with ember-data, that's fine too.
This parsing of the response seems more like a job for the RESTSerializer than the RESTAdapter(though you will still need the adapter if you need to set the dataType/url)
Not 100% sure, but it looks like your reponse is an array that doesn't have the correct key
as stated in the jsonapi.org documenation?
If this is the case, you'd want to create a serializer for events like this
App.EventsSerializer = DS.RESTSerializer.extend({
extractFindAll: function(store, type, rawPayload, id, requestType) {
this._super(store, type, { 'events': rawPayload }, id, requestType);
}
});
The above serializer will reformat the response to be ember-data readable(as per the above documentation), and ember-data will take care of the rest
DS.RESTSerializer documentation
As an aside, the current store is passed as the first parameter to DS.RESTAdapter.findAll, so you should access the store through that parameter
<\EDIT>
including DS.RESTAdapter.findall source
kaungst's answer was really helpful, but ember was still throwing an error. It led me to a solution that works, though:
App.EventSerializer = DS.RESTSerializer.extend({
normalizePayload: function(payload) {
return {'events': payload};
}
});
App.EventAdapter = DS.RESTAdapter.extend({
findAll: function(store) {
var events = [];
$.ajax({
url: '...',
dataType: 'jsonp',
success: function(response) {
response.results.forEach(function(event) {
events.addObject(store.createRecord('event', event));
}, this);
}
});
return events;
}
});
I overrode DS.RESTSerializer's normalizePayload instead of extractFindAll, which fixed the subsequent error I was getting. Additionally, I defined App.EventSerializer (singular) instead of App.EventsSerializer.

Duplicate null-id records in ember-data

I'm using ember 1.0 and ember-data 1.0.0 beta 1. I have the following routes and controller to create and save simple notes ('AuthenticatedRoute' is just a custom made route for logged-in users):
App.Note = DS.Model.extend({
title: DS.attr(),
author: DS.attr(),
body: DS.attr(),
createdAt: DS.attr()
});
App.NotesRoute = App.AuthenticatedRoute.extend({
model: function() { return this.store.find('note'); },
});
App.NotesNewRoute = App.AuthenticatedRoute.extend({
model: function() {
return this.store.createRecord('note');
}
});
App.NotesNewController = Ember.ObjectController.extend({
actions: {
save: function() {
var self = this, model = this.get('model');
model.set('author', localStorage.username);
model.set('createdAt', new Date());
model.save().then(function() {
self.get('target.router').transitionTo('notes.index');
});
}
}
});
When I save a new note everything works as expected. But when I navigate away from the notes route and then back into it, the notes list is populated with a duplicate entry. One entry has an id and can be edited, deleted etc, the other has all the data of the first entry except the id attribute is null. It seems to me ember-data keeps the newly created record (that hasn't been committed to the database and thus has no id yet) alive even when the record becomes committed but I am uncertain as to why. When I reload the page, the list is correctly displayed, no duplicates appear. What am I doing wrong?
For the record, I am using mongodb so I use a custom serializer to convert '_id' attributes to ember-data friendly 'id's, essentially copied from here:
App.NoteSerializer = DS.RESTSerializer.extend({
normalize: function(type, hash, property) {
// normalize the '_id'
var json = { id: hash._id };
delete hash._id;
// normalize the underscored properties
for (var prop in hash) {
json[prop.camelize()] = hash[prop];
}
// delegate to any type-specific normalizations
return this._super(type, json, property);
}
});
I should also mention that this problem existed in ember-data 0.13 as well.
It was a stupid mistake in my RESTful server. I was responding to POST requests with a 204 (empty) response instead of what ember-data expected, that is a 201 ("created") response with the newly created record as the payload. This post made me realize it.
It would be nice though to include this information in the official REST adapter documentation.
That is certainly strange behaviour indeed. Unfortunately I'm not able to explain why you're experiencing this, however:
You can use the willTransition callback in the actions object in your Route to ensure that when it is transitioned away from, if NotesNewController's content property is dirty (i.e. has not been persisted yet), it will have its transaction rolled back.
App.NotesNewRoute = App.AuthenticatedRoute.extend({
model: function() {
return this.store.createRecord('note');
},
actions: {
willTransition: function (transition) {
var model = this.controllerFor('notesNew').get('content');
if (model.get('isDirty') === true) {
model.get('transaction').rollback();
}
return this._super(transition);
}
}
});

how to observe changes to a static object?

I'm trying to implement geocoding using Google maps JSON API.
I created a model for the location, and and ObjectController,
the model has the async geocoding logic.
I want the controller to observe changes to the model to reflect the most update data.
I'm trying both with binding and observe and both doesn't work:
App.Location = Ember.Object.extend({
formatted_address: null,
location: {
lat: null,
lng: null
}
})
App.Location.reopenClass({
geocoded: Ember.Object.create(),
geocode: function(address) {
$.ajax({
url: 'http://maps.googleapis.com/maps/api/geocode/json?sensor=false&address=' + address,
dataType: 'json',
context: this,
success: function(response) {
App.Location.geocoded.set('response', response);
}
});
return this.geocoded;
}
});
App.LocationController = Ember.ObjectController.extend({
contentBinding: 'App.Location.geocoded',
testProperty: function() {
console.log('test');
}.property('content'),
testObserve: function() {
console.log('test');
}.observes('App.Location.geocoded')
});
EDIT: changing to observes('App.Location.geocoded.response')fix the issue. can it also work with binding? is there a better way to do this ?
Here when you write contentBinding: 'App.Location.geocoded', the content is updated when App.Location.geocoded change. But in the success handler, you don't change the geocoded object, but just update its response property.
So, if you want to keep the binding with the geocoded object, you can try to do
App.Location.set('geocoded', Ember.Object.create({response: response}));
in the ajax success handler.