Models are not serialized with Ember.js and WebApiAdapter - javascript

I'm trying to use the Ember.js MVC4 Spa Template with my own Models, but I'm not getting this to work.
For now, the serverside code is is working. The result to the browser is correct. But Ember-Data, or the custom WebApi - Serializer, is not able to prepare the Data.
I have two Models:
Patient:
App.Patient = DS.Model.extend();
App.Patient.reopen({
patientId: DS.attr('number'),
firstName: DS.attr('string'),
lastName: DS.attr('string'),
aufenthalte: DS.hasMany('aufenthalt'), //, { async: true }
fullName: function () {
return this.get('firstName') + ' ' + this.get('lastName');
}.property('firstName', 'lastName'),
});
App.PatientSerializer = DS.WebAPISerializer.extend({
primaryKey: 'patientId',
// ember-data-1.0.0-beta2 does not handle embedded data like they once did in 0.13, so we've to update individually if present
// once embedded is implemented in future release, we'll move this back to WebAPISerializer.
// see https://github.com/emberjs/data/blob/master/TRANSITION.md for details
extractArray: function (store, primaryType, payload) {
var primaryTypeName = primaryType.typeKey;
var typeName = primaryTypeName,
type = store.modelFor(typeName);
var data = {};
data[typeName] = payload;
data.aufenthalte = [];
var normalizedArray = payload.map(function (hash) {
hash.aufenthalte.map(function (aufenthalt) {
data.aufenthalte.push(aufenthalt);
});
hash.aufenthalte = hash.aufenthalte.mapProperty('aufenthaltId');
return hash;
}, this);
payload = data;
return this._super.apply(this, arguments);
},
normalizeHash: {
patient: function (hash) {
hash.patientId = hash.id;
return hash;
}
}
});
Aufenthalt:
App.Aufenthalt = DS.Model.extend({
aufenthaltId: DS.attr('number'),
name: DS.attr('string'),
patientId: DS.attr('number'),
patient: DS.belongsTo('patient'),
});
App.AufenthaltSerializer = DS.WebAPISerializer.extend({
primaryKey: 'aufenthaltId',
normalizeHash: {
aufenthalte: function (hash) {
hash.aufenthaltId = hash.id;
return hash;
},
}
});
When I get a List of "Patients" from my Controller, the Datamodels are filled correctly (I can check it in the Chrome Ember plugin.) When I hit a Action with the Patient Id, I get the error: "Error while loading route: TypeError: Cannot set property 'store' of undefined"
Thank You!

Did you added the proper router in app/routes folder, controller in app/controllers folder, and corresponding views and templates? Feel free to psot a link to your sample solution so I can download and have a look.
=== Update 2/22/2014 ===
I fixed the code. You should be able to download the modified solution from https://www.dropbox.com/s/4j3vbczqr4nx68m/EmberVSTemplateModified.zip. You should do a windiff on the two directories to see the changes. I need to change a few places to make it work for your scenario, including:
patient.js, make it directly extend from RESTSerialzer, and add extractSingle implementation.
change template of patsucheautocomplete.hbs
added patient\index.hbs . You should be able to remove patient.hbs file
paitentview.js (may not be necessary as it's all default)
modified controllers\htmlhelperextensions.cs, to make it work correctly for sub folder templates in debug mode.

Related

Ember data store return undefined record from FIXTURES

I am new in ember and i am trying to simply get log of items stored by Fixtures model and i cannot. Here is what I have done:
app.js
App = Ember.Application.create();
App.Store = DS.Store.extend({
adapter: DS.FixtureAdapter.extend()
});
App.Documenter = DS.Model.extend({
firstName: DS.attr('string'),
lastName: DS.attr('string')
});
App.Documenter.FIXTURES = [
{ id: 1, firstName: 'Trek', lastName: 'Glowacki' },
{ id: 2, firstName: 'Tom', lastName: 'Dale' }
];
App.IndexRoute = Ember.Route.extend({
controllerName: 'application',
actions: {
addtostore: function () {
},
displaystore: function () {
var obj = this.store.all('documenter');
console.log(obj.objectAt(0).firstName); //here i get undefined
}
}
});
html:
<script type="text/x-handlebars">
<h2> Ember POC</h2>
<p>POC</p>
{{outlet}}
<button {{action 'displaystore'}}>TestButton</button>
</script>
I've looked at few answers already on stackoverflow:
Ember-Data Fixture Adapter
Ember App Kit with ember data
but still I dont get, why button TestButton dont log into console. I tried many ways but always it is undefined
No need to define the store, and your adapter should be defined like so:
App.ApplicationAdapter = DS.FixtureAdapter;
And all only returns records that have already been found and are in the store. The FIXTURES hash isn't automatically loaded into the store, you still need to use find in order to get records from the FIXTURES hash.
displaystore: function () {
this.store.find('documenter').then(function(records){ // this is async
console.log(records.get('firstObject.firstName'));
});
}
It's doubtful you would want to call find from the action like this, since it'd call back to the server each time, generally you'd get the records in the route (or even prime your store in the route so you can use all).
Because I am fresh in Ember, I made few common mistakes. I did not understand routes, handlebars, etc., and I messed up. The problem was that I did not assign the model to a controller.
App.IndexRoute = Ember.Route.extend({
model: function () {
var store = this.get('store');
return store.findAll('documenter');
}
});

Why are these records not stored in cache?

I would like to cache my records once they are received, but I can't figure out how. According to the Documentation you can just call this.store.push('model', record), but it doesn't seem to work. Ember requests the data from the server with each call of the route, I would like to do this only once and use the local store after it is fetched from the server.
If I try to debug it as suggested by the Documentation, i get that there is no cache:
Pd.__container__.lookup('store:main').recordCache
// --> undefined
This is my route (where I try to cache it):
Pd.ProductsRoute = Ember.Route.extend({
model: function () {
var promise = this.store.find('product');
var that = this;
promise.then(function(value) {
// Caching supposed to happen here
value.content.forEach(function(product){
that.store.push('product', product);
});
}, function(reason) {
// on rejection
});
return promise;
}
});
And this the according Adapter (seems to work fine):
Pd.ProductAdapter = DS.RESTAdapter.extend({
primaryKey: 'nid', // DOES NOT WORK BUT I CAN LIVE WITH THAT (SEE WORKAROUND)
findAll: function(store, type) {
var url = 'ws/rest/products';
return new Ember.RSVP.Promise(function(resolve, reject) {
jQuery.getJSON(url).then(function(data) {
Ember.Logger.debug("Received Products:"); // TRIGGERS EVERY TIME!
var srcPattern = /src=["']([^'"]+)/;
data.forEach(function(product){
product.id = product.nid;
product.field_image = srcPattern.exec(product.field_image)[1];
});
Ember.Logger.debug(data);
Ember.run(null, resolve, {product: data});
}, function(jqXHR) {
jqXHR.then = null; // tame jQuery's ill mannered promises
Ember.run(null, reject, jqXHR);
});
});
}
});
this.store.find('type') will always make a call to the server for records. If you only want to make a call to the server once do it in the ApplicationRoute and then instead of using find use the all filter inside of the route that's hit multiple times.
Pd.ApplicationRoute = Em.Route.extend({
model: function(params){
return Em.RSVP.hash({
product: this.store.find('product'),
somethingElse: otherPromise
})
}
});
Pd.ProductRoute = Em.Route.extend({
model: function(params){
return this.store.all('product');
}
});
If you just want to prep the store with your products, you don't even need to return it, or use it in the app route
Pd.ApplicationRoute = Em.Route.extend({
model: function(params){
this.store.find('product');
return {foo:'bar'}; // or return nothing, it doesn't matter
}
});
Lazy loading the models
App.ProductRoute = Ember.Route.extend({
hasPreLoaded: false,
model: function() {
if(this.get('hasPreLoaded')){
return this.store.all('product');
} else {
this.toggleProperty('hasPreLoaded');
return this.store.find('product');
}
}
});
Example
http://emberjs.jsbin.com/OxIDiVU/482/edit
You don't define the primary key on the adapter, it goes on the serializer
Pd.ProductSerializer = DS.RESTSerializer.extend({
primaryKey: 'nid'
});
The cache no longer lives there, it lives in this.store.typeMapFor(Pd.Product) or this.store.typeMaps.
The site is still referencing an older version of ember data until ember data 1.0 is released, I'll assume you're using 1.0 beta version. This document is more up to date https://github.com/emberjs/data/blob/master/TRANSITION.md

Backbone targetModel = undefined

I am running into this issue with backbone where the model seems to be undefined to backbone, though all scripts are loaded.
(I am using require to load backbone and other javascript files).
So whenever I do a collection.fetch I get this error in firebug:
TypeError: targetModel is undefined
When I run the script it holds at this point:
if (attrs instanceof Model) {
id = model = attrs;
} else {
id = attrs[targetModel.prototype.idAttribute];
}
when I hover with my mouse over targetModel it says: undefined
It somehow doesn't seem to work now and the only thing I did was changing my html template, which only get loaded after the collection.fetch.
Can you please help me out here?
Here is my model:
var OF = OF || {};
OF.UsersMdl = Backbone.Model.extend({
default: {
username: '',
mailinglist: '',
email: ''
},
initialize: function() {
//
},
result: {
success: false,
message: ''
},
validate: function(att) {
}
});
Here is the collection:
var OF = OF || {};
OF.UsersCollection = Backbone.Collection.extend({
initialize: function() {
//
},
parse: function(data){
return data["all-users"];
},
model: OF.UsersMdl,
url: 'php/api/users'
});
And last but not least the router with the require part:
goToUsers: function() {
require(['./models/users', './views/users_view', './collections/user_collection'], function(UsersMdl, UsersView, UsersCollection) {
OF.usersMdl = new OF.UsersMdl;
OF.usersCollection = new OF.UsersCollection;
OF.usersView = new OF.UsersView;
//when the collection is fetched
$.when(OF.usersCollection.fetch({
data: {
"admin": OF.login.attributes.admin,
"session": OF.login.attributes.session
},
success: function(){
//console.log(OF.usersCollection.length);
}
//then render the view
})).then(function(){
OF.usersView.render();
}, 300);
});
},
Here is the JSON which will be retreived by the fetch:
{
"all-users":
[
{
"username":"tester",
"mailinglist":"1",
"email":"tester#tester.test"
},
{
"username":"tester2",
"mailinglist":"1",
"email":"tester2#tester.test"
},
{
"username":"tester3",
"mailinglist":"0",
"email":"tester3#tester.test"
}
]
}
Thanks in advance
I had this same error and banged my head against it for quite a while because backbone is new to me and this was compounding a fetch issue. Anyhow, I eventually figured out that order matters. Doh! (Less obvious when using CoffeeScript and "class" statements I thinks.) With one of my models I was setting the Collection before the Model (thanks to bad example code from the Backbone.js on Rails book). I reversed that and this error went away to reveal my true fetch issue.
Similarly, your model: property may be invalid for this reason or another reason, leaving it undefined when attempting to reference later.
Side note: I had a similar error in Backbone 1.0.0. When I upgraded to Backbone 1.1.0 I then got this exact error at the same point in backbone code.

What is the best way to add server variables (PHP) in to the Backbone.model using require.js?

I'm not sure what is the elegant way to pass server variables in to my Model.
For example, i have an id of user that has to be implemented on my Model. But seems like Backbone with require are not able to do that.
My two options are:
Get a json file with Ajax.
Add the variable on my index.php as a global.
Someone know if exists a other way. Native on the clases?
Trying to make work the example of backbonetutorials. I am not able to throw a callback when the method fetch().
$(document).ready(function() {
var Timer = Backbone.Model.extend({
urlRoot : 'timeserver/',
defaults: {
name: '',
email: ''
}
});
var timer = new Timer({id:1});
timer.fetch({
success: function(data) {
alert('success')
},
fail: function(model, response) {
alert('fail');
},
sync: function(data) {
alert('sync')
}
});
});
The ajax request it has been threw. But does not work at all. Because any alert its dispatched.
var UserModel = Backbone.Model.extend({
urlRoot: '/user',
defaults: {
name: '',
email: ''
}
});
// Here we have set the `id` of the model
var user = new Usermodel({id: 1});
// The fetch below will perform GET /user/1
// The server should return the id, name and email from the database
user.fetch({
success: function (user) {
console.log(user);
}
})
The server will reply with a json object then you can leave the rendering part for your backbone. Based on a template for the user.
You may also want to check these out: http://backbonetutorials.com/

Ember.js commiting parent model with already existing child models

Cheers! For example I have three models:
App.Foo = DS.Model.extend({
bars: DS.hasMany('App.Bar', {embedded:'always'}),
bazes: DS.hasMany('App.Baz', {embedded:'always'})
});
App.Bar = DS.Model.extend({
foo: DS.belongsTo('App.Foo')
});
App.Baz = DS.Model.extend({
foo: DS.belongsTo('App.Foo)
});
And adapter mappings like these:
App.RESTSerializer = DS.RESTSerializer.extend({
init: function() {
this._super();
this.map('App.Foo', {
bars:{embedded: 'always'},
bazes:{embedded: 'always'}
})
}
});
I'm saving child records first in separated transactions (github.com/emberjs/data/pull/440):
barTransaction = App.store.transaction();
bar = barTransaction.createRecord(App.Bar);
//later
bazTransaction = App.store.transaction();
baz = bazTransaction.createRecord(App.Baz);
//later
fooTransaction = App.store.transaction();
foo = fooTransaction.createRecord(App.Foo);
//later
foo.get('bars').addObject(bar);
foo.get('bazes').addObject(baz);
fooTransaction.commit();
I just want to know if it possible to save parent and all child records with one POST request?
For now it's creating one POST request for every child record separately.
I believe your issue is with an outdated way of mapping embedded records. This used to happen in the serializer, but is now happening on a map() on the adapter...
You can see the integration test here: https://github.com/emberjs/data/blob/master/packages/ember-data/tests/integration/embedded/embedded_saving_test.js#L50
I think something along these lines will work:
App.Foo = DS.Model.extend({
bars: DS.hasMany('App.Bar'),
bazes: DS.hasMany('App.Baz')
});
App.Bar = DS.Model.extend({
foo: DS.belongsTo('App.Foo')
});
App.Baz = DS.Model.extend({
foo: DS.belongsTo('App.Foo')
});
App.Adapter = DS.RESTAdapter.extend({..configs..});
App.Adapter.map('App.Foo', {
bars:{embedded: 'always'},
bazes:{embedded: 'always'}
})
This should POST to the api at: api.com/foo and include the bars and bazes

Categories

Resources