Using the default instructions in the README.md file for a freshly generated Ember-CLI 0.0.20 app, I'm getting a 404 error. I'd love to be able to use the API Stub for offline development.
FWIW, I realize this is a duplicate of How do I setup the api-stub in an ember-cli app?, but the answer on that one isn't working for me, and hasn't been marked as accepted by the asker.
Here's what api-stub/README.md says to do:
API Stub
========
The stub allows you to implement express routes to fake API calls.
Simply add API routes in the routes.js file. The benefit of an API
stub is that you can use the REST adapter from the get go. It's a
way to use fixtures without having to use the fixture adapter.
As development progresses, the API stub becomes a functioning spec
for the real backend. Once you have a separate development API
server running, then switch from the stub to the proxy pass through.
To configure which API method to use edit **package.json**.
* Set the **APIMethod** to 'stub' to use these express stub routes.
* Set the method to 'proxy' and define the **proxyURL** to pass all API requests to the proxy URL.
Default Example
----------------
1. Create the following models:
app/models/post.js
```
var attr = DS.attr,
hasMany = DS.hasMany,
belongsTo = DS.belongsTo;
var Post = DS.Model.extend({
title: attr(),
comments: hasMany('comment'),
user: attr(),
});
export default Post;
```
app/models/comment.js
```
var attr = DS.attr,
hasMany = DS.hasMany,
belongsTo = DS.belongsTo;
var Comment = DS.Model.extend({
body: attr()
});
export default Comment;
```
2. Setup the REST adapter for the application:
app/adapters/application.js
```
var ApplicationAdapter = DS.RESTAdapter.extend({
namespace: 'api'
});
export default ApplicationAdapter;
```
3. Tell the Index router to query for a post:
app/routes/index.js
```
var IndexRoute = Ember.Route.extend({
model: function() {
return this.store.find('post', 1);
}
});
export default IndexRoute;
```
4. Expose the model properties in the index.hbs template
app/templates/index.hbs
```
<h2>{{title}}</h2>
<p>{{body}}</p>
<section class="comments">
<ul>
{{#each comment in comments}}
<li>
<div>{{comment.body}}</div>
</li>
{{/each}}
</ul>
</section>
```
When Ember Data queries the store for the post, it will make an API call to
http://localhost:8000/api/posts/1, to which the express server will respond with
some mock data:
```
{
"post": {
"id": 1,
"title": "Rails is omakase",
"comments": ["1", "2"],
"user" : "dhh"
},
"comments": [{
"id": "1",
"body": "Rails is unagi"
}, {
"id": "2",
"body": "Omakase O_o"
}]
}
```
api-stub/routes.js:
/* global module */
module.exports = function(server) {
// Create an API namespace, so that the root does not
// have to be repeated for each end point.
server.namespace('/api', function() {
// Return fixture data for '/api/posts/:id'
server.get('/posts/:id', function(req, res) {
var post = {
"post": {
"id": 1,
"title": "Rails is omakase",
"comments": ["1", "2"],
"user" : "dhh"
},
"comments": [{
"id": "1",
"body": "Rails is unagi"
}, {
"id": "2",
"body": "Omakase O_o"
}]
};
res.send(post);
});
});
};
app/adapters/application.js:
var ApplicationAdapter = DS.RESTAdapter.extend({
namespace: 'api'
});
export default ApplicationAdapter;
app/models/post.js:
var attr = DS.attr,
hasMany = DS.hasMany,
belongsTo = DS.belongsTo;
var Post = DS.Model.extend({
title: attr(),
comments: hasMany('comment'),
user: attr(),
});
export default Post;
app/models/comment.js:
var attr = DS.attr,
hasMany = DS.hasMany,
belongsTo = DS.belongsTo;
var Comment = DS.Model.extend({
body: attr()
});
export default Comment;
app/routes/index.js:
var IndexRoute = Ember.Route.extend({
model: function() {
return this.store.find('post', 1);
}
});
export default IndexRoute;
app/router.js:
var Router = Ember.Router.extend(); // ensure we don't share routes between all Router instances
Router.map(function() {
this.route('index', {path: '/'});
this.route('component-test');
this.route('helper-test');
// this.resource('posts', function() {
// this.route('new');
// });
});
export default Router;
app/templates/index.hbs:
<h2>{{title}}</h2>
<p>{{body}}</p>
<section class="comments">
<ul>
{{#each comment in comments}}
<li>
<div>{{comment.body}}</div>
</li>
{{/each}}
</ul>
</section>
package.json:
...
"name": "cli2test",
"APIMethod": "stub",
"version": "0.0.0",
"private": true,
"directories": {
"doc": "doc",
"test": "test"
...
The short-term answer on this, from Stefan Penner via Twitter:
#caretpi API stub does not work in cli. We hope to have it integrated soon though— Stefan Penner (#stefanpenner) April 1, 2014
There's an issue open on GitHub: https://github.com/stefanpenner/ember-cli/issues/153
Related
everyone. I've got a strange behaviour after update Ember from 2.9.1 to 2.10. After this update, when I navigate List links with link-to helper I've got a full page refresh instead of particle refresh. In Ember 2.9.1 it was a particle refresh. Maybe I've missed something in these updates.
Here is my code for List
app/router.js
import Ember from 'ember';
import config from './config/environment';
const Router = Ember.Router.extend({
location: 'auto',
rootURL: '/app/'
});
Router.map(function() {
this.route('home', { path: '/' });
this.route('list', { path: '/list/:listId' }, function() {
this.route('lead', { path: 'lead/:leadId', resetNamespace: true });
});
this.route('pageNotFound', { path: '/page-not-found' });
});
controllers/list.js
import Ember from 'ember';
export default Ember.Controller.extend({
queryParams: ['page', 'sortBy', 'direction', 'archived'],
page: 1,
perPage: 50,
sortBy: 'createdAt',
direction: 'asc',
archived: false
});
routes/list.js
import Ember from 'ember';
import AuthenticatedRouteMixin from 'ember-simple-auth/mixins/authenticated-route-mixin';
import RouteMixin from 'ember-cli-pagination/remote/route-mixin';
export default Ember.Route.extend(AuthenticatedRouteMixin, RouteMixin, {
queryParams: {
page: {refreshModel: true},
perPage: {refreshModel: true},
sortBy: {refreshModel: true},
direction: {refreshModel: true},
archived: {refreshModel: true}
},
intercom: Ember.inject.service('intercom'),
leadLink: 'lead',
model(params)
{
var intercom = this.get('intercom');
var store = this.get('store');
var list;
params.leadLink = this.leadLink;
if (params.listId) {
list = this.store.findRecord('list', params.listId).then(function (list) {
intercom.update({
"Emails": list.get('user.discovered_emails'),
"Limit": list.get('user.max_discovered_emails')
});
return list;
});
}
else {
params.title = 'All leads';
}
return Ember.RSVP.hash({
list: list,
leads: this.store.query('lead', {
filter: this.getFilter(params)
}, { reload: true }).then(function (leads) {
params.total = leads.get('meta.total');
leads.forEach(function (lead) {
var list = store.peekRecord('list', lead.get('listId'));
lead.set('list', list);
return lead;
});
return leads;
}),
lists: this.store.peekAll('list'),
params: params
});
},
// ....
templates/components/sidebar-list-item.hbs
{{#link-to "list" list.id (query-params page=1 archived=false)}}<i class="icon-list-unordered"></i> {{list.name}} <span class="text-muted text-thin">{{#if activeLeadsCount}}({{activeLeadsCount}}){{/if}}</span>{{/link-to}}
Thanks for any help.
I would like to point out issues I found in posted code,
1.Inside list route model hook, you are trying to load list model from store by using peekAll and peekRecord and I haven't found the code to load list model to store.
Without doing findAll('list') if you do peekAll or peekRecord you will get nothing, since peekAll or peekRecord both will return whatever already loaded in store it will not fetch it from server.
So you need to load list model records into store. For this you can do it in beforeModel hook, or you can do it any one of the parent route
beforeModel(transition){
this._super(...arguments);
return this.store.findAll('list');
}
2.For link-to helper if we provide dynamic segment value(in your case list.id) then it will always call beforeModel , model and afterModel hook.
3.Also set {refreshModel: false} if you don't want to opt into full transition
Sorry, guys. I found that it was not full refresh. I have application-loading.hbs with loading circle. And it appears only on app boot, but after fix https://github.com/emberjs/ember.js/pull/14545 it appears any time when I refresh list model.
Anyway thanks for help!
I searched everywhere but can't seem to find an answer for this simple problem here on SO.
Problem:
I have a hasMany relationship in a model that is loaded by the route in a findAll(). Payload looks fin according to many answers I've seen here, but I get "TypeError: Cannot read property 'replace' of undefined". More details below.
How can I get this hasMany relationship to work? I'm using asynch:false and sending the sideload as recommended.
Using: Ember 2.1.0, Ember-Data 2.1.0.
Stacktrace:
TypeError: Cannot read property 'replace' of undefined
at Object.func (ember.debug.js:36026)
at Object.Cache.get (ember.debug.js:13165)
at decamelize (ember.debug.js:36068)
at Object.func (ember.debug.js:35974)
at Object.Cache.get (ember.debug.js:13165)
at Object.dasherize (ember.debug.js:36072)
at ember$data$lib$system$normalize$model$name$$normalizeModelName (normalize-model-name.js:13)
at ember$data$lib$serializers$json$serializer$$default.extend.modelNameFromPayloadKey (json-api-serializer.js:267)
at ember$data$lib$serializers$json$serializer$$default.extend._extractType (json-api-serializer.js:258)
at ember$data$lib$serializers$json$serializer$$default.extend.normalize (json-api-serializer.js:290)
Route:
app/routes/search.js
export default Ember.Route.extend({
model(params) {
if(params.query){
return this.store.findAll('search-result');
}
return null;
},
actions:{
sendSearch: function(queryString){
this.store.unloadAll('search-result');
this.refresh();
}
}
});
Models:
app/models/search-result.js
import DS from 'ember-data';
export default DS.Model.extend({
title: DS.attr('string'),
description: DS.attr('string'),
url: DS.attr('string'),
tags: DS.hasMany('search-result-tag', {async:false})
});
app/models/search-result-tag.js
import DS from 'ember-data';
export default DS.Model.extend({
name: DS.attr('string'),
});
Adapter (for search-result)
import DS from 'ember-data';
export default DS.RESTAdapter.extend({
host: 'http://localhost:8080',
urlForFindRecord(id, modelName, snapshot) {
let url = this._super(...arguments);
let query = Ember.get(snapshot, 'adapterOptions.query');
if (query) {
url += '?' + Ember.$.param(query); // assumes no query params are present already
}
return url;
},
urlForFindAll(modelName) {
var queryDict = {};
location.search.substr(1).split("&").forEach(function(item) {queryDict[item.split("=")[0]] = item.split("=")[1]})
let url = this._super(...arguments);
let query = queryDict['query'];
if (query) {
url += '?query=' + query; // assumes no query params are present already
}
return url;
}
});
Payload
{
"search-result-tags": [
{
"name": "this-is-tag-#-0",
"id": 0
}
],
"search-results": [
{
"description": "This is description for blabla2",
"id": 0,
"title": "Blabla 2",
"url": "http://blablabla2.com",
"tags": []
},
{
"description": "This is description for blabla",
"id": 1,
"title": "Blabla",
"url": "http://blabla.com",
"tags": [
0
]
}
]
}
You need to use the RESTSerializer in addition to the RESTAdapter. So app/serializers/application.js would be -
import DS from 'ember-data';
export default DS.RESTSerializer.extend({
});
See the docs. You may need to override keyForAttribute, if you need to change cases / underscores of your keys.
Note that if you are using Rails API for the backend you want ActiveModelAdapter and ActiveModelSerializer, which are available as an addon.
I'm trying to get a nested route to make a request for additional data, update the record, and then render the view. See below:
// models
var attr = DS.attr,
hasMany = DS.hasMany,
belongsTo = DS.belongsTo
App.List = DS.Model.extend({
title: attr('string'),
links: hasMany('link')
})
App.Link = DS.Model.extend({
list: belongsTo('list'),
subtitle: attr('string'),
})
// JSON for index route
{
"list": [
{
"id": 532,
"title": "first list"
},
{
"id": 991,
"title": "second list"
},
{
"id": 382,
"title": "third list"
}
]
}
// JSON for list route - /user/532
{
"list":
{
"id": 532,
"title": "list numero uno",
"links" : [1, 2]
},
"link": [
{
"id": 1,
"subtitle": "this is a subtitle of the firsto listo!"
},
{
"id": 2,
"subtitle": "this is a subtitle of a second part in the list"
}
]
}
// routing
this.resource('user', function(){
this.route('list', {path: ':id'})
})
App.UserRoute = Ember.Route.extend({
model: function(){
return this.store.find('list')
}
})
App.UserListRoute = Ember.Route.extend({
model: function(params) {
return this.store.find('list', params.id)
}
})
I want the index route to display a basic list with just the {{title}}, and clicking on one links it to UserListRoute which then makes another ajax call to update the record with the additional link data. I've tried adding:
afterModel: function(modal){
model.reload()
}
to the UserListRoute but it initially uses the index route model, and then it reloads with the new data. I'm trying to avoid this, and make it send an ajax request immediately and then render the content - so I don't want it to depend on the parent model/data.
create a different record type, and extend from the original, or just use a completely different model.
App.BasicList = DS.Model.extend({
foo: DS.attr()
});
App.FullList = App.BasicList.extend({
//foo
bar: DS.attr()
});
App.UserListRoute = Ember.Route.extend({
model: function(params) {
return this.store.find('fullList', params.id)
}
})
I tried with kingpin2k's answer, and it worked on a page refresh but navigating from the user index to the nested still caused it to use the parent model. I figured out why it wasn't used the correct model. My links on the user index template were:
{{#link-to 'user.list' this}} {{title}} {{/link-to}}
So clicking on link correctly redirects me to /users/:id, but it passes on the data of the item selected. To remedy this I did:
{{#link-to `user.list` id}} {{title}} {{/link-to}}
So by changing this to id, when it transitions to the route, it uses the appropriate model specified in the route.
I'm getting this error after I save a post (title, text) to a mongodb database via a REST API written with Express and refresh the browser. I've already set my primary key to '_id' and have been reading about possibly normalizing the data?
Here is the payload from the server (only 1 post in db):
{
"posts": [
{
"title": "The Title",
"text": "Lorem ipsum",
"_id": "52c22892381e452d1d000010",
"__v": 0
}
]
}
The controller:
App.PostsController = Ember.ArrayController.extend({
actions: {
createPost: function() {
// Dummy content for now
var to_post = this.store.createRecord('post', {
title: 'The Title',
text: 'Lorem ipsum'
});
to_post.save();
}
}
});
The model:
App.Post = DS.Model.extend({
title: DS.attr('string'),
text: DS.attr('string')
});
Serializer:
App.MySerializer = DS.RESTSerializer.extend({
primaryKey: function(type){
return '_id';
}
});
Adapter:
App.ApplicationAdapter = DS.RESTAdapter.extend({
namespace: 'api'
});
Any help is much appreciated! Please let me know if you need any other info.
Thanks
When using custom adapters/serializers the naming is important. If you want it to apply to the entire application it should be called ApplicationSerializer
App.ApplicationSerializer = DS.RESTSerializer.extend({
primaryKey: '_id'
});
Adapter:
App.ApplicationAdapter = DS.RESTAdapter.extend({
namespace: 'api'
});
If you just want it to apply to a single model (this applies to adapter's as well)
App.PostSerializer = DS.RESTSerializer.extend({
primaryKey: '_id'
});
I had this same error but after some debugging discovered it was caused by my rest API not returning the saved objects json.
I'm trying to figure out why my routes have stopped working. I recently upgraded to Ember JS RC 3 (from RC2) and after going through the changelog, I can't figure out why my routes stopped working. Maybe I changed something in trying to fix it; I don't know. But here's what I've got.
Here are my routes.
App.Router.map(function() {
this.route("site", { path: "/" });
this.route("about");
this.resource('posts', { path: '/posts' }, function() {
this.route('new');
this.route('show', { path: '/:post_id' });
this.route('edit', { path: '/:post_id/edit'})
});
this.route("map");
});
Here is my route handler...
App.PostsIndexRoute = Em.Route.extend({
model: function(){
return App.Post.find();
},
setupController: function(controller, model){
controller.set('content', model);
}
});
Here is my model
App.Post = DS.Model.extend({
body: DS.attr('string'),
headline: DS.attr('string'),
creator: DS.attr('string'),
created: DS.attr('date')
});
Here's the JSON being pulled over the network (I see it coming, just not being rendered -- the view is not rendered at all)
[
{
"creatorID": "512808071f7152f9b6000001",
"creator": "Aaron",
"headline": "second entry",
"body": "this is my second entry. ",
"updated": "2013-03-04T20:10:14.566Z",
"created": "2013-03-04T20:10:14.566Z",
"id": "5134ffa6095e930000000001"
},
{
"body": "this is my first entry. ",
"created": "2013-02-28T22:39:32.001Z",
"creator": "Aaron",
"creatorID": "512808071f7152f9b6000001",
"headline": "first entry",
"updated": "2013-03-01T18:13:35.934Z",
"id": "512fdca479d3420000000001"
}
]
Probably also important to note that I do NOT see any JS errors here, as I'm visiting localhost/posts
Thanks for your help!
EDIT: Here is the template I'm using to render the entries (jade based).
div {{content}}
{{#each post in content}}
div(class="post limit-width")
h3(class="headline") {{#linkTo 'posts.show' post}} {{ post.headline }} {{/linkTo}}
p {{{post.body}}}
h6
em Posted by {{post.creator}} on {{post.created}}
{{/each}}
note that the {{content}} tag is being used to determine if there's even an object present; there is, but its length seems to be zero. Which makes me think that no entries are being loaded via Ember Data, but I cannot figure out why.