Cannot read property 'match' of undefined at Ember.DefaultResolver.extend.podBasedComponentsInSubdir - ember.js

I'm getting a really opaque error message (opaque in the sense I have no point of reference for my own source) from console, I'm not entirely sure where to look, I feel it's likely an error in library code but before posting this on github I'll just double check it's not my own fault.
The Problem
The Problem is simple, I'm calling this.store.find('player'), in hopes to get a list of all players, and then display them in some kind of list, but I'm not even getting past the loading part. The data is pulled from the server and looks properly formatted, but something seems to be failing after the route.model method call. And the error message seems to be somewhere in the ember.js library code with nothing pointing back to my own code.
Server Response
The content type is of course application/json, and note the id property is actually _id.
[
{
"_id":"55405a5102b4ed623c225e87",
"alias":"mikeTest",
"__v":0,
"scans":[],
"createdAt":"2015-04-29T04:13:05.223Z"
}
]
Error message
Note there is part of the stack trace pointing to my source, only Ember source. Which has made this a pain to debug.
Error while processing route: leader Cannot read property 'match' of undefined TypeError: Cannot read property 'match' of undefined
at Ember.DefaultResolver.extend.podBasedComponentsInSubdir (http://localhost:4200/assets/vendor.js:60138:76)
at http://localhost:4200/assets/vendor.js:60190:34
at Array.exports.default.mixin.Mixin.create.find (http://localhost:4200/assets/vendor.js:39572:30)
at Ember.DefaultResolver.extend.findModuleName (http://localhost:4200/assets/vendor.js:60188:44)
at resolveOther (http://localhost:4200/assets/vendor.js:60051:37)
at superWrapper (http://localhost:4200/assets/vendor.js:28141:20)
at exports.default.EmberObject.default.extend.resolve (http://localhost:4200/assets/vendor.js:15454:35)
at Object.resolve [as resolver] (http://localhost:4200/assets/vendor.js:15217:23)
at resolve (http://localhost:4200/assets/vendor.js:12792:29)
at Object.Registry.resolve (http://localhost:4200/assets/vendor.js:12336:21)
Source
This ember app is very young, so there is very little source at the moment, but this is all the relevant source at the moment.
Routes
import Ember from 'ember';
import config from './config/environment';
var Router = Ember.Router.extend({
location: config.locationType
});
export default Router.map(function() {
this.resource('leader');
this.resource('profile');
this.route('loading');
});
Leader route
Leader has a template and a controller, but they are basically empty right now.
import Ember from 'ember';
export default Ember.Route.extend({
model: function () {
return Ember.RSVP.hash({
players: this.get('store').find('player')
});
},
});
Player Model
import DS from 'ember-data';
export default DS.Model.extend({
alias: DS.attr('string'),
createdAt: DS.attr('date'),
scans: DS.hasMany('scan'),
});
Application Adapter
import DS from 'ember-data';
export default DS.RESTAdapter.extend({
namespace: ''
});
Application Serialiser
import DS from 'ember-data';
export default DS.RESTSerializer.extend({
primaryKey: function (type) {
return '_id';
},
serializeId: function(id) {
return id.toString();
}
});
Versions
I'm not sure if any of the versions here are particularly important
ember-cli is 0.2.3
ember-data is 1.0.0-beta.16.1
ember is 1.11.1
Things I've tried
removing properties from the model, in the event the relationships seemed to be the problem (nothing changed)
tried setting up a serialiser and adapter for the application (included above), nothing changed.
the serialiser in the event that the id field in the response is actually _id.
tried updating ember data, nothing changed.

Okay I figured out what was being done wrong... I forgot to check if the data being returned by the server abides to the convention/protocol required to use ember data. The JSON returned by the server looks like this.
[
{
"_id":"55405a5102b4ed623c225e87",
"alias":"mikeTest",
"__v":0,
"scans":[],
"createdAt":"2015-04-29T04:13:05.223Z"
}
]
It should actually look like this
{
"players": [
{
"_id":"55405a5102b4ed623c225e87",
"alias":"mikeTest",
"__v":0,
"scans":[],
"createdAt":"2015-04-29T04:13:05.223Z"
}
]
}
So yes this was me being dumb and missing something.
Why is this Required
Ember data expects JSON returned from the server to meet the JSON API Standard, which is a standard that specifies the formatting of the JSON returned from a server. In this case, the data didn't meet the JSON API standard, as I forgot to put the array of players under a key called players. There are some more examples of this in the Ember v1.10.0 guide to models.
The reason Ember Data expects this is so Ember Data can make the certain assumptions about the data returned from the server.

Related

Ember Data usage without a Backend API and converting Models to JSON (Ember 2.5.0)

I am using Ember v2.5.0 without an external datastore.
After creating a record at the route using the createRecord method it cannot be queried for in other parts of the app (controller or components).
My understanding is that I need to use store.push to save the record locally so that it may be accessed by the controller. However the store.push method requires the arguments to be in json format.
I could just do away with the models however I was wondering if there a quick way to convert the models into json format using Ember version 2.5.0?
I would also like to know if my assumptions on using store.push to persist the data locally is a recommended way to go when using Ember Data without an external backend.
There are other references on "Ember models to json" on stack overflow however they are outdated and I particularly would like to know if my approach/assumptions are correct and if not, what the alternatives are. Im very new to Ember.
Problem
//Route
import Ember from 'ember';
export default Ember.Route.extend({
model() {
let shape, square;
square = this.store.createRecord('square');
shape = this.store.createRecord('shape', {
shared: 'shared-value',
square: square
});
return shape;
}
});
//Controller
import Ember from 'ember';
export default Ember.Controller.extend({
actions: {
someActionName() {
console.log(this.store.peekRecord('shape', 1)); //undefined!
}
}
});
//Shape Model
import DS from 'ember-data';
export default DS.Model.extend({
shared: DS.attr('string', { defaultValue: '' }),
square: DS.belongsTo('square')
});
//Square Model
import DS from 'ember-data';
export default DS.Model.extend({
sides: DS.attr('string', { defaultValue: '4' }),
whereIbelong: DS.belongsTo('shape')
});
I don't think using store.push is a good long-term approach, it's better to use the save and destroyRecord methods, as Ember Data expects.
You can create an adapter from the base Adapter using local storage, or maybe just returning the record passed in createRecord/updateRecord works.
Experiment with it and find what works better for your use case, adapters are very flexible.
As a side note, the best way I found to make store.push work as expected is like this:
var obj = {
id: '1',
name: "object name",
// More object properties...
};
store.push(store.normalize('model-name', obj));
createRecord does not add an ID. So this.store.peekRecord('shape', 1) is undefined because your record created does not have an ID. Everything works well if you set an ID for your record.
Ember Twiddle: https://ember-twiddle.com/e2b24b5a2cdab19c7b7401c57aff9959?openFiles=controllers.application.js%2C
If your goal is to persist records on client side have a look at ember-local-storage.

Ember destroyRecord on polymorphic record calls the wrong route

I have a polymorphic record type in my ember application. Posts has a polymorphic association called response, which can be a few kinds of response.
// post.js
import DS from 'ember-data';
export default DS.Model.extend({
...
responses: DS.hasMany('response', { polymorphic: true })
});
The response model is the basis for all the other types.
// response.js
import DS from 'ember-data';
export default DS.Model.extend({
post: DS.belongsTo('post'),
message: DS.attr('string')
});
For example, a comment inherits from response.
// comment.js
import Response from '../models/response';
export default Response.extend();
This works perfectly for loading models using ember-data. It correctly connects to /responses on the Rails server for all types of responses.
However when I try to delete a record using record.destroyRecord() it sends the delete to /comments on the server, instead of /responses. Because this is a polymorphic record there is no comments route on the server.
Is there some other part of ember-data where I can indicate the record deletion should be polymorphic?
What you are looking for is the adapter part of ember. In the adapter you can specify the endpoints for your calls.
https://guides.emberjs.com/v2.3.0/models/customizing-adapters/
http://emberjs.com/api/data/classes/DS.RESTAdapter.html
make sure youre referencing the same version of your ember app.
the buildUrlMixin is great for custimizing the urls for specific actions
http://emberjs.com/api/data/classes/DS.BuildURLMixin.html
import DS from 'ember-data';
export default DS.RESTAdapter.extend({
namespace: 'api/1'
});

What array name does Ember Data expect for sub directory models?

I recently started learning Ember and using Ember-CLI so I'm not quite well educated about Ember Data and what array names it expects for relationships that are in sub directories in my app.
// models/server.js
import DS from 'ember-data';
export default DS.Model.extend({
serverHistory: DS.hasMany("history/server", { async: true })
});
// models/history/server.js
import DS from 'ember-data';
export default DS.Model.extend({
server: DS.belongsTo("server", { async: true })
});
I've tried returning these names from my API
server_historys_ids
server_histories_ids
history_server_ids
history_servers_ids
But I don't see an XHR request for Server history in my application. The servers itself are fetched fine.
Update
I changed my relationship name and the API is returning history ids but I'm still not getting an history json request even though I'm trying to each in the template. The game relationship data is accessible in the template and a request is successfully made.
// models/server.js
import DS from 'ember-data';
export default DS.Model.extend({
// attr's here.. not relevant
// Relationships
game: DS.belongsTo("game", { async: true }), // works
serverHistories: DS.hasMany("history/server", { async: true }) // doesn't make a request like game does.
});
I also have an adapter/history/server.js but it's only telling what namespace to use - "api".
Update 2
I think the problem may be in the way I'm calling the data to the model.
// routes/server/view/index.js
import Ember from 'ember';
export default Ember.Route.extend({
model: function() {
var parentModel = this.modelFor("server.view");
return this.store.query("server", { server_address: parentModel.server_address });
// return this.store.find("server", 1);
}
});
How come when I use find with an id it updates the template data and when I use query with parameters it doesn't?
Update 3
So I got my find and query problem sorted out, here's the way I got it to work: https://stackoverflow.com/a/31831667/1814027
The relationship problem still persists. I see no serverHistory data in my Ember toolbar nor a request being made to the API for it.
I beleive serverHistory is anti-conventional name for hasMany and serverHistories should be instead.
export default DS.Model.extend({
serverHistories: DS.hasMany("history/server", { async: true })
});
Then in case of ActiveModelAdapter expected server payload is:
{"server": {"id": 1, "server_history_ids": [1,2,3]}}
It doesn't depend on the fact that serverHistory is namespaced model, it depends on relation name only.
For example for model:
// models/server.js
import DS from 'ember-data';
export default DS.Model.extend({
bars: DS.hasMany("history/server", { async: true })
});
expected payload is:
{"server": {"id": 1, "bar_ids": [1,2,3]}}
Update
Working ember-cli example: https://github.com/artych/so_ember_data_subdir
Artych's answer helped me on the right path but Ember didn't want to recognise server_history_ids so I just renamed the hasMany relation to histories and returned histories: [] from my API. Now it works.. don't know why but it works.

How to properly use Fixtures with ember-cli project structure

I'm using ember-cli and trying to make some sense of the structure of the app and how it is all wired together. There are some differences in the main Ember guide docs and what I'm seeing in the ember-cli generated project. I understand the API's are moving fast so I just need to be pointed in the right direction.
In router.js I have the following:
Router.map(function() {
this.route('domains', {path: "/domains" });
});
Then I have models/domain.js
import DS from 'ember-data';
var Domain = DS.Model.extend({
name: DS.attr('string')
});
Domain.reopenClass({
FIXTURES: [
{ id: 1, name: 'User'},
{ id: 2, name: 'Address'}
]
});
export default Domain;
And I have routes/domains.js
import Ember from 'ember';
export default Ember.Route.extend({
model: function() {
return this.store.all('domain');
}
});
And finally ( I think ), I have templates/domains.hbs
<h1>Domains</h1>
{{#each}}
<p>{{name}}</p>
{{/each}}
Only the header is being rendered when I visit the http://localhost:4200/domains url. I'm using the ember chrome extension and I don't see any data coming back in the request. I'm not sure if it is a naming convention issue or what I'm doing wrong so any help is appreciated.
all just returns records that have already been found in the store. find will issue a request (in this case hitting the fixtures) and populate the store, and also return all of the records in the store.
this.store.find('domain');
The problem ended up being 2-fold. Kingpin2K was right in that I needed to use find instead of all. I also had to change the adapter to the following in adapters/application.js:
export default DS.FixtureAdapter.extend();

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.