ember/ember-cli - impossible url fetched while routing - ember.js

I am new to Ember, but I got a successful "app" with it, then i'm trying to "port" it to ember-cli.
I have a quite empty main page, and a link to the about page: main and about routes are defined.
However I got a 404 "/mains" not found when accessing /… why the hell is he adding an extra "s"?
I've uploaded the project:
https://github.com/Leryan/testember/
https://raw.githubusercontent.com/Leryan/testember/master/2015-03-21-202815_1920x1080_scrot.png
You'll see a picture with the problem: when accessing the website root, ember try to fetch "/mains" …
Thanks

Ember is looking for the all the records of the type 'main' by calling this url.
This is because in the router "main.js" you are using this.store.find method, which pluralizes the model type to retrieve all the records for this model ("/mains"):
var MainRoute = Ember.Route.extend({
model: function() {
return this.store.find('main');
}
});
But it looks like you want to use fixtures instead?
Therefore you have to use the FixtureAdapter for the desired route and define the fixtures for the model. To use the FixtureAdapter you must rename your existing adapter "all.js" to "application.js" or "main.js" depending where you want to use it.
And furthermore you have to use reopenClass to assign any fixtures in your model "main.js":
Main.reopenClass({
FIXTURES : [
{ id: 1, name: "blah" },
{ id: 2, name: "blah2" }
]
});
Here is the ember gudie for the fixture adapter:
http://emberjs.com/guides/models/the-fixture-adapter/

Related

Dynamic segments other than id in Ember.js

I'm trying to wrap my head around dynamic segments and I want to be able to use a slug or other property instead of the id. When I can get things working it feels like a fluke. (I'm using ember 2.7+)
I've been looking at the following sources/threads for ideas:
https://guides.emberjs.com/v1.10.0/cookbook/ember_data/linking_to_slugs (1.10)
http://discuss.emberjs.com/t/slugs-for-replacing-id-path-for-dynamic-segments
I plan on using ember-data, but I want to ensure I'm in control - and so I don't want to use the :post_slug / underscore style that has some built in magic that I want to avoid.
Here is an ember-twiddle
Here are step-by-step commits in a github repo
My thought process
1. Conceptually, lets say I need a list of cats - so I need to describe the model for what a 'cat' is.
models/cat.js
import Model from "ember-data/model";
import attr from "ember-data/attr";
export default Model.extend({
name: attr('string'),
slug: attr('string')
});
2. define where the dynamic segment will be in the url. I'm going to use catId to prove a point instead of cat_id or :id like most of the tutorials I've seen. For this example, I'm also writing an actual app structure instead of the smallest router possible - to test the edges of this. + what if I needed something like this later: animals.com/cats/:catId/best-friend/:dogId
router.js
Router.map(function() {
this.route('index', { path: '/' });
this.route('cats', { path: '/cats' }, function() {
this.route('index', { path: '/' }); // list of cats
this.route('cat', { path: '/:catId' }); // cat spotlight
});
});
3. pull in the catData into the store ~ in the /cats route
routes/cats.js
import Ember from 'ember';
const catData = [
{
id: 1,
name: 'Dolly',
slug: 'dolly'
},
{
id: 2,
name: 'kitty cat',
slug: 'kitty-cat'
},
{
id: 3,
name: 'Cleopatra',
slug: 'cleo'
}
];
export default Ember.Route.extend({
model() {
return catData;
// return this.get('store').findAll('cat'); // with mirage or live api
}
});
Update from comments:
I do not believe that you can use queryRecord with your test data.
Ember data plays dumb with query and queryRecord; it doesn't assume
anything about your data and just passes the call on to your server.
~ #xcskier56
So this kinda blows my twiddle as is. The git repo example is Mirage.
4. create the templates... + set up the 'cat' route. The records are in the store... right? so I should be able to 'peek' at them based on id. The docs use params - but -
Ember will extract the value of the dynamic segment from the URL for
you - and pass them as a hash to the model hook as the first argument
: ~ and so the params object name isn't special and could really be anything you wanted... and is just replaced with a hash - so to that point / I'm using 'passedInThing' just to assert control over the confusing conventions (many tutorials use param instead of params)
routes/cats/cat.js
model( passedInThing ) {
return this.store.peekRecord('cat', passedInThing.catId );
} // not going to happen - but in theory...
model( passedInThing ) {
return this.store.queryRecord('cat', { slug: passedInThing.catId } );
}
5. At this point, I should be able to navigate to the url /cats/2 - and the 2 should get passed through the model hook - to the query. "Go get a 'cat' with an id of 2" --- right??? ((the twiddle example uses a hard-coded set of catData - but in my other attempts I'm using mirage with a combination of fixtures and dynamic slugs: https://github.com/sheriffderek/dynamic-segments-tests/commits/queryRecord
6. Typing in the segment works - but for link-to helpers I need to pass in the explicit cat.id
{{#link-to 'cats.cat' cat.id}}
<span>{{cat.name}}</span>
{{/link-to}}
7. I can get all that working - but I don't want an ID in the URL. I want cats/cleo with the 'slug' ~ in theory I can just switch catId for catSlug and cat.id to cat.slug etc - but that is not the case. I've seen many tutorials outlining this but they are outdated. I've tried passing in { slug: params.slug } and every combo of find query and peek etc. Sometimes the url will work but the model wont render - or the opposite.
8. This seems like 101 stuff. Can anyone explain this to me? Oh wise emberinos - come to my aid!
UPDATES
A nice video showing how to use serialize() in this case
There is supposedly an example coming to the Ember tutorial, but I haven't seen it land yet.
I've struggled with this same issue, and AFIK there is not a way to handle this without querying the server. While this is old and the code is a little out dated the logic still stands. http://discuss.emberjs.com/t/use-slug-for-page-routes-id-for-store-find/6443/12
What I have done is to use store.queryRecord() and then my server is able to return a record fetched via a slug.
This is what the route would look like:
model: function (params) {
return this.store.queryRecord('cat', {slug: params.catSlug})
}
This will enable you to not expose the ID in the url, but it will issue a query to the store every single time that the model gets hit. There seems to be some discussion of caching query and queryRecord in ember-data, but nothing working yet.
https://github.com/emberjs/data/issues/1966
Other helpful resources:
How to cache query result in ember data
Cache a record when using Query Params in the call? Ember-data

EmberJS embedded items in payload JSONAPI

Ember : 1.13.3
Ember Data : 1.13.5
jQuery : 1.11.3
I am trying to send a JSON payload using ember-data from my EmberJS client to my server. I want to send the entire object graph to the server on saving the project, as I don't want to send multiple requests. I wouldn't mind sending multiple requests, but I am worried about what happens if one of the requests fails in the middle and the data on the server will not be correct.
I wanted to use JSONAPI (http://jsonapi.org/format/#document-compound-documents) as that is becoming the default adapter in Ember. Also, there is a few C# libraries that handle this format, so I thought it would be quite straightforward. However, after reading the spec, it seems that I cannot embed objects if they do not have an id. EmberJS also does not attach the child objects to the JSON either even though I have specified { async: false, embedded: 'always' }) on the DS.attr.
My question is: If an application is used in such a way that an object graph is created on the client side, how do you use JSONAPI format to send the entire object graph to the server? Do I have to generate ids on the client side to satisfy the JSONAPI standard? Then once they get to the server just ignore them so they get saved with an id generated by the ORM?
Here is my labelGroup model:
import DS from 'ember-data';
export default DS.Model.extend({
name: DS.attr('string'),
labels: DS.hasMany('label-model', { async: false, embedded: 'always' })
});
Here is my project model:
import DS from 'ember-data';
export default DS.Model.extend(DS.EmbeddedRecordsMixin, {
name: DS.attr('string'),
labelGroups: DS.hasMany('labelGroup', { async: false, embedded: 'always'})
});
Here is the POST that I get after doing a save() on the project:
{
"data":{
"attributes":{"name":"Project"},
"relationships":{
"label-groups":{
"data":[
{"type":"label-groups","id":null},
{"type":"label-groups","id":null},
{"type":"label-groups","id":null},
{"type":"label-groups","id":null},
{"type":"label-groups","id":null},
{"type":"label-groups","id":null},
{"type":"label-groups","id":null}
]
}
},
"type":"label-projects"
}
}
UPDATE: I tried using https://www.npmjs.com/package/ember-cli-uuid to generate client side ids which it has. However the data getting output does not include the extra objects, only a reference to their ids. I expected to see an "included" property as specified here:http://jsonapi.org/format/#document-compound-documents, but it is not there.
{
"data":{
"id":"7b4544ee-91cd-493d-8b10-52040e68c283",
"attributes":{"name":"Project"},
"relationships":{
"label-groups":{
"data":[
{"type":"label-groups","id":"08115273-e82a-4d46-93ea-232ce071fb78"},
{"type":"label-groups","id":"9ca94fe9-8077-411e-98d2-1694c6fecce4"},
{"type":"label-groups","id":"d629f1e8-7962-404d-8034-38229ab21f77"},
{"type":"label-groups","id":"c6bda655-5489-4760-847b-bf02239bb2c5"},
{"type":"label-groups","id":"f6fef249-2d1d-43f0-ba64-24b7ff8b5637"},
{"type":"label-groups","id":"a7db25bf-52c8-477b-83e4-64e7c76b072e"},
{"type":"label-groups","id":"f3b5fbb3-261a-4b3d-b481-b9352f8ce2d6"}
]
}
},
"type":"label-projects"
}
}
Ember-data has no support for what you want at the moment. So ember-data will not save your relationships data in a save payload.
But its possible to do this your own by using a custom adapter and serializer.
I strongly recommend you to checkout the API and then look into the source.
If you call .save() on your Model the createRecord method is called on your adapter.
Here serializeIntoHash on the serializer is called to serialize the Model.
serializeIntoHash calls serialize, where serializeBelongsTo and serializeHasMany is called.
Now you can just override serializeHasMany and modify the hasMany before the line:
json[payloadKey] = hasMany;
Here you have the type and the ids as they are sent by ember-data. You could just .forEach the data on the hasMany and then fetch the store for the data and build your included array.
I hope this helps you to understand the serializer and the adapter so you can modify it to do whatever you want pretty easy. Actually this is the best part about ember-data. The structure of the adapter and the serializer, which allows easy modifications.

Accessing store in another route

I have this model:
App.Game = DS.Model.extend({
name: attr(),
uri: attr()
});
and this route:
App.GamesRoute = Ember.Route.extend({
model: function() {
return this.store.find('game');
}
});
This works fine, calls the backend server, and stores elements in the store (I've checked with Ember inspector). This is the json I return:
{"games":[{"id":"TicTacToe","name":"TicTacToe","uri":"http://localhost:10000/games/TicTacToe"}]}
Now I have this template for 'games' (snipped):
{{#each game in model}}
{{#link-to 'games.matchlist' game.id}}{{game.uri}}{{/link-to}}
This shows the URI for each game. Now in the games.matchlist route what I would like to do is to search in the store by the game_id received param and get the game URI. The reason is that the server doesn't follow RESTAdapter conventions, and I would like to make a custom AJAX query to that URI myself.
This doesn't work:
App.GamesMatchlistRoute = Ember.Route.extend({model: function(params) {
var store = this.store;
var game = store.find('game', params.game_id)
console.log(game);
console.log("URI: " + game.uri);
at this point, game is an object but it's not an instance of my model. It doesn't have a uri attribute. What am I doing wrong? I'm feeling that I'm missing something obvious.
If you want to get records without hitting the server and you know you already have it in the store, use this.store.getById('game', ID).
I'm on my mobile, but you need to create a GameAdapter and customize I believe the fetch function. Checkout the docs for adapters on the ember site and you should have your answer.
Your other option is to fetch the data from your server and use this.store.pushPayload(data).
Docs here: http://emberjs.com/api/data/classes/DS.Store.html#method_pushPayload
And the adapter docs here: http://emberjs.com/guides/models/customizing-adapters/

Ember-Data parent template not updating when model is changed

I am using EAK with a simple nested routing structure, but changing the parent model in the child controller does not change the top-level view. For example if I have the following router.js file:
this.resource('similar', function() {
this.resource('list', { path: '/list/:phone_id' });
this.resource('upload');
this.resource('new');
});
For the 'similar' route model I am using ember-data together with the RESTAdapter which is backed Flask.
export default Ember.Route.extend({
model: function() {
return this.store.find('phone');
}
});
If I manipulate the model inside the 'upload' controller then the changes are not reflected in the template, e.g.
var record = this.store.createRecord('phone', {
numbers: [1,2,3,4]
});
record.save();
will not change "{{#each list in model}} {{list.numbers}} {{/each}}".
If I reload the page it works fine. What am I doing wrong?
Instead of store.find which hits the server and stores that exact list, try using store.filter. store.filter "remains up to date as new records are loaded into the store or created locally"
http://emberjs.com/api/data/classes/DS.Store.html#method_filter
The problem here was that the REST endpoint did not return the new record (with the id attribute set).

How to access Ember Model from the Route in Ember CLI

Perhaps doing this with regular Ember application having all the code sitting in app.js would have been much easier for me. But since I'm using Ember CLI I'm having trouble accessing my model in my Route. I'm still trying to learn how to use Ember CLI so please help me out.
As I just want to fire AJAX calls and get the data to render on my UI, I downloaded and added Ember Model library to my project. I don't see a need of using Ember Data. This is the Ember Model documentation I'm referring: https://github.com/ebryn/ember-model#example-usage. With that said, here's my directory structure that Ember CLI proposed:
|-app
|-controllers
| |-customers.js
|-models
| |-customers.js
|-routes
| |-customers.js
|-templates
| |-customers.hbs
|-app.js
|-index.html
|-main.js
|-router.js
This is much simpler representation of the actual project structure that I have just to focus on the problem. As proposed in Ember Model documentation I added following code to my Customers model (model\customers.js):
export default Ember.Model.extend({
nameid: attr(),
firstname: attr(),
middlename: attr(),
lastname: attr(),
prefixname: attr(),
suffixname: attr()
});
this.url = "http://restapi/api/customers";
this.adapter = Ember.RESTAdapter.create();
Notice that I had to do the "export default" instead of "App.Customers = Ember.Model.extend...". This is the Ember CLI convention. So when I try to access the model I created in my Customers Route I get error "Error while loading route: ReferenceError: App is not defined"..
Customers Route code:
export default Ember.Route.extend({
model: function () {
App.Customers.find();
},
actions: {
addnew: function(){
//logic of saving edited customer
alert('customer created!');
}
}
});
I tried this.model() - Returns an object of type supperWrapper and this.modelFor() - Returns null.
Please suggest how to get an handle of my model in its route so that I can perform CRUD operations provided out-of-the-box by Ember Model.
Thanks!
I suggest you change the file name of the model to singular e.g. customer.js.
If you want to access the model class within the route file you have to import the model. Since Ember CLI uses ES6 module syntax you can't / shouldn't access anything directly on the App object. This should be done via import statements or Ember internally via the resolver.
import Customer from "../models/customer";
Now you can use it in the model hook. There is also another error in your example code, you have to return the promise from the find call.
model: function () {
return Customer.find();
},
I'm curious why you picked Ember Model over Ember Data, because for this example you would need less code with Ember Data and it would be more like the Ember way AFAIK.