I've set up an ember.js client with ember-data as persistance handler.
Fetching all records from the DB works fine with this code:
router.js
App.ProjectsRoute = Ember.Route.extend({
model: function() {
return this.store.find('project');
}
});
But adding a new record doesn't work correctly with this code. It's only added to the model and not persisted.
index.html (example)
{{input type="text" id="newTitle" value=Title}}
<button class="small" {{action 'createProject'}}><i class="icon-plus-sign"></i></button>
Controller.js
App.ProjectsController = Ember.ArrayController.extend({
actions: {
createProject: function () {
var project = this.store.createRecord('project', {
project_number: this.get('newProject_number'),
title: this.get('newTitle'),
client: this.get('newClient'),
comment: this.get('newComment'),
xmlfile: this.get('newXmlfile')
});
this.set('newProject_number', '');
this.set('newTitle', '');
this.set('newClient', '');
this.set('newComment', '');
project.save();
}
}
});
models.js
App.Project = DS.Model.extend({
title: DS.attr('string'),
client: DS.attr('string'),
comment: DS.attr('string'),
project_number: DS.attr('string'),
});
app.js
window.App = Ember.Application.create();
App.store = DS.Store.extend({
adapter: DS.RESTAdapter,
});
DS.RESTAdapter.reopen({
namespace: 'api/index.php',
headers: {
"API_KEY": "secret key",
"ANOTHER_HEADER": "Some header value"
}
});
Framework versions
Ember : 1.2.0;
Ember Data : 1.0.0-beta.5+canary.e120006;
Handlebars : 1.1.2;
jQuery : 2.0.3;
What have I missed to configure? There's no error in the console however.
REST-Api works well with curl.
The problem is the structure of the JSON "POST"ed towards the API:
Sent:
{project: {client: "test",comment: "test",project_number: "test",title: "test"}}
Expected by API-Backend:
{client: "test",comment: "test",project_number: "test",title: "test"}
I wonder where I could have found the information how ember-data build it's requests. (For further development)
Related
I am a beginner in Ember and trying to implement a simple post and comment app.
I have a rails background and hence i'm using Rails API for this.
I have followed a tutorial and i'm able to save a post, fetch all its comments and delete the post. However i'm having issues in saving comment related to the post.
Following is the code for models
post.js
import DS from 'ember-data';
export default DS.Model.extend({
title: DS.attr('string'),
body: DS.attr('string'),
comments: DS.hasMany('comment')
});
comment.js
import DS from 'ember-data';
export default DS.Model.extend({
author: DS.attr('string'),
body: DS.attr('string'),
post: DS.belongsTo('post')
});
routes/post/comment/new.js
import Ember from 'ember';
export default Ember.Route.extend({
model() {
return {};
},
renderTemplate() {
this.render('post.comment.new', { into: 'application' });
},
actions: {
save() {
const post = this.modelFor('post');
const newComment = this.get('store').createRecord('comment', this.currentModel);
newComment.set('post', post);
newComment.save().then(() => {
this.transitionTo('post', post);
});
},
cancel() {
this.transitionTo('post', this.modelFor('post'));
}
}
});
router.js
import Ember from 'ember';
import config from './config/environment';
const Router = Ember.Router.extend({
location: config.locationType,
rootURL: config.rootURL
});
Router.map(function() {
this.route('posts');
this.route('post.new', { path: 'posts/new' });
this.resource('post', { path: 'posts/:post_id' }, function() {
this.route('comment.new', { path: 'comments/new' });
});
});
export default Router;
Saving the comment is where i'm facing an issue. This is really strange but while saving the comment, the params passed to the server looks like
Parameters: {"comment"=>{"author"=>"dsa", "body"=>"asd", "post"=>"9"}}
Unpermitted parameter: post
From what i understand, the parameter should be post_id and not post. If post is being passed, then it should be object. I may be wrong of course because i don't have a clear understanding in Ember yet.
On randomly fiddling with the code, i found that if i replace the relationship in comments model from
post: DS.belongsTo('post')
to
post_id: DS.belongsTo('post')
the params passed to server are
Parameters: {"comment"=>{"author"=>"fg", "body"=>"dfs", "post_id"=>nil}}
This however doesn't actually pass the post_id as its nil.
This might be absolutely wrong and not how its supposed to work but i'm clueless.
Thanks for any help.
Create comment serializer and override keyForRelationship method like below :
keyForRelationship(key/*, relationship, method*/) {
if(key === 'post') return 'post_id';
return this._super(...arguments);
}
and the post relation should be :
post: DS.belongsTo('post')
Version: Ember 2.11
I am trying to use Ember data model to load the data from REST API but it is failing with error message as
"ember.debug.js:17634 TypeError: Cannot read property 'type' of undefined
at Class._pushInternalModel (store.js:2005)
Here is more details:
1. REST API response is:
{
"sfresults":[
{
"url":"https://google.com/1",
"title":"Titl1",
"description":"Description1",
"type":"KB",
"lastModifiedDate":"12/23/16",
"viewScore":"86.12006476690622",
"caseNumber":"case1",
"id":"cd4ac3e8-c3ac-4be5-ad11-c62a85ddf289"
},
{
"url":"https://google.com/2",
"title":"Titl2",
"description":"Description2",
"type":"KB",
"lastModifiedDate":"12/23/16",
"viewScore":"86.12006476690622",
"caseNumber":"case2",
"id":"cd4ac3e8-c3ac-4be5-ad11-c62a85ddf289"
},
],
"message":"SUCCESS",
"id":"3bd116c7-db63-4277-8ace-a7ea846a04ee"
}
Controller code:
let sfdata = this.store.query('sfresult',{ 'searchText': inputSearchText, 'searchType' : 'SF' } );
this.set('sfresult', sfdata);
My Models
sfresult.js
import DS from 'ember-data';
export default DS.Model.extend({
sfresults: DS.hasMany('sfresults'),
message: DS.attr('string')
});
sfresults.js
import DS from 'ember-data';
export default DS.Model.extend({
title: DS.attr('string'),
description: DS.attr('string'),
caseNumber: DS.attr('string'),
lastModifiedDate: DS.attr('string'),
type: DS.attr('string'),
url: DS.attr('string'),
searchText: DS.attr('string'),
messageId: DS.attr('string')
});
export default DS.RESTAdapter.extend({
host: 'http://localhost:8080',
namespace: 'server'
});
Then i wanted to iterate sfresult and show it in the UI. but here as soon as API call response comes back it is unable to load into ember data models. fails with above error.
Note: if i try without hasMany model it works fine - meaning json response having array of elements and using only sfresults model without sfresult, but wanted to get the message in the JSON response to act accordingly - please ignore this statement if it is confusing a bit.
Any help would be greatly appreciated.
You'll need to make sure you are using the RESTSerializer. By default the serializer in a new app is JSONAPISerializer, which throws the error that you are seeing.
I have a back-end server(running on node.js) and I am using Ember (v 2.0.2) and Ember-data (2.0.0) with JQuery (2.1.4). The node.js has body-parser and express installed. Front-end libraries are Ember, Ember-data and JQuery
When I fetch data using .findAll() method in the app.ContactsRoute function in the App.js file, I get the following error in the Chrome developer console
Error while processing route: contacts Assertion Failed: You must include an 'id' for undefined in an object passed to 'push' Error: Assertion Failed: You must include an 'id' for undefined in an object passed to 'push'
at new Error (native)
There is further to this error with local host website links showing error in the lines of Ember-template-compiler and Ember-data. Just couldn't post it here in the question due to limitation of posting Hyperlinks in the question
Server.js file:
var express = require('express'),
bodyParser = require('body-parser'),
app=express();
var id = 7;
var data = {
1: {id:1, firstName: 'Danny', lastName: 'Stork', email: 'dannystork#example.com'},
2: {id:2, firstName: 'Carlotta', lastName: 'McOwen', email: 'carlottamcowen#example.com'},
3: {id:3, firstName: 'Luther', lastName: 'Ellery', email: 'lutherellery#example.com'},
4: {id:4, firstName: 'Finch', lastName: 'Hosky', email: 'finchhosky#example.com'},
5: {id:5, firstName: 'Carson', lastName: 'Andrews', email: 'carsonandrews#example.com'},
6: {id:6, firstName: 'Mac', lastName: 'Parker', email: 'macparker#example.com'},
7: {id:7, firstName: 'J.D.', lastName: 'Barney', email: 'jdbarney#example.com'},
};
app.use(bodyParser.json());
app.use(express.static('./public'));
app.route('/api/contacts')
.get(function(req,res){
res.json(Object.keys(data).map(function(key){
return data[key];
}));
})
.post(function(req,res){
var record = req.body
record.id = ++id;
data[record.id] = record;
res.json(record);
});
app.route('/api/contacts/:id')
.get(function(req,res){
res.json(data[req.params.id]);
})
.put(function(req,res){
data[req.params.id]=req.body;
res.json(req.body);
})
.delete(function(req,res){
delete data[req.params.id];
res.json(null);
});
app.get('*', function(req,res){
res.sendFile(__dirname + '/public/index.html');
});
app.listen(3000);
app.js file:
var app = Ember.Application.create();
app.ApplicationAdapter = DS.RESTAdapter.extend({
namespace:'api'
});
app.Contact = DS.Model.extend({
firstName: DS.attr('string'),
lastName: DS.attr('string'),
email: DS.attr('string')
});
app.ContactSerializer = DS.RESTSerializer.extend({
extractArray: function(store, primaryType, payload){
payload ={contacts: payload} ;
return this._super(store,primaryType,payload);
},
extractSingle: function(store, primaryType, payload, recordId){
payload ={contact: payload} ;
return this._super(store,primaryType,payload,recordId);
},
serializeIntoHash: function(hash,type, snapshot,option){
var json = this.serialize(snapshot, {included:true});
Object.keys(json).forEach(function(key){
hash[key] = json[key];
});
}
});
//to get pushstate routes instead of hashbang routes - #!
//access the app.Router class and set the location
app.Router.reopen({
location: 'history'
});
//routes
app.Router.map( function(){
//this.resource used when there is a noun or object behind e.g. /contacts or /contact/:id
//this.route used when there is no noun or object behind it e.g. /contact/new (no existing data is present)
this.route('contacts');
this.route('contact',{path: 'contacts/:contact_id'});
this.route('new',{path: 'contacts/new'});
});
//this is used for the default route when it doesn't match any other routes in the above list of routes.
app.IndexRoute = Ember.Route.extend({
redirect: function(){
this.transitionTo('contacts');
}
});
app.ContactsRoute = Ember.Route.extend({
model: function() {
return this.store.findAll('contact');
}
})
Index.html:
<html>
<head>
<title> Ember Contacts </title>
<link rel='stylesheet' href='/style.css' />
<base href="/">
</head>
<body>
<!-- This is like the app class (here id = application is used) defined as top level template -->
<!-- This is common to all our pages -->
<script type='text/x-handlebars' id='application'>
<div id='main'>
<header>
<h1> {{#link-to 'contacts'}} Ember Contacts {{/link-to}}</h1>
</header>
<div id='app'>
{{outlet}}
</div>
</div>
</script>
<script type='text/x-handlebars' id='contacts'>
<div class='actions'>
{{#link-to 'new'}} NEW Contact {{/link-to}}
</div>
<ul>
{{#each contact in model}}
<li>
{{#link-to 'contact' contact}}
{{contact.firstName}} {{contact.lastName}} <span>{{contact.email}}</span>
{{/link-to}}
</li>
{{/each}}
</ul>
</script>
<script src='/lib/jquery/dist/jquery.min.js'></script>
<script src='/lib/ember/ember-template-compiler.js'></script>
<script src='/lib/ember/ember.debug.js'></script>
<script src='/lib/ember-data/ember-data.js'></script>
<script src='/app.js'></script>
</body>
</html>
Can someone please help with this issue? Why is this error coming in ember-data?
I am new to ember.js, was completing a tutorial. I could not use the same version that he was using in the video. Hence used the latest version of Ember and Ember-data.
I have made it work for the latest version. Have changed the following from your code:
In App.js:
Inside app.ContactSerializer = DS.RESTSerializer.extend({ function:
extractArray function has been changed to new function normalizeFindAllResponse (note when changing this function the parameters have changed as well). The function now looks like:
normalizeFindAllResponse: function(store, primaryModelClass, payload, id, requestType){
payload = {contacts: payload};
return this._super(store, primaryModelClass, payload, id, requestType);
},
extractSingle function has been changed to new function normalizeFindRecordResponse (note when changing this function the parameters have changed as well). The function now looks like:
normalizeFindRecordResponse: function(store, primaryModelClass, payload, id, requestType){
payload ={contact: payload} ;
return this._super(store, primaryModelClass, payload, id, requestType);
},
In Index.html, inside the <ul> tag, change "{{#each contact in model}}" to "{{#each model as |contact|}}". And Remove the Handlebars reference <script src='/lib/handlebars/handlebars.js'> </script>. The latest version of Ember doesn't need explicit reference to Handlebars.
Hope this helps, its good to be working with the latest version as the changes are eminent to move forward to newer versions.
I'm pretty new to Ember.js and am building an app to pick up some Ember chops. I wanted to use a computed property in one of my models as a route but it seems something isn't working correctly. I'm using FIXTURES by the way.
What I'm trying to achieve is /peeps/john-smith instead of /peeps/1
I've got my model setup like this:
App.Peep = DS.Model.extend({
firstName: DS.attr('string'),
lastName: DS.attr('string'),
slug: function(){
this.get('firstName').toLowerCase() + '-' + this.get('lastName').toLowerCase();
}.property('firstName', 'lastName')
});
My router setup is like this:
App.Router.map(function(){
this.resource('peep', { path: '/peeps/:peep_slug'});
});
App.PeepRoute = Ember.Route.extend({
model: function(params){
return this.store.find('peep', params.peep_slug);
},
serialize: function(model){
return { peep_slug: model.get('slug') };
}
});
When I navigate to /peeps/john-smith in the browser, I get this warning in my console You made a request for a peep with id john-smith.
Is there something I'm missing?
By default it searches by id param, so you could either change the adapter to make it search by slug or try to add id as Ember.computed.alias('slug').
I am new to EmberJs and I don't clearly understand in Ember's Adapter .I just try the ember adapter in my App.Js like that and I got this error ( Assertion failed: You tried to set adapter property to an instance of DS.Adapter, where it should be a name or a factory ) . My ember's code in App.js is :
//Store
App.Adapter = DS.RESTAdapter.extend();
App.Store = DS.Store.extend({
revision: 12,
adapter: App.Adapter.create()
});
//Models
App.Product = DS.Model.extend({
name: DS.attr('string'),
description: DS.attr('string'),
price: DS.attr('number')
});
// Products Route
App.ProductsRoute = Ember.Route.extend({
model: (function() {
return this.store.find('Product');
})
});
return App;
I think you misunderstand the way you setup and configure adapters.
//
// Application-wide adapter, everything will use this unless you override it
//
App.ApplicationAdapter = DS.RESTAdapter.extend({
host: 'https://api.example.com'
});
//
// Product model, will use ApplicationAdapter
//
App.Product = DS.Model.extend({
name : DS.attr('string'),
description : DS.attr('string'),
price : DS.attr('number')
});
//
// Invoice model, will use fixtures, so specify a different adapter
//
App.InvoiceAdapter = DS.FixtureAdapter.extend({ /* options */ });
App.Invoice = DS.Model.extend({
name : DS.attr('string'),
amount : DS.attr('number')
});
//
// Routes, these should work as expected
//
App.ProductRoute = Ember.Route.extend({
model: function(params) {
return this.store.find('product', params.id);
}
});
App.ProductsRoute = Ember.Route.extend({
model: function() {
return this.store.find('product');
}
});
App.InvoicesRoute = Ember.Route.extend({
model: function() {
return this.store.find('invoice');
}
});
return App;
Ember will know which models/routes/etc to use based on their names - see http://emberjs.com/guides/concepts/naming-conventions/ for the details.
Define the store in this way
App.Store = DS.Store.extend({
revision: 12,
adapter: App.Adapter
});
Without the create().
The main use of the adapter is to do the serialization and deserailzation of the data according to some conventions like constructing the url to post or get data from and then constructing the actual objects from the response. The Default adapter used by ember data models is the Rest adapter.
see
http://emberjs.com/guides/models/the-rest-adapter/
for more details
To use a different adapter other than the rest adapter you can specify its name like
Storm.Store = DS.Store.extend({
adapter: '_ams',
});
Try:
App.ApplicationAdapter = DS.RESTAdapter.extend();
This works for me