Ember Data 1.13.5 RESTAdapter queryRecord - ember.js

Hi according to the ember ds 1.13 release docs:
If your app is using the vanilla JSONSerializer or RESTSerializer,
you will not have to make any changes, and your app will continue to
work. The existing serializers have been updated in a backwards
compatible way to return JSON API data to the store.
Currently I am the default RESTAdapter:
export default DS.RESTAdapter.extend({
host: 'http://localhost:9990',
namespace: 'api/v1'
});
Which has a custom serailzer for the model:
export default DS.RESTSerializer.extend(DS.EmbeddedRecordsMixin, {
attrs: {
comments: { embedded: 'always' }
}
});
When I attempt to use the new queryRecord method:
this.store.queryRecord('discussion',{ titleid: self.get('title_id')});
I get the following exception in the logs:
Error while processing route: title.index Assertion Failed: You tried to make a query but your adapter does not implement `queryRecord` Error: Assertion Failed: You tried to make a query but your adapter does not implement `queryRecord`
at new Error (native)
at Error.EmberError (http://localhost:4200/assets/vendor.js:25705:21)
at Object._emberMetalCore.default.assert (http://localhost:4200/assets/vendor.js:15895:13)
at ember$data$lib$system$store$$Service.extend.queryRecord (http://localhost:4200/assets/vendor.js:80502:15)
at loadDiscussionModel (http://localhost:4200/assets/ui.js:2728:32)
at renderTemplate (http://localhost:4200/assets/ui.js:2715:12)
at _emberRuntimeSystemObject.default.extend.setup (http://localhost:4200/assets/vendor.js:37282:14)
at Object.callHook (http://localhost:4200/assets/vendor.js:65078:38)
at handlerEnteredOrUpdated (http://localhost:4200/assets/vendor.js:63868:12)
at setupContexts (http://localhost:4200/assets/vendor.js:63836:9)
serializer/application.js
import DS from 'ember-data';
export default DS.RESTSerializer.extend({
serialize: function(record) {
return this._super(record, {includeId: true});
},
isNewSerializerAPI: true
});

According to source code default adapter does not have an implementation for queryRecord method: https://github.com/emberjs/data/blob/e89732a5aefb6a81b46927da1c6f048f4aede85e/packages/ember-data/lib/system/adapter.js#L226
Nor it's defined in RESTAdapter, neither in new JSONAPIAdapter. To my mind, this is due to query requests are too specific for every project thus are hard to generalize.
Nevertheless documentation contains explanation and example of implementation: http://emberjs.com/api/data/classes/DS.Adapter.html#method_queryRecord
By the way, there are two errors:
id shold be passed as 4th argument;
type.typeKey should be replaced with typeClass.modelName.
We prefer using simpler implementation in our own project:
export default DS.RESTAdapter.extend({
...
queryRecord: function(store, type, query, id) {
return this.ajax(this.buildURL(type.modelName, id, null, 'query', query), 'GET');
}
});
You can replace id argument with null in buildUrl method if needed.
Update
I forgot to mention, that in ember-data 1.13.5 RESTAdapter's default urlForQuery implementation returns url without actual query parameters passed.
So here's out implementation based on default _buildUrl method, with id replaced by query:
urlForQuery: function(query, modelName) {
var url = [];
var host = this.get('host');
var prefix = this.urlPrefix();
var path;
if (modelName) {
path = this.pathForType(modelName);
if (path) {
url.push(path);
}
}
if (prefix) {
url.unshift(prefix);
}
url = url.join('/');
if (!host && url && url.charAt(0) !== '/') {
url = '/' + url;
}
if (query) {
var queryParams = [];
for(var paramName in query) {
if(query.hasOwnProperty(paramName)) {
queryParams.push(paramName + '=' + encodeURIComponent(query[paramName]))
}
}
if(queryParams.length > 0) {
url = url + '?' + queryParams.join('&');
}
}
return url;
}
This method is in the same adapter as queryRecord from the original answer.

Adding isNewSerializerAPI: true to the all the relevant model Serializers worked to a certain degree (it removed the Error stated below). However the original error still occurrs.
Before due to an incorrect import the following Error in the console logs was not being displayed.
Error: Assertion Failed: is using the
old serializer API and expects
it collaborates with to do the same. Make sure to set
isNewSerializerAPI: true in your custom serializers if you want to
use the new Serializer API.
Also FYI according to the documentation this flag will not be required in Ember Data 2.0:
http://emberjs.com/blog/2015/06/18/ember-data-1-13-released.html
If you have customized your serializer, you should upgrade to Ember
Data 1.13, check the upgrade guide to see if you need to make any
changes, and then set a temporary flag on your Serializer:
isNewSerializerAPI. This will opt you into the new serializer API.
Once you are on the Ember Data 2.0 train, new Serializer API is the
default one, and there is no need for a flag.

Related

record was saved to the server, but the response does not have an id and your record does not either

I’m facing this error since yesterday, Error: Assertion Failed: 'todo' was saved to the server, but the response does not have an id and your record does not either.
I know it should come from app/serializers/todo.js or my app/routes/application.jsbut after looking into severals forum, I have to ask the question to expert emberJs dev, as i’m a newbie :smiley:
Here’s my app/serializers/todo.js:
import DS from 'ember-data';
export default DS.JSONSerializer.extend({
serialize: function(record, options) {
var json = this._super.apply(this, arguments); // Get default serialization
json.id = record.id; // tack on the id
return json;
}
});
And my app/routes/application.js
import Route from '#ember/routing/route';
export default Route.extend({
model(){
return this.store.findAll('todo');
},
actions: {
createtodo: function() {
var titleInput = this.get("newTitle")
var todo = this.store.createRecord('todo', {
title: titleInput,
isCompleted: false
});
this.set('newTitle', '');
todo.save();
}
}
});
The way the createtodo action is triggered app/templates/application.hbs:
{{input type="text" id="new-todo" value=newTitle}}
<button {{action "createtodo"}}>Save</button>
So my objec is created but not save. When i’m looking into my ember Inspector, I see that each object I create have an ID but the title field is null or "".
This is a todoApp with an Rails-API as back and Ember as front.
Anyone see what’s wrong here ?
Your serializer is the problem. If you're using a somewhat standard api setup, you shouldn't really need the serializer at all. I'd delete that first.
What's likely happening is that your API isn't setup to recieve JSON-API documents, which are structured like:
{
id: <id>,
type: <type>,
attributes: {
name: <string>,
created: <date>,
complete: <boolean>
}
}
So you should either install a json-api gem on the backend or switch the adapter to use the DS.RestSerializer, which has a totally flat data structure.

How to override the primary key with a JSON API attribute

I've got a model called "Membership" that has a string attribute "inviteToken" which I would like to use as my primary key.
I've created the following serializer, but cannot get it to pick up the primary key from the JSON.
app/serializers/membership.js:
import DS from 'ember-data';
export default DS.JSONAPISerializer.extend({
primaryKey: 'invite-token' // also tried 'inviteToken'
});
The specific error I'm getting is:
Error while processing route: invitations.show Assertion Failed: You
must include an 'id' for membership in an object passed to 'push'
Error: Assertion Failed: You must include an 'id' for membership in an
object passed to 'push'
Which happens when I try to get a record by its ID in the route:
import Ember from 'ember';
export default Ember.Route.extend({
model(params) {
return this.store.find('membership', params.token);
}
});
API Response:
{
"jsonapi":{
"version":"1.0"
},
"data":{
"type":"membership",
"id":"30",
"attributes":{
"invite-token":"5bGo7IhZh93E4SB07VWauw"
}
}
}
The strange thing is that if I use "type" as the primary key, I see "membership" as the id in the ember inspector. It's as if ember data doesn't know how to use something from the "attributes". I'm using ember data 2.4.0.
Update
I can hack this to work in my serializer by doing this:
import DS from 'ember-data';
export default DS.JSONAPISerializer.extend({
normalize: function(type, hash) {
const json = this._super(type, hash);
json.data.id = json.data.attributes.inviteToken;
return json;
}
});
The serializer expects the value of primaryKey to refer to a top level element in the json. This is why "type" and "id" works. It currently does not support nested properties (for example primaryKey: "attributes.invite-token")
However there are two good workarounds:
The first is overriding the extractId method. The default implementation is quite simple. In your case you could do something like:
extractId(modelClass, resourceHash) {
var id = resourceHash['attributes']['invite-key';
return coerceId(id);
},
The second way is the method you discovered, a more brute force approach, and that is to assign the id manually in the normalize function.

Response Not getting save in model in ember

I have to send the requests to "http:example.com/api/galleries/gallery_id/photos.The gallery_id is varying and I am getting it from params.id. Now the problem I am facing is that the response from a server is an array of objects.So what should I use?By That, I meant (findAll or findRecord).When I use FindRecord it make the correct call But is not able to serialize the data.When I use findAll it calls the wrong API.
My Serializer
import DS from 'ember-data';
export default DS.JSONSerializer.extend(DS.EmbeddedRecordsMixin,{
isNewSerializerAPI: true,
primaryKey:'pk',
normalize: function(typeClass, hash) {
console.log(hash)
var fields = Ember.get(typeClass, 'fields');
fields.forEach(function(field) {
var payloadField = Ember.String.underscore(field);
if (field === payloadField) { return; }
hash[field] = hash[payloadField];
delete hash[payloadField];
});
return this._super.apply(this, arguments);
}
});
This is the error I am getting is
Assertion Failed: You must include an 'id' for gallery in an object passed to 'push'

Emberjs - how to remove pluralization of URLs?

I am building an emberjs app and I want to call my REST API for results. I have this code:
App.Post = DS.Model.extend();
App.PostAdapter = DS.RESTAdapter.extend({
namespace: 'api/v1',
host: 'http://myapp.com'
});
And in the controller I have this
post: this.store.find('post')
The problem is that it always calls with "s" added in the end, for example - http://myapp.com/api/v1/posts
How do I remove the plural form from these calls?
You need to override the pathForType method in your adapter.
App.PostAdapter = DS.RESTAdapter.extend({
pathForType: function(type) {
var camelized = Ember.String.camelize(type);
return Ember.String.singularize(camelized);
}
});
var inflector = new Ember.Inflector();
inflector.singularize('posts');
Ember put 's' automatically. You need to force it to use singular. Above code tells Ember to request to myapp.com/post when you call this.store.find('post'); otherwise default behaviour will try to send request to myapp.com/posts
I had same issue once upon a time. I could not even found a way to set this behaviour globally. I have repeated this code ( inflector.singularize('posts'); ) for every store.

Get current route name in Ember

I need to get the current route name in my ember application; I tried this:
Ember App.Router.router.currentState undefined
but it doesn't work for me (there is probablig something i'm missimg...) I use Ember rc6 and I have a multilingual app; in the applicationRoute I detect the browser's language and I redirect to the correct page with:
this.transitionTo(userLang);
but I would like this to be executed only when user are on the home page, so something like this:
if (currentRoute == 'home'){
this.transitionTo(userLang)
}
NOTE: as of Ember 3.16, the original answer is not only recommended, but observers are strongly discouraged.
To get the current route name, you can utilize the Router Service: https://api.emberjs.com/ember/3.18/classes/RouterService/properties/currentRouteName?anchor=currentRouteName
export default class MyComponent extends Component {
#service router;
get activeRoute() {
return this.router.currentRouteName;
}
}
Original answer below
You could observe the application's currentPath and set it to the current route accordingly when it changes:
App = Ember.Application.create({
currentPath: '',
});
App.ApplicationController = Ember.Controller.extend({
updateCurrentPath: function() {
App.set('currentPath', this.get('currentPath'));
}.observes('currentPath')
}),
This way you have access to the currentPath when ever you want with App.get('currentPath');
E.g.
if (App.get('currentPath') == 'home'){
this.transitionTo(userLang);
}
Hope it helps.
This worked for me on 1.3.0-beta (and a quick glance at the source for 1.1.2 suggests it would work there too):
App.__container__.lookup('router:main').location.lastSetURL
Note that the documentation states:
At present, it relies on a hashchange event existing in the browser.
However, I believe it's strongly suggested that App.__container__ not be used in production code. A more acceptable alternative would be to use App.Router.router.currentHandlerInfos, which provides information on the current Ember route.
Yet another option is currentRouteName on the ApplicationController. You can add needs: ['application'] to your controller, then access the route name with controllers.application.currentRouteName. This will return something like posts.index.
With the shift to components, it is harder to get route name. The best way is to add an initializer such as
ember g initializer router
(from command line), and
export function initialize(application) {
application.inject('route', 'router', 'router:main');
application.inject('component', 'router', 'router:main');
}
export default {
name: 'router',
initialize
};
in a initializers/router.js. You can also inject into controller if you need to. Then just do simply
this.get('router.currentRouteName');
in JS, or
{{router.currentRouteName}}
in template.
This is the only way I have found to get it reliably, and observable in Ember 2.4
If you want to get current route in your component or controller you can inject routing service (routing: Ember.inject.service('-routing'))
(for more) and use:
this.get('routing.currentRouteName') or this.get('routing.currentPath')
Example with component and computed property:
import Ember from 'ember';
export default Ember.Component.extend({
routing: Ember.inject.service('-routing'),
checkMyRouteName: Ember.computed('routing.currentRouteName', function() {
return this.get('routing.currentRouteName');
})
})
Example with controller and computed property:
import Ember from 'ember';
export default Ember.Controller.extend({
routing: Ember.inject.service('-routing'),
checkMyRouteName: Ember.computed('routing.currentRouteName', function() {
return this.get('routing.currentRouteName');
})
})
Current route in your route you just need this.routeName
Example with route:
import Ember from 'ember';
export default Ember.Route.extend({
checkMyRouteName() {
return this.routeName;
}
})
Just as an update, in Ember 1.8.1, we can get the routeName inside an Ember.Route object by doing this.routeName.
Currently as of Ember 1.7.0 you can get the current route from within a route by calling this.routeName.
The Ember namespace API now has a getOwner method, which is very useful for looking up the currentRouteName, or, other route properties.
const owner = Ember.getOwner(this);
const currentRoute = owner.lookup('router:main').currentRouteName;
const routeInfo = owner.lookup(`route:${currentRoute}`).get('info');
// etc.
I've created an Ember Twiddle example to demonstrate. Use the text input above the "Output" pane to hit other routes like /blue, /green, or /red.
Ember has a RouterService since 2.15. It provides the name of the current route as currentRouteName property. A polyfill exists for Ember 2.4 - 2.14 if you are still on such an old version.
import Component from '#ember/component';
export default Component.extend({
router: service(),
isHomeRoute: computed('router.currentRouteName', function() {
return this.router.currentRouteName === 'home';
}),
});
All other solutions mentioned here are relying on private API that might already be deprecated / removed. Using RouterService is working at least up the current version, which is 3.12 at the time of writing this.
Please note that the "home" is not /. The root URL is called "index".
I had the same problem for a while. then i started exploring router. It always have a state object which can be obtained from any route using
var route = this;
var handlerInfos = route.get("router.router.state.handlerInfos");
var currRouteHandlerInfo = handlerInfos[handlerInfos.length-1];
var currRouteName = currRouteHandlerInfo.name; //"home"
that's it. Now you have the current route name!
if you want the current route params,
var routerParams = this.get("router.router.state.params");
var currRouteParams = routerParams[currRouteName]; //{ homeId : "1" }
You can simple parse the current URL. This way you can use your full url for example:
http://127.0.0.1:8000/index.html/#/home
and extract from this string the suffix:
/home
which is the current route name.
A simple JS function (that works regardless to your Ember version) will be:
function getCurrentRoute()
{
var currentRoute;
var currentUrl = window.location.href; // 'http://127.0.0.1:8000/index.html/#/home'
var indexOfHash = currentUrl.indexOf('#');
if ((indexOfHash == -1) ||
(indexOfHash == currentUrl.length - 1))
{
currentRoute = '/';
}
else
{
currentRoute = currentUrl.slice(indexOfHash + 1); // '/home'
}
return currentRoute;
}
Example of use:
if (getCurrentRoute() == '/home')
{
// ...
}