My server uses sensorID as the primary key on my Sensor model. I've tried the following
App.SensorSerializer = DS.RESTSerializer.extend({
primaryKey: "sensorID"
});
based on what I see in this test, but it's not working. I'm getting an error:
Error while loading route: Error: No model was found for '0'
I'm using a custom adapter. The response is JSONP:
jQuery203041337518650107086_1397489458691([{"sensorID":1,"address":"XXX, YYY","latitude":"nnnn","longitude":"mmmm"...
but when I inspect the data that gets returned, it's a normal array:
// App.SensorAdapter
findAll: function(store, type, sinceToken) {
var url = 'http://blahblahblah/?callback=?';
var query = { since: sinceToken };
return new Ember.RSVP.Promise(function(resolve, reject) {
jQuery.getJSON(url, query).then(function(data) {
debugger;
// data.forEach(function(s) {
// s.id = +s.sensorID;
// });
Ember.run(null, resolve, data);
}, function(jqXHR) {
jqXHR.then = null; // tame jQuery's ill mannered promises
Ember.run(null, reject, jqXHR);
});
});
What is the correct syntax for Ember Data 1.0.0-beta.7?
Try this:
App.Adapter.map('App.Sensor', {
primaryKey: 'sensorID'
});
Related
I'm trying to learn more about creating custom adapters in Ember so I started one where I want to create and update records in sessionStorage. I have the createRecord method working, but in the updateRecord method, the id is always null. Any idea why? For the id of models, I am just using a timestamp.
export default DS.Adapter.extend({
createRecord(store, type, snapshot) {
var data = this.serialize(snapshot, { includeId: true });
return new Ember.RSVP.Promise((resolve, reject) => {
var comments = {}, id = Date.now();
if (window.sessionStorage.getItem('comment')) {
comments = JSON.parse(window.sessionStorage.getItem('comment'));
}
data.id = id;
comments[id] = data;
window.sessionStorage.setItem('comment', JSON.stringify(comments));
resolve(data);
});
},
updateRecord(store, type, snapshot) {
var data = this.serialize(snapshot, { includeId: true });
var id = snapshot.id;
console.log('updating', id); // id is null. Why???
return new Ember.RSVP.Promise((resolve, reject) => {
resolve(data);
});
}
});
I am aware that I need to create a few other methods like findAll(), findRecord() etc, but would not having those cause this issue? I'm not sure what I am missing. Thanks in advance!
I'm loading a route. Its model hook loads some models. Some are fetch from ember store and some are promises requested through AJAX:
model: function () {
return Em.RSVP.hash({
//the server data might not be loaded if user is offline (application runs using appcache, but it's nice to have)
someServerData: App.DataService.get(),
users: this.store.find('user')
});
}
The App.DataService.get() is defined as:
get: function () {
return new Ember.RSVP.Promise(function(resolve, reject) {
//ajax request here
});
}
Obviously if the request is rejected, the flow is interrupted and I cannot display the page at all.
Is there a way to overcome this?
Ember.RSVP.hashSettled is exactly meant for this purpose.
From tildeio/rsvp.js Github repository:
hashSettled() work exactly like hash(), except that it fulfill with a hash of the constituent promises' result states. Each state object will either indicate fulfillment or rejection, and provide the corresponding value or reason. The states will take one of the following formats:
{ state: 'fulfilled', value: value }
or
{ state: 'rejected', reason: reason }
Here is an example for using it (working JS Bin example):
App.IndexRoute = Ember.Route.extend({
fallbackValues: {
firstProperty: null,
secondProperty: null
},
model: function() {
var fallbackValues = this.get('fallbackValues');
return new Ember.RSVP.Promise(function(resolve, reject) {
Ember.RSVP.hashSettled({
firstProperty: Ember.RSVP.Promise.resolve('Resolved data despite error'),
secondProperty: (function() {
var doomedToBeRejected = $.Deferred();
doomedToBeRejected.reject({
error: 'some error message'
});
return doomedToBeRejected.promise();
})()
}).then(function(result) {
var objectToResolve = {};
Ember.keys(result).forEach(function(key) {
objectToResolve[key] = result[key].state === 'fulfilled' ? result[key].value : fallbackValues[key];
});
resolve(objectToResolve);
}).catch(function(error) {
reject(error);
});
});
}
});
fallbackValues can be useful for managing resolved hash's properties' fallback values without using conditions inside the promise function.
Taking into account that Ember.RSVP.hashSettled is not available in my Ember version. I come up with the following solution:
model: function(params) {
var self = this;
return new Em.RSVP.Promise(function(resolve, reject){
// get data from server
App.DataService.get().then(function(serverData) { //if server responds set it to the promise
resolve({
serverData: serverData,
users: self.store.find('user')
});
}, function(reason){ //if not ignore it, and send the rest of the data
resolve({
users: self.store.find('user')
});
});
});
}
I want to have a dynamic segment path in Ember without using the :id attribute
As per the Ember Guides, I'm using the serialize method to achieve this.
Heres my Router:
App.Router.map(function() {
this.resource("orders", function(){
this.resource('order', { path: ':order_sequence'}, function(){
this.route('edit');
})
});
});
And my Route:
var OrderRoute = Ember.Route.extend({
model: function(params) {
return this.store.find('order', params.order_sequence)
},
serialize: function(model) {
return { order_sequence: model.get('sequence') };
}
});
module.exports = OrderRoute;
However, my URL's still behave using the id attribute in the path instead of the sequence attribute..
Any ideas?
Is your browser caching something, because that's correct. Are you passing in the id instead of the sequence/model in any of your transitionTo/transitionToRoute/link-to?
Oh, you aren't talking about the slug in the url, nor the route, you are talking about the id of your model. You need to create a serializer for that particular model and override the primary key
App.OrderSerializer = DS.RESTSerializer.extend({
primaryKey: 'sequence'
});
The Fixture Adapter has a constraint on defining the id, but you can lazily get around it by extending the fixture adapter and overriding a single method
App.OrderAdapter = DS.FixtureAdapter.extend({
fixturesForType: function(type) {
if (type.FIXTURES) {
var fixtures = Ember.A(type.FIXTURES);
return fixtures.map(function(fixture){
// aka we massasge the data a bit here so the fixture adapter won't whine so much
fixture.id = fixture.sequence;
var fixtureIdType = typeof fixture.id;
if(fixtureIdType !== "number" && fixtureIdType !== "string"){
throw new Error(fmt('the id property must be defined as a number or string for fixture %#', [fixture]));
}
fixture.id = fixture.id + '';
return fixture;
});
}
return null;
},
});
Hello I am fairly new with ember and exploring it, I have been able to do a simple post to a resource, nevertheless it render my object like this
{"person":{"atribute1":"jjj","atribute2":"jjj"}}
Is there a way to remove the "login" like a custom serializer, my endpoint work by passing an object in the form of
{"atribute1":"jjj","atribute2":"jjj"}
Thanks.
The only solution I could find is override the createRecord, before I had
data[root] = this.serialize(record, { includeId: true });
I removed the index of root and got this instead:
App.Store = DS.Store.extend({
revision: 11,
adapter : 'App.CustomAdapter'
});
App.CustomAdapter = DS.RESTAdapter.extend({
createRecord: function(store, type, record) {
var root = this.rootForType(type);
var data = {};
data = this.serialize(record, { includeId: true });
this.ajax(this.buildURL(root), "POST", {
data: data,
context: this,
success: function(json) {
Ember.run(this, function(){
if ( this.rootForType(type) == 'login' ) {
return;
}
this.didCreateRecord(store, type, record, json);
});
},
error: function(xhr) {
//HERE to handle login operation failed
this.didError(store, type, record, xhr);
}
});
}
});
Maybe a attribute like withRoot or something similar might be required.
I have two models:
App.User = DS.Model.create({
comments: DS.hasMany('App.Comment')
});
App.Comment = DS.Model.create({
user: DS.belongsTo('App.User')
});
When a user is deleted, it also will delete all its comments on the backend, so I should delete them from the client-side identity map.
I'm listing all the comments on the system from another place, so after deleting a user it would just crash.
Is there any way to specify this kind of dependency on the association? Thanks!
I use a mixin when I want to implement this behaviour. My models are defined as follows:
App.Post = DS.Model.extend(App.DeletesDependentRelationships, {
dependentRelationships: ['comments'],
comments: DS.hasMany('App.Comment'),
author: DS.belongsTo('App.User')
});
App.User = DS.Model.extend();
App.Comment = DS.Model.extend({
post: DS.belongsTo('App.Post')
});
The mixin itself:
App.DeletesDependentRelationships = Ember.Mixin.create({
// an array of relationship names to delete
dependentRelationships: null,
// set to 'delete' or 'unload' depending on whether or not you want
// to actually send the deletions to the server
deleteMethod: 'unload',
deleteRecord: function() {
var transaction = this.get('store').transaction();
transaction.add(this);
this.deleteDependentRelationships(transaction);
this._super();
},
deleteDependentRelationships: function(transaction) {
var self = this;
var klass = Ember.get(this.constructor.toString());
var fields = Ember.get(klass, 'fields');
this.get('dependentRelationships').forEach(function(name) {
var relationshipType = fields.get(name);
switch(relationshipType) {
case 'belongsTo': return self.deleteBelongsToRelationship(name, transaction);
case 'hasMany': return self.deleteHasManyRelationship(name, transaction);
}
});
},
deleteBelongsToRelationship: function(name, transaction) {
var record = this.get(name);
if (record) this.deleteOrUnloadRecord(record, transaction);
},
deleteHasManyRelationship: function(key, transaction) {
var self = this;
// deleting from a RecordArray doesn't play well with forEach,
// so convert to a normal array first
this.get(key).toArray().forEach(function(record) {
self.deleteOrUnloadRecord(record, transaction);
});
},
deleteOrUnloadRecord: function(record, transaction) {
var deleteMethod = this.get('deleteMethod');
if (deleteMethod === 'delete') {
transaction.add(record);
record.deleteRecord();
}
else if (deleteMethod === 'unload') {
var store = this.get('store');
store.unloadRecord(record);
}
}
});
Note that you can specify via deleteMethod whether or not you want to send the DELETE requests to your API. If your back-end is configured to delete dependent records automatically, then you will want to use the default.
Here's a jsfiddle that shows it in action.
A quick-and-dirty way would be to add the following to your user model
destroyRecord: ->
#get('comments').invoke('unloadRecord')
#_super()
I adapted the answer of #ahmacleod to work with ember-cli 2.13.1 and ember-data 2.13.0. I had an issue with nested relationships and the fact that after deleting an entity from the database its id was reused. This lead to conflicts with remnants in the ember-data model.
import Ember from 'ember';
export default Ember.Mixin.create({
dependentRelationships: null,
destroyRecord: function() {
this.deleteDependentRelationships();
return this._super()
.then(function (model) {
model.unloadRecord();
return model;
});
},
unloadRecord: function() {
this.deleteDependentRelationships();
this._super();
},
deleteDependentRelationships: function() {
var self = this;
var fields = Ember.get(this.constructor, 'fields');
this.get('dependentRelationships').forEach(function(name) {
self.deleteRelationship(name);
});
},
deleteRelationship (name) {
var self = this;
self.get(name).then(function (records) {
if (!records) {
return;
}
var reset = [];
if (!Ember.isArray(records)) {
records = [records];
reset = null;
}
records.forEach(function(record) {
if (record) {
record.unloadRecord();
}
});
self.set(name, reset);
});
},
});
Eventually, I had to set the relationship to [] (hasMany) or null (belongsTo). Else I would have run into the following error message:
Assertion Failed: You cannot update the id index of an InternalModel once set. Attempted to update <id>.
Maybe this is helpful for somebody else.